Merge pull request #991 from EdgeTX/access-options
Access options / menus
This commit is contained in:
@@ -98,6 +98,10 @@ if(PXX2 OR MULTIMODULE)
|
||||
add_gui_src(radio_spectrum_analyser.cpp)
|
||||
endif()
|
||||
|
||||
if(PXX2)
|
||||
add_gui_src(access_settings.cpp)
|
||||
endif()
|
||||
|
||||
if(MULTIMODULE)
|
||||
add_gui_src(multi_rfprotos.cpp)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,940 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "access_settings.h"
|
||||
#include "channel_bar.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
|
||||
static void startBindWaitDialog(Window* parent, uint8_t moduleIdx,
|
||||
uint8_t receiverIdx)
|
||||
{
|
||||
auto& bindInfo = getPXX2BindInformationBuffer();
|
||||
#if defined(SIMU)
|
||||
const char* receiverName =
|
||||
bindInfo.candidateReceiversNames[bindInfo.selectedReceiverIndex];
|
||||
memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx],
|
||||
receiverName, PXX2_LEN_RX_NAME);
|
||||
storageDirty(EE_MODEL);
|
||||
bindInfo.step = BIND_OK;
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
new MessageDialog(parent, STR_BIND, STR_BIND_OK);
|
||||
#else
|
||||
bindInfo.step = BIND_START;
|
||||
new BindWaitDialog(parent, moduleIdx, receiverIdx);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void startFlexBindWaitDialog(Window* parent, uint8_t moduleIdx,
|
||||
uint8_t receiverIdx, uint8_t flex)
|
||||
{
|
||||
auto& bindInfo = getPXX2BindInformationBuffer();
|
||||
bindInfo.flexMode = flex;
|
||||
startBindWaitDialog(parent, moduleIdx, receiverIdx);
|
||||
}
|
||||
|
||||
static void startLBTBindWaitDialog(Window* parent, uint8_t moduleIdx,
|
||||
uint8_t receiverIdx, uint8_t lbt)
|
||||
{
|
||||
auto& bindInfo = getPXX2BindInformationBuffer();
|
||||
bindInfo.lbtMode = lbt;
|
||||
startBindWaitDialog(parent, moduleIdx, receiverIdx);
|
||||
}
|
||||
|
||||
BindRxChoiceMenu::BindRxChoiceMenu(Window* parent, uint8_t moduleIdx,
|
||||
uint8_t receiverIdx) :
|
||||
Menu(parent), moduleIdx(moduleIdx), receiverIdx(receiverIdx)
|
||||
{
|
||||
const auto& bindInfo = getPXX2BindInformationBuffer();
|
||||
auto receiversCount = min<uint8_t>(bindInfo.candidateReceiversCount,
|
||||
PXX2_MAX_RECEIVERS_PER_MODULE);
|
||||
|
||||
for (uint8_t i = 0; i < receiversCount; i++) {
|
||||
const char* receiverName = bindInfo.candidateReceiversNames[i];
|
||||
addLine(receiverName, [=]() {
|
||||
auto& bindInfo = getPXX2BindInformationBuffer();
|
||||
auto& modInfo = getPXX2ModuleSetupBuffer().moduleInformation;
|
||||
bindInfo.selectedReceiverIndex = i;
|
||||
if (isModuleR9MAccess(moduleIdx) &&
|
||||
modInfo.information.variant == PXX2_VARIANT_EU) {
|
||||
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
if (modSetup.moduleSettings.txPower <= 14) {
|
||||
// with telemetry
|
||||
startLBTBindWaitDialog(parent, moduleIdx, receiverIdx, 1);
|
||||
} else {
|
||||
// without telemetry
|
||||
startLBTBindWaitDialog(parent, moduleIdx, receiverIdx, 2);
|
||||
}
|
||||
return;
|
||||
} else if (isModuleR9MAccess(moduleIdx) &&
|
||||
modInfo.information.variant == PXX2_VARIANT_FLEX) {
|
||||
bindInfo.step = BIND_RX_NAME_SELECTED;
|
||||
auto flexMenu = new Menu(parent);
|
||||
flexMenu->addLine(STR_FLEX_868, [=]() {
|
||||
startFlexBindWaitDialog(parent, moduleIdx, receiverIdx, 0);
|
||||
});
|
||||
flexMenu->addLine(STR_FLEX_915, [=]() {
|
||||
startFlexBindWaitDialog(parent, moduleIdx, receiverIdx, 1);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
startBindWaitDialog(parent, moduleIdx, receiverIdx);
|
||||
});
|
||||
}
|
||||
|
||||
setCancelHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; });
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
bool BindRxChoiceMenu::onTouchEnd(coord_t x, coord_t y)
|
||||
{
|
||||
// Note: onCancel() is not called when the menu is discarded
|
||||
// by clicking outside the menu window and the onCancel
|
||||
// handler is not accessible from here
|
||||
if (!Window::onTouchEnd(x, y)) {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
onKeyPress();
|
||||
deleteLater();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
BindWaitDialog::BindWaitDialog(Window* parent, uint8_t moduleIdx,
|
||||
uint8_t receiverIdx) :
|
||||
Dialog(parent, STR_BIND, {50, 73, LCD_W - 100, LCD_H - 146}),
|
||||
moduleIdx(moduleIdx),
|
||||
receiverIdx(receiverIdx)
|
||||
{
|
||||
new StaticText(&content->form, {0, height() / 2, width(), PAGE_LINE_HEIGHT},
|
||||
STR_WAITING_FOR_RX, 0, CENTERED | COLOR_THEME_PRIMARY1);
|
||||
|
||||
setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; });
|
||||
}
|
||||
|
||||
void BindWaitDialog::checkEvents()
|
||||
{
|
||||
auto& bindInfo = getPXX2BindInformationBuffer();
|
||||
|
||||
if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) {
|
||||
|
||||
// returned to normal after bind
|
||||
if (bindInfo.step > BIND_INIT) {
|
||||
removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx);
|
||||
deleteLater();
|
||||
if (bindInfo.step == BIND_OK) {
|
||||
POPUP_INFORMATION(STR_REG_OK);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// pre-bind phase: fetching info for R9M
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
switch(bindInfo.step) {
|
||||
case BIND_MODULE_TX_INFORMATION_REQUEST:
|
||||
if (modSetup.moduleInformation.information.variant == PXX2_VARIANT_EU) {
|
||||
// In EU mode we will need the power of the module to know if telemetry
|
||||
// can be proposed
|
||||
bindInfo.step = BIND_MODULE_TX_SETTINGS_REQUEST;
|
||||
#if defined(SIMU)
|
||||
modSetup.moduleSettings.txPower = 14;
|
||||
#else
|
||||
moduleState[moduleIdx].readModuleSettings(&modSetup.moduleSettings);
|
||||
#endif
|
||||
} else {
|
||||
bindInfo.step = 0;
|
||||
moduleState[moduleIdx].startBind(&bindInfo);
|
||||
}
|
||||
break;
|
||||
case BIND_MODULE_TX_SETTINGS_REQUEST:
|
||||
// We just receive the module settings (for TX power)
|
||||
bindInfo.step = 0;
|
||||
moduleState[moduleIdx].startBind(&bindInfo);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (bindInfo.step == BIND_INIT && bindInfo.candidateReceiversCount > 0) {
|
||||
|
||||
// create RX choice dialog...
|
||||
new BindRxChoiceMenu(parent, moduleIdx, receiverIdx);
|
||||
|
||||
// ... prevent module mode being reset to NORMAL before exiting
|
||||
setCloseHandler(nullptr);
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
Dialog::checkEvents();
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void BindWaitDialog::onEvent(event_t event)
|
||||
{
|
||||
TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(),
|
||||
event);
|
||||
|
||||
if (event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ReceiverButton::ReceiverButton(FormGroup* parent, rect_t rect,
|
||||
uint8_t moduleIdx, uint8_t receiverIdx) :
|
||||
TextButton(parent, rect, STR_BIND,
|
||||
std::bind(&ReceiverButton::pressBind, this)),
|
||||
moduleIdx(moduleIdx),
|
||||
receiverIdx(receiverIdx)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReceiverButton::pressBind()
|
||||
{
|
||||
if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] == '\0') {
|
||||
startBind();
|
||||
} else {
|
||||
auto menu = new Menu(parent);
|
||||
menu->addLine(STR_BIND, [=]() {
|
||||
startBind();
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_OPTIONS, [=]() {
|
||||
// pushMenu(menuModelReceiverOptions);
|
||||
new RxOptions(this, moduleIdx, receiverIdx);
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_SHARE, [=]() {
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
modSetup.shareReceiverIndex = receiverIdx;
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_SHARE;
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_DELETE, [=]() {
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
memclear(&modSetup, sizeof(modSetup));
|
||||
modSetup.resetReceiverIndex = receiverIdx;
|
||||
modSetup.resetReceiverFlags = 0x01;
|
||||
new ConfirmDialog(parent, STR_RECEIVER, STR_RECEIVER_DELETE, [=]() {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_RESET;
|
||||
removePXX2Receiver(moduleIdx, receiverIdx);
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_RESET, [=]() {
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
memclear(&modSetup, sizeof(modSetup));
|
||||
modSetup.resetReceiverIndex = receiverIdx;
|
||||
modSetup.resetReceiverFlags = 0xFF;
|
||||
new ConfirmDialog(parent, STR_RECEIVER, STR_RECEIVER_DELETE, [=]() {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_RESET;
|
||||
removePXX2Receiver(moduleIdx, receiverIdx);
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
menu->setCloseHandler(
|
||||
[=]() { removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); });
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ReceiverButton::startBind()
|
||||
{
|
||||
auto& bindInfo = getPXX2BindInformationBuffer();
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
|
||||
memclear(&bindInfo, sizeof(bindInfo));
|
||||
bindInfo.rxUid = receiverIdx;
|
||||
bindInfo.step = BIND_INIT;
|
||||
|
||||
if (isModuleR9MAccess(moduleIdx)) {
|
||||
bindInfo.step = BIND_MODULE_TX_INFORMATION_REQUEST;
|
||||
#if defined(SIMU)
|
||||
modSetup.moduleInformation.information.modelID = 1;
|
||||
modSetup.moduleInformation.information.variant = 2;
|
||||
#else
|
||||
moduleState[moduleIdx].readModuleInformation(
|
||||
&modSetup.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID);
|
||||
#endif
|
||||
} else {
|
||||
moduleState[moduleIdx].startBind(&bindInfo);
|
||||
}
|
||||
|
||||
new BindWaitDialog(parent, moduleIdx, receiverIdx);
|
||||
}
|
||||
|
||||
void ReceiverButton::checkEvents()
|
||||
{
|
||||
if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] != '\0') {
|
||||
char receiverName[PXX2_LEN_RX_NAME + 1];
|
||||
memset(receiverName, 0, sizeof(receiverName));
|
||||
strncpy(receiverName,
|
||||
g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx],
|
||||
effectiveLen(
|
||||
g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx],
|
||||
PXX2_LEN_RX_NAME));
|
||||
setText(receiverName);
|
||||
} else {
|
||||
setText(STR_BIND);
|
||||
}
|
||||
|
||||
TextButton::checkEvents();
|
||||
}
|
||||
|
||||
RegisterDialog::RegisterDialog(Window* parent, uint8_t moduleIdx) :
|
||||
Dialog(parent, STR_REGISTER, {50, 73, LCD_W - 100, 0}), moduleIdx(moduleIdx)
|
||||
{
|
||||
FormGroup* form = &content->form;
|
||||
FormGridLayout grid(content->form.width());
|
||||
grid.setLabelWidth(150);
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
// Register ID
|
||||
new StaticText(form, grid.getLabelSlot(), STR_REG_ID, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
auto edit =
|
||||
new ModelTextEdit(form, grid.getFieldSlot(), g_model.modelRegistrationID,
|
||||
sizeof(g_model.modelRegistrationID));
|
||||
grid.nextLine();
|
||||
|
||||
// UID
|
||||
new StaticText(form, grid.getLabelSlot(), "UID", 0, COLOR_THEME_PRIMARY1);
|
||||
uid = new NumberEdit(
|
||||
form, grid.getFieldSlot(), 0, 2,
|
||||
GET_SET_DEFAULT(getPXX2ModuleSetupBuffer().registerLoopIndex));
|
||||
grid.nextLine();
|
||||
|
||||
// RX name
|
||||
new StaticText(form, grid.getLabelSlot(), STR_RX_NAME, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
waiting = new StaticText(form, grid.getFieldSlot(), STR_WAITING_FOR_RX, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
grid.nextLine();
|
||||
grid.spacer(6);
|
||||
|
||||
// Buttons
|
||||
exitButton =
|
||||
new TextButton(form, grid.getLabelSlot(), "EXIT", [=]() -> int8_t {
|
||||
this->deleteLater();
|
||||
return 0;
|
||||
});
|
||||
exitButton->setFocus(SET_FOCUS_DEFAULT);
|
||||
grid.nextLine();
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
FormField::link(exitButton, edit);
|
||||
form->setHeight(grid.getWindowHeight());
|
||||
content->adjustHeight();
|
||||
|
||||
start();
|
||||
|
||||
setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; });
|
||||
}
|
||||
|
||||
void RegisterDialog::start()
|
||||
{
|
||||
auto& pxx2Setup = getPXX2ModuleSetupBuffer();
|
||||
memclear(&pxx2Setup, sizeof(pxx2Setup));
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_REGISTER;
|
||||
}
|
||||
|
||||
void RegisterDialog::checkEvents()
|
||||
{
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
if (!rxName && modSetup.registerStep >= REGISTER_RX_NAME_RECEIVED) {
|
||||
rect_t rect = waiting->getRect();
|
||||
waiting->deleteLater();
|
||||
|
||||
rxName = new ModelTextEdit(&content->form, rect, modSetup.registerRxName,
|
||||
PXX2_LEN_RX_NAME);
|
||||
rect = exitButton->getRect();
|
||||
auto okButton = new TextButton(&content->form, rect, "OK", [=]() -> int8_t {
|
||||
auto& modSetup = getPXX2ModuleSetupBuffer();
|
||||
modSetup.registerStep = REGISTER_RX_NAME_SELECTED;
|
||||
return 0;
|
||||
});
|
||||
exitButton->setLeft(left() + rect.w + 10);
|
||||
FormField::link(uid, rxName);
|
||||
FormField::link(rxName, okButton);
|
||||
FormField::link(okButton, exitButton);
|
||||
okButton->setFocus(SET_FOCUS_DEFAULT);
|
||||
} else if (modSetup.registerStep == REGISTER_OK) {
|
||||
deleteLater();
|
||||
POPUP_INFORMATION(STR_REG_OK);
|
||||
}
|
||||
|
||||
Dialog::checkEvents();
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void RegisterDialog::onEvent(event_t event)
|
||||
{
|
||||
TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(),
|
||||
event);
|
||||
|
||||
if (event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
#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<coord_t>(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& rxInfo = module.receivers[receiverIdx].information;
|
||||
|
||||
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 = rxInfo.modelID;
|
||||
uint8_t rxVariant = rxInfo.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<uint8_t>(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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "menu.h"
|
||||
#include "static.h"
|
||||
#include "button.h"
|
||||
#include "dialog.h"
|
||||
#include "textedit.h"
|
||||
#include "numberedit.h"
|
||||
|
||||
class BindRxChoiceMenu : public Menu
|
||||
{
|
||||
public:
|
||||
BindRxChoiceMenu(Window* parent, uint8_t moduleIdx, uint8_t receiverIdx);
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
uint8_t receiverIdx;
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
bool onTouchEnd(coord_t x, coord_t y) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
class BindWaitDialog : public Dialog
|
||||
{
|
||||
public:
|
||||
BindWaitDialog(Window* parent, uint8_t moduleIdx, uint8_t receiverIdx);
|
||||
|
||||
void checkEvents() override;
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
uint8_t receiverIdx;
|
||||
};
|
||||
|
||||
class ReceiverButton : public TextButton
|
||||
{
|
||||
public:
|
||||
ReceiverButton(FormGroup* parent, rect_t rect, uint8_t moduleIdx,
|
||||
uint8_t receiverIdx);
|
||||
|
||||
uint8_t pressBind();
|
||||
void startBind();
|
||||
void checkEvents() override;
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
uint8_t receiverIdx;
|
||||
};
|
||||
|
||||
class RegisterDialog : public Dialog
|
||||
{
|
||||
public:
|
||||
RegisterDialog(Window* parent, uint8_t moduleIdx);
|
||||
|
||||
void start();
|
||||
void checkEvents() override;
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
NumberEdit* uid;
|
||||
StaticText* waiting;
|
||||
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();
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
#include "storage/modelslist.h"
|
||||
#include "algorithm"
|
||||
|
||||
#if defined(PXX2)
|
||||
#include "access_settings.h"
|
||||
#endif
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
|
||||
std::string switchWarninglabel(swsrc_t index)
|
||||
@@ -162,172 +166,6 @@ class FailSafePage : public Page {
|
||||
uint8_t moduleIdx;
|
||||
};
|
||||
|
||||
class RegisterDialog: public Dialog {
|
||||
public:
|
||||
RegisterDialog(Window * parent, uint8_t moduleIdx):
|
||||
Dialog(parent, STR_REGISTER, {50, 73, LCD_W - 100, 0}),
|
||||
moduleIdx(moduleIdx)
|
||||
{
|
||||
FormGroup * form = &content->form;
|
||||
FormGridLayout grid(content->form.width());
|
||||
grid.setLabelWidth(150);
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
// Register ID
|
||||
new StaticText(form, grid.getLabelSlot(), STR_REG_ID, 0, COLOR_THEME_PRIMARY1);
|
||||
auto edit = new ModelTextEdit(form, grid.getFieldSlot(), g_model.modelRegistrationID, sizeof(g_model.modelRegistrationID));
|
||||
grid.nextLine();
|
||||
|
||||
// UID
|
||||
new StaticText(form, grid.getLabelSlot(), "UID", 0, COLOR_THEME_PRIMARY1);
|
||||
uid = new NumberEdit(form, grid.getFieldSlot(), 0, 2, GET_SET_DEFAULT(reusableBuffer.moduleSetup.pxx2.registerLoopIndex));
|
||||
grid.nextLine();
|
||||
|
||||
// RX name
|
||||
new StaticText(form, grid.getLabelSlot(), STR_RX_NAME, 0, COLOR_THEME_PRIMARY1);
|
||||
waiting = new StaticText(form, grid.getFieldSlot(), STR_WAITING, 0, COLOR_THEME_PRIMARY1);
|
||||
grid.nextLine();
|
||||
grid.spacer(6);
|
||||
|
||||
// Buttons
|
||||
exitButton = new TextButton(form, grid.getLabelSlot(), "EXIT",
|
||||
[=]() -> int8_t {
|
||||
this->deleteLater();
|
||||
return 0;
|
||||
});
|
||||
exitButton->setFocus(SET_FOCUS_DEFAULT);
|
||||
grid.nextLine();
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
FormField::link(exitButton, edit);
|
||||
form->setHeight(grid.getWindowHeight());
|
||||
content->adjustHeight();
|
||||
|
||||
start();
|
||||
|
||||
setCloseHandler([=]() {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
});
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2));
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_REGISTER;
|
||||
}
|
||||
|
||||
void checkEvents() override
|
||||
{
|
||||
if (!rxName && reusableBuffer.moduleSetup.pxx2.registerStep >= REGISTER_RX_NAME_RECEIVED) {
|
||||
rect_t rect = waiting->getRect();
|
||||
waiting->deleteLater();
|
||||
|
||||
rxName = new ModelTextEdit(&content->form, rect, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME);
|
||||
rect = exitButton->getRect();
|
||||
auto okButton = new TextButton(&content->form, rect, "OK",
|
||||
[=]() -> int8_t {
|
||||
reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_RX_NAME_SELECTED;
|
||||
return 0;
|
||||
});
|
||||
exitButton->setLeft(left() + rect.w + 10);
|
||||
FormField::link(uid, rxName);
|
||||
FormField::link(rxName, okButton);
|
||||
FormField::link(okButton, exitButton);
|
||||
okButton->setFocus(SET_FOCUS_DEFAULT);
|
||||
}
|
||||
else if (reusableBuffer.moduleSetup.pxx2.registerStep == REGISTER_OK) {
|
||||
deleteLater();
|
||||
POPUP_INFORMATION(STR_REG_OK);
|
||||
}
|
||||
|
||||
Dialog::checkEvents();
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
NumberEdit * uid;
|
||||
StaticText * waiting;
|
||||
TextEdit * rxName = nullptr;
|
||||
TextButton * exitButton;
|
||||
};
|
||||
|
||||
class BindWaitDialog: public Dialog {
|
||||
public:
|
||||
BindWaitDialog(Window * parent, uint8_t moduleIdx, uint8_t receiverIdx):
|
||||
Dialog(parent, STR_BIND, {50, 73, LCD_W - 100, LCD_H - 146}),
|
||||
moduleIdx(moduleIdx),
|
||||
receiverIdx(receiverIdx)
|
||||
{
|
||||
new StaticText(&content->form, {0, height() / 2, width(), PAGE_LINE_HEIGHT}, STR_WAITING_FOR_RX, 0, CENTERED | COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
|
||||
void checkEvents() override;
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override
|
||||
{
|
||||
TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event);
|
||||
|
||||
if (event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
uint8_t receiverIdx;
|
||||
};
|
||||
|
||||
class BindRxChoiceMenu: public Menu {
|
||||
public:
|
||||
BindRxChoiceMenu(Window * parent, uint8_t moduleIdx, uint8_t receiverIdx):
|
||||
Menu(parent),
|
||||
moduleIdx(moduleIdx),
|
||||
receiverIdx(receiverIdx)
|
||||
{
|
||||
auto receiversCount = min<uint8_t>(reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount, PXX2_MAX_RECEIVERS_PER_MODULE);
|
||||
for (uint8_t i = 0; i < receiversCount; i++) {
|
||||
const char * receiverName = reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[i];
|
||||
addLine(receiverName, [=]() {
|
||||
reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex = i;
|
||||
if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_EU) {
|
||||
reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED;
|
||||
// POPUP_MENU_ADD_ITEM(STR_16CH_WITH_TELEMETRY);
|
||||
// POPUP_MENU_ADD_ITEM(STR_16CH_WITHOUT_TELEMETRY);
|
||||
// POPUP_MENU_START(onPXX2R9MBindModeMenu);
|
||||
}
|
||||
else if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_FLEX) {
|
||||
reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED;
|
||||
// POPUP_MENU_ADD_ITEM(STR_FLEX_868);
|
||||
// POPUP_MENU_ADD_ITEM(STR_FLEX_915);
|
||||
// POPUP_MENU_START(onPXX2R9MBindModeMenu);
|
||||
}
|
||||
else {
|
||||
#if defined(SIMU)
|
||||
memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], receiverName, PXX2_LEN_RX_NAME);
|
||||
storageDirty(EE_MODEL);
|
||||
reusableBuffer.moduleSetup.bindInformation.step = BIND_OK;
|
||||
new MessageDialog(parent, STR_BIND, STR_BIND_OK);
|
||||
#else
|
||||
reusableBuffer.moduleSetup.bindInformation.step = BIND_START;
|
||||
new BindWaitDialog(parent, moduleIdx, receiverIdx);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setCancelHandler([=]() {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
uint8_t receiverIdx;
|
||||
};
|
||||
|
||||
class BindChoiceMenu : public Menu
|
||||
{
|
||||
protected:
|
||||
@@ -400,122 +238,6 @@ class BindChoiceMenu : public Menu
|
||||
}
|
||||
};
|
||||
|
||||
void BindWaitDialog::checkEvents()
|
||||
{
|
||||
if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) {
|
||||
removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx);
|
||||
deleteLater();
|
||||
if (reusableBuffer.moduleSetup.bindInformation.step == BIND_OK)
|
||||
POPUP_INFORMATION(STR_BIND_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
if (reusableBuffer.moduleSetup.bindInformation.step == BIND_INIT && reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount > 0) {
|
||||
deleteLater();
|
||||
new BindRxChoiceMenu(this, moduleIdx, receiverIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
Dialog::checkEvents();
|
||||
}
|
||||
|
||||
class ReceiverButton: public TextButton
|
||||
{
|
||||
public:
|
||||
ReceiverButton(FormGroup * parent, rect_t rect, uint8_t moduleIdx, uint8_t receiverIdx):
|
||||
TextButton(parent, rect, STR_BIND, [=]() {
|
||||
if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] == '\0') {
|
||||
startBind();
|
||||
}
|
||||
else {
|
||||
auto menu = new Menu(parent);
|
||||
menu->addLine(STR_BIND, [=]() {
|
||||
startBind();
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_OPTIONS, [=]() {
|
||||
memclear(&reusableBuffer.hardwareAndSettings, sizeof(reusableBuffer.hardwareAndSettings));
|
||||
reusableBuffer.hardwareAndSettings.receiverSettings.receiverId = receiverIdx;
|
||||
// pushMenu(menuModelReceiverOptions);
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_SHARE, [=]() {
|
||||
reusableBuffer.moduleSetup.pxx2.shareReceiverIndex = receiverIdx;
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_SHARE;
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_DELETE, [=]() {
|
||||
memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2));
|
||||
reusableBuffer.moduleSetup.pxx2.resetReceiverIndex = receiverIdx;
|
||||
reusableBuffer.moduleSetup.pxx2.resetReceiverFlags = 0x01;
|
||||
new ConfirmDialog(parent, STR_RECEIVER, STR_RECEIVER_DELETE, [=]() {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_RESET;
|
||||
removePXX2Receiver(moduleIdx, receiverIdx);
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
menu->addLine(STR_RESET, [=]() {
|
||||
memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2));
|
||||
reusableBuffer.moduleSetup.pxx2.resetReceiverIndex = receiverIdx;
|
||||
reusableBuffer.moduleSetup.pxx2.resetReceiverFlags = 0xFF;
|
||||
new ConfirmDialog(parent, STR_RECEIVER, STR_RECEIVER_DELETE, [=]() {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_RESET;
|
||||
removePXX2Receiver(moduleIdx, receiverIdx);
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
menu->setCloseHandler([=]() {
|
||||
removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx);
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
}),
|
||||
moduleIdx(moduleIdx),
|
||||
receiverIdx(receiverIdx)
|
||||
{
|
||||
}
|
||||
|
||||
void startBind()
|
||||
{
|
||||
memclear(&reusableBuffer.moduleSetup.bindInformation, sizeof(BindInformation));
|
||||
reusableBuffer.moduleSetup.bindInformation.rxUid = receiverIdx;
|
||||
if (isModuleR9MAccess(moduleIdx)) {
|
||||
reusableBuffer.moduleSetup.bindInformation.step = BIND_MODULE_TX_INFORMATION_REQUEST;
|
||||
#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);
|
||||
}
|
||||
|
||||
new BindWaitDialog(parent, moduleIdx, receiverIdx);
|
||||
}
|
||||
|
||||
void checkEvents() override
|
||||
{
|
||||
if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] != '\0') {
|
||||
char receiverName[PXX2_LEN_RX_NAME + 1];
|
||||
memset(receiverName, 0, sizeof(receiverName));
|
||||
strncpy(receiverName, g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx],
|
||||
effectiveLen(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME));
|
||||
setText(receiverName);
|
||||
}
|
||||
else {
|
||||
setText(STR_BIND);
|
||||
}
|
||||
|
||||
TextButton::checkEvents();
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
uint8_t receiverIdx;
|
||||
};
|
||||
|
||||
class TrChoice : public Choice
|
||||
{
|
||||
public:
|
||||
@@ -792,8 +514,6 @@ class TrainerModuleWindow : public FormGroup
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ModuleWindow : public FormGroup {
|
||||
public:
|
||||
ModuleWindow(FormWindow * parent, const rect_t &rect, uint8_t moduleIdx) :
|
||||
@@ -917,7 +637,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,
|
||||
@@ -929,6 +651,7 @@ class ModuleWindow : public FormGroup {
|
||||
rfChoice->setFocus(SET_FOCUS_DEFAULT);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
#if defined(AFHDS2) || defined(AFHDS3)
|
||||
else if (isModuleFlySky(moduleIdx)) {
|
||||
rfChoice =
|
||||
@@ -1399,18 +1122,7 @@ class ModuleWindow : public FormGroup {
|
||||
resetPulsesAFHDS2();
|
||||
}
|
||||
#endif
|
||||
auto rssiDialog = new DynamicMessageDialog(
|
||||
this, "Range Test",
|
||||
[=]() {
|
||||
char buf[16];
|
||||
sprintf(buf, "%d db", (int)TELEMETRY_RSSI());
|
||||
return std::string(buf);
|
||||
},
|
||||
"RSSI:", 50,
|
||||
COLOR_THEME_SECONDARY1 | CENTERED | FONT(BOLD) | FONT(XL));
|
||||
rssiDialog->setCloseHandler([this]() {
|
||||
rangeButton->check(false);
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
startRSSIDialog([=]() {
|
||||
#if defined(AFHDS2)
|
||||
if (isModuleFlySky(moduleIdx)) {
|
||||
resetPulsesAFHDS2();
|
||||
@@ -1461,6 +1173,7 @@ class ModuleWindow : public FormGroup {
|
||||
grid.nextLine();
|
||||
}
|
||||
|
||||
#if defined(PXX2)
|
||||
// Register and Range buttons
|
||||
if (isModuleRFAccess(moduleIdx)) {
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_MODULE, 0, COLOR_THEME_PRIMARY1);
|
||||
@@ -1478,12 +1191,23 @@ class ModuleWindow : public FormGroup {
|
||||
}
|
||||
else {
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_RANGECHECK;
|
||||
startRSSIDialog();
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
// R9M Power
|
||||
if (isModuleR9M_FCC(moduleIdx)) {
|
||||
@@ -1498,7 +1222,7 @@ class ModuleWindow : public FormGroup {
|
||||
GET_DEFAULT(min<uint8_t>(g_model.moduleData[moduleIdx].pxx.power, R9M_LBT_POWER_MAX)),
|
||||
SET_DEFAULT(g_model.moduleData[moduleIdx].pxx.power));
|
||||
}
|
||||
|
||||
#if defined(PXX2)
|
||||
// Receivers
|
||||
if (isModuleRFAccess(moduleIdx)) {
|
||||
for (uint8_t receiverIdx = 0; receiverIdx < PXX2_MAX_RECEIVERS_PER_MODULE; receiverIdx++) {
|
||||
@@ -1509,7 +1233,7 @@ class ModuleWindow : public FormGroup {
|
||||
grid.nextLine();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
// SBUS refresh rate
|
||||
if (isModuleSBUS(moduleIdx)) {
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_REFRESHRATE, 0, COLOR_THEME_PRIMARY1);
|
||||
@@ -1532,6 +1256,23 @@ class ModuleWindow : public FormGroup {
|
||||
par->adjustInnerHeight();
|
||||
}
|
||||
|
||||
void startRSSIDialog(std::function<void()> closeHandler = nullptr)
|
||||
{
|
||||
auto rssiDialog = new DynamicMessageDialog(
|
||||
parent, "Range Test",
|
||||
[=]() {
|
||||
return std::to_string((int)TELEMETRY_RSSI()) + std::string(" db");
|
||||
},
|
||||
"RSSI:", 50,
|
||||
COLOR_THEME_SECONDARY1 | CENTERED | FONT(BOLD) | FONT(XL));
|
||||
|
||||
rssiDialog->setCloseHandler([this, closeHandler]() {
|
||||
rangeButton->check(false);
|
||||
moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
|
||||
if (closeHandler) closeHandler();
|
||||
});
|
||||
}
|
||||
|
||||
void checkEvents() override
|
||||
{
|
||||
if (isModuleFailsafeAvailable(moduleIdx) != hasFailsafe
|
||||
|
||||
@@ -9,8 +9,6 @@ set(GUI_SRC
|
||||
../common/stdlcd/model_inputs.cpp
|
||||
../common/stdlcd/model_mixes.cpp
|
||||
../common/stdlcd/view_text.cpp
|
||||
../common/stdlcd/model_module_options.cpp
|
||||
../common/stdlcd/model_receiver_options.cpp
|
||||
../common/stdlcd/model_notes.cpp
|
||||
../common/stdlcd/model_curves.cpp
|
||||
../common/stdlcd/radio_hardware.cpp
|
||||
@@ -55,3 +53,11 @@ if(PXX2 OR MULTIMODULE)
|
||||
../common/stdlcd/radio_spectrum_analyser.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(PXX2)
|
||||
set(GUI_SRC
|
||||
${GUI_SRC}
|
||||
../common/stdlcd/model_module_options.cpp
|
||||
../common/stdlcd/model_receiver_options.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -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)
|
||||
|
||||
+3
-26
@@ -1022,24 +1022,7 @@ union ReusableBuffer
|
||||
uint8_t previousType;
|
||||
uint8_t newType;
|
||||
BindInformation bindInformation;
|
||||
struct {
|
||||
union {
|
||||
uint8_t registerStep;
|
||||
uint8_t resetStep;
|
||||
};
|
||||
uint8_t registerPopupVerticalPosition;
|
||||
uint8_t registerPopupHorizontalPosition;
|
||||
int8_t registerPopupEditMode;
|
||||
char registerRxName[PXX2_LEN_RX_NAME];
|
||||
uint8_t registerLoopIndex; // will be removed later
|
||||
union {
|
||||
uint8_t shareReceiverIndex;
|
||||
uint8_t resetReceiverIndex;
|
||||
};
|
||||
uint8_t resetReceiverFlags;
|
||||
ModuleInformation moduleInformation;
|
||||
ModuleSettings moduleSettings;
|
||||
} pxx2;
|
||||
PXX2ModuleSetup pxx2;
|
||||
#if defined(BLUETOOTH)
|
||||
struct {
|
||||
char devices[MAX_BLUETOOTH_DISTANT_ADDR][LEN_BLUETOOTH_ADDR+1];
|
||||
@@ -1082,13 +1065,8 @@ union ReusableBuffer
|
||||
} version;
|
||||
#endif
|
||||
|
||||
struct {
|
||||
ModuleInformation modules[NUM_MODULES];
|
||||
uint32_t updateTime;
|
||||
ModuleSettings moduleSettings;
|
||||
ReceiverSettings receiverSettings; // when dealing with receiver settings, we also need module settings
|
||||
char msg[64];
|
||||
} hardwareAndSettings; // moduleOptions, receiverOptions, radioVersion
|
||||
// moduleOptions, receiverOptions, radioVersion
|
||||
PXX2HardwareAndSettings hardwareAndSettings;
|
||||
|
||||
struct {
|
||||
ModuleInformation modules[NUM_MODULES];
|
||||
@@ -1171,7 +1149,6 @@ extern ReusableBuffer reusableBuffer;
|
||||
|
||||
uint8_t zlen(const char *str, uint8_t size);
|
||||
bool zexist(const char *str, uint8_t size);
|
||||
unsigned int effectiveLen(const char * str, unsigned int size);
|
||||
char * strcat_zchar(char *dest, const char *name, uint8_t size, const char spaceSym = 0, const char *defaultName=nullptr, uint8_t defaultNameSize=0, uint8_t defaultIdx=0);
|
||||
#define strcatFlightmodeName(dest, idx) strcat_zchar(dest, g_model.flightModeData[idx].name, LEN_FLIGHT_MODE_NAME, 0, STR_FM, PSIZE(TR_FM), idx+1)
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@ InternalModulePulsesData intmodulePulsesData __DMA;
|
||||
ExternalModulePulsesData extmodulePulsesData __DMA;
|
||||
TrainerPulsesData trainerPulsesData __DMA;
|
||||
|
||||
//use only for PXX
|
||||
void ModuleState::startBind(BindInformation * destination, ModuleCallback bindCallback)
|
||||
// use only for PXX
|
||||
void ModuleState::startBind(BindInformation* destination,
|
||||
ModuleCallback bindCallback)
|
||||
{
|
||||
bindInformation = destination;
|
||||
callback = bindCallback;
|
||||
@@ -46,6 +47,45 @@ void ModuleState::startBind(BindInformation * destination, ModuleCallback bindCa
|
||||
#endif
|
||||
}
|
||||
|
||||
void ModuleState::readModuleInformation(ModuleInformation* destination,
|
||||
int8_t first, int8_t last)
|
||||
{
|
||||
moduleInformation = destination;
|
||||
moduleInformation->current = first;
|
||||
moduleInformation->maximum = last;
|
||||
mode = MODULE_MODE_GET_HARDWARE_INFO;
|
||||
}
|
||||
|
||||
void ModuleState::readModuleSettings(ModuleSettings* destination)
|
||||
{
|
||||
moduleSettings = destination;
|
||||
moduleSettings->state = PXX2_SETTINGS_READ;
|
||||
mode = MODULE_MODE_MODULE_SETTINGS;
|
||||
}
|
||||
|
||||
void ModuleState::writeModuleSettings(ModuleSettings* source)
|
||||
{
|
||||
moduleSettings = source;
|
||||
moduleSettings->state = PXX2_SETTINGS_WRITE;
|
||||
moduleSettings->timeout = 0;
|
||||
mode = MODULE_MODE_MODULE_SETTINGS;
|
||||
}
|
||||
|
||||
void ModuleState::readReceiverSettings(ReceiverSettings* destination)
|
||||
{
|
||||
receiverSettings = destination;
|
||||
receiverSettings->state = PXX2_SETTINGS_READ;
|
||||
mode = MODULE_MODE_RECEIVER_SETTINGS;
|
||||
}
|
||||
|
||||
void ModuleState::writeReceiverSettings(ReceiverSettings* source)
|
||||
{
|
||||
receiverSettings = source;
|
||||
receiverSettings->state = PXX2_SETTINGS_WRITE;
|
||||
receiverSettings->timeout = 0;
|
||||
mode = MODULE_MODE_RECEIVER_SETTINGS;
|
||||
}
|
||||
|
||||
void getModuleStatusString(uint8_t moduleIdx, char * statusText)
|
||||
{
|
||||
*statusText = 0;
|
||||
|
||||
+7
-109
@@ -67,77 +67,6 @@
|
||||
|
||||
extern uint8_t s_pulses_paused;
|
||||
|
||||
PACK(struct PXX2Version {
|
||||
uint8_t major;
|
||||
uint8_t revision:4;
|
||||
uint8_t minor:4;
|
||||
});
|
||||
|
||||
PACK(struct PXX2HardwareInformation {
|
||||
uint8_t modelID;
|
||||
PXX2Version hwVersion;
|
||||
PXX2Version swVersion;
|
||||
uint8_t variant;
|
||||
uint32_t capabilities; // variable length
|
||||
uint8_t capabilityNotSupported;
|
||||
});
|
||||
|
||||
PACK(struct ModuleInformation {
|
||||
int8_t current;
|
||||
int8_t maximum;
|
||||
uint8_t timeout;
|
||||
PXX2HardwareInformation information;
|
||||
struct {
|
||||
PXX2HardwareInformation information;
|
||||
tmr10ms_t timestamp;
|
||||
} receivers[PXX2_MAX_RECEIVERS_PER_MODULE];
|
||||
});
|
||||
|
||||
class ModuleSettings {
|
||||
public:
|
||||
uint8_t state; // 0x00 = READ 0x40 = WRITE
|
||||
tmr10ms_t timeout;
|
||||
uint8_t externalAntenna;
|
||||
int8_t txPower;
|
||||
uint8_t dirty;
|
||||
};
|
||||
|
||||
class ReceiverSettings {
|
||||
public:
|
||||
uint8_t state; // 0x00 = READ 0x40 = WRITE
|
||||
tmr10ms_t timeout;
|
||||
uint8_t receiverId;
|
||||
uint8_t dirty;
|
||||
uint8_t telemetryDisabled;
|
||||
uint8_t telemetry25mw;
|
||||
uint8_t pwmRate;
|
||||
uint8_t fport;
|
||||
uint8_t enablePwmCh5Ch6;
|
||||
uint8_t fport2;
|
||||
uint8_t outputsCount;
|
||||
uint8_t outputsMapping[24];
|
||||
};
|
||||
|
||||
class BindInformation {
|
||||
public:
|
||||
int8_t step;
|
||||
uint32_t timeout;
|
||||
char candidateReceiversNames[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME + 1];
|
||||
uint8_t candidateReceiversCount;
|
||||
uint8_t selectedReceiverIndex;
|
||||
uint8_t rxUid;
|
||||
uint8_t lbtMode;
|
||||
uint8_t flexMode;
|
||||
PXX2HardwareInformation receiverInformation;
|
||||
};
|
||||
|
||||
class OtaUpdateInformation: public BindInformation {
|
||||
public:
|
||||
char filename[FF_MAX_LFN + 1];
|
||||
uint32_t address;
|
||||
uint32_t module;
|
||||
};
|
||||
|
||||
typedef void (* ModuleCallback)();
|
||||
|
||||
PACK(struct ModuleState {
|
||||
@@ -146,6 +75,8 @@ PACK(struct ModuleState {
|
||||
uint8_t paused:1;
|
||||
uint8_t spare:7;
|
||||
uint16_t counter;
|
||||
|
||||
// PXX specific items
|
||||
union
|
||||
{
|
||||
ModuleInformation * moduleInformation;
|
||||
@@ -157,44 +88,11 @@ PACK(struct ModuleState {
|
||||
ModuleCallback callback;
|
||||
|
||||
void startBind(BindInformation * destination, ModuleCallback bindCallback = nullptr);
|
||||
|
||||
void readModuleInformation(ModuleInformation * destination, int8_t first, int8_t last)
|
||||
{
|
||||
moduleInformation = destination;
|
||||
moduleInformation->current = first;
|
||||
moduleInformation->maximum = last;
|
||||
mode = MODULE_MODE_GET_HARDWARE_INFO;
|
||||
}
|
||||
|
||||
void readModuleSettings(ModuleSettings * destination)
|
||||
{
|
||||
moduleSettings = destination;
|
||||
moduleSettings->state = PXX2_SETTINGS_READ;
|
||||
mode = MODULE_MODE_MODULE_SETTINGS;
|
||||
}
|
||||
|
||||
void writeModuleSettings(ModuleSettings * source)
|
||||
{
|
||||
moduleSettings = source;
|
||||
moduleSettings->state = PXX2_SETTINGS_WRITE;
|
||||
moduleSettings->timeout = 0;
|
||||
mode = MODULE_MODE_MODULE_SETTINGS;
|
||||
}
|
||||
|
||||
void readReceiverSettings(ReceiverSettings * destination)
|
||||
{
|
||||
receiverSettings = destination;
|
||||
receiverSettings->state = PXX2_SETTINGS_READ;
|
||||
mode = MODULE_MODE_RECEIVER_SETTINGS;
|
||||
}
|
||||
|
||||
void writeReceiverSettings(ReceiverSettings * source)
|
||||
{
|
||||
receiverSettings = source;
|
||||
receiverSettings->state = PXX2_SETTINGS_WRITE;
|
||||
receiverSettings->timeout = 0;
|
||||
mode = MODULE_MODE_RECEIVER_SETTINGS;
|
||||
}
|
||||
void readModuleInformation(ModuleInformation * destination, int8_t first, int8_t last);
|
||||
void readModuleSettings(ModuleSettings * destination);
|
||||
void writeModuleSettings(ModuleSettings * source);
|
||||
void readReceiverSettings(ReceiverSettings * destination);
|
||||
void writeReceiverSettings(ReceiverSettings * source);
|
||||
});
|
||||
|
||||
extern ModuleState moduleState[NUM_MODULES];
|
||||
|
||||
@@ -25,6 +25,50 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
BindInformation& getPXX2BindInformationBuffer()
|
||||
{
|
||||
return reusableBuffer.moduleSetup.bindInformation;
|
||||
}
|
||||
|
||||
PXX2HardwareAndSettings& getPXX2HardwareAndSettingsBuffer()
|
||||
{
|
||||
return reusableBuffer.hardwareAndSettings;
|
||||
}
|
||||
|
||||
uint8_t Pxx2Pulses::addFlag0(uint8_t module)
|
||||
{
|
||||
uint8_t flag0 = g_model.header.modelId[module] & 0x3F;
|
||||
@@ -418,12 +462,14 @@ bool Pxx2Pulses::setupFrame(uint8_t module)
|
||||
setupRegisterFrame(module);
|
||||
break;
|
||||
case MODULE_MODE_BIND:
|
||||
if (g_model.moduleData[module].type == MODULE_TYPE_ISRM_PXX2 && g_model.moduleData[module].subType != MODULE_SUBTYPE_ISRM_PXX2_ACCESS)
|
||||
if ((g_model.moduleData[module].type == MODULE_TYPE_ISRM_PXX2 &&
|
||||
g_model.moduleData[module].subType !=
|
||||
MODULE_SUBTYPE_ISRM_PXX2_ACCESS) ||
|
||||
(g_model.moduleData[module].type == MODULE_TYPE_XJT_LITE_PXX2)) {
|
||||
setupAccstBindFrame(module);
|
||||
else if (g_model.moduleData[module].type == MODULE_TYPE_XJT_LITE_PXX2)
|
||||
setupAccstBindFrame(module);
|
||||
else
|
||||
} else {
|
||||
setupAccessBindFrame(module);
|
||||
}
|
||||
break;
|
||||
case MODULE_MODE_RESET:
|
||||
setupResetFrame(module);
|
||||
|
||||
@@ -316,6 +316,111 @@ enum PXX2ReceiverStatus {
|
||||
PXX2_SETTINGS_OK
|
||||
};
|
||||
|
||||
PACK(struct PXX2Version {
|
||||
uint8_t major;
|
||||
uint8_t revision:4;
|
||||
uint8_t minor:4;
|
||||
});
|
||||
|
||||
PACK(struct PXX2HardwareInformation {
|
||||
uint8_t modelID;
|
||||
PXX2Version hwVersion;
|
||||
PXX2Version swVersion;
|
||||
uint8_t variant;
|
||||
uint32_t capabilities; // variable length
|
||||
uint8_t capabilityNotSupported;
|
||||
});
|
||||
|
||||
bool isPXX2PowerAvailable(const PXX2HardwareInformation& info, int value);
|
||||
|
||||
PACK(struct ModuleInformation {
|
||||
int8_t current;
|
||||
int8_t maximum;
|
||||
uint8_t timeout;
|
||||
PXX2HardwareInformation information;
|
||||
struct {
|
||||
PXX2HardwareInformation information;
|
||||
tmr10ms_t timestamp;
|
||||
} receivers[PXX2_MAX_RECEIVERS_PER_MODULE];
|
||||
});
|
||||
|
||||
class ModuleSettings {
|
||||
public:
|
||||
uint8_t state; // 0x00 = READ 0x40 = WRITE
|
||||
tmr10ms_t timeout;
|
||||
uint8_t externalAntenna;
|
||||
int8_t txPower;
|
||||
uint8_t dirty;
|
||||
};
|
||||
|
||||
struct PXX2ModuleSetup {
|
||||
union {
|
||||
uint8_t registerStep;
|
||||
uint8_t resetStep;
|
||||
};
|
||||
uint8_t registerPopupVerticalPosition;
|
||||
uint8_t registerPopupHorizontalPosition;
|
||||
int8_t registerPopupEditMode;
|
||||
char registerRxName[PXX2_LEN_RX_NAME];
|
||||
uint8_t registerLoopIndex; // will be removed later
|
||||
union {
|
||||
uint8_t shareReceiverIndex;
|
||||
uint8_t resetReceiverIndex;
|
||||
};
|
||||
uint8_t resetReceiverFlags;
|
||||
ModuleInformation moduleInformation;
|
||||
ModuleSettings moduleSettings;
|
||||
};
|
||||
|
||||
class ReceiverSettings {
|
||||
public:
|
||||
uint8_t state; // 0x00 = READ 0x40 = WRITE
|
||||
tmr10ms_t timeout;
|
||||
uint8_t receiverId;
|
||||
uint8_t dirty;
|
||||
uint8_t telemetryDisabled;
|
||||
uint8_t telemetry25mw;
|
||||
uint8_t pwmRate;
|
||||
uint8_t fport;
|
||||
uint8_t enablePwmCh5Ch6;
|
||||
uint8_t fport2;
|
||||
uint8_t outputsCount;
|
||||
uint8_t outputsMapping[24];
|
||||
};
|
||||
|
||||
class BindInformation {
|
||||
public:
|
||||
int8_t step;
|
||||
uint32_t timeout;
|
||||
char candidateReceiversNames[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME + 1];
|
||||
uint8_t candidateReceiversCount;
|
||||
uint8_t selectedReceiverIndex;
|
||||
uint8_t rxUid;
|
||||
uint8_t lbtMode;
|
||||
uint8_t flexMode;
|
||||
PXX2HardwareInformation receiverInformation;
|
||||
};
|
||||
|
||||
class OtaUpdateInformation: public BindInformation {
|
||||
public:
|
||||
char filename[FF_MAX_LFN + 1];
|
||||
uint32_t address;
|
||||
uint32_t module;
|
||||
};
|
||||
|
||||
struct PXX2HardwareAndSettings {
|
||||
ModuleInformation modules[NUM_MODULES];
|
||||
uint32_t updateTime;
|
||||
ModuleSettings moduleSettings;
|
||||
ReceiverSettings receiverSettings; // when dealing with receiver settings, we
|
||||
// also need module settings
|
||||
char msg[64];
|
||||
};
|
||||
|
||||
PXX2ModuleSetup& getPXX2ModuleSetupBuffer();
|
||||
BindInformation& getPXX2BindInformationBuffer();
|
||||
PXX2HardwareAndSettings& getPXX2HardwareAndSettingsBuffer();
|
||||
|
||||
extern ModuleFifo intmoduleFifo;
|
||||
extern ModuleFifo extmoduleFifo;
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ int8_t char2zchar(char c);
|
||||
void str2zchar(char *dest, const char *src, int size);
|
||||
int zchar2str(char *dest, const char *src, int size);
|
||||
int strnlen(const char *src, int max_size);
|
||||
unsigned int effectiveLen(const char * str, unsigned int size);
|
||||
|
||||
char *strAppend(char *dest, const char *source, int len = 0);
|
||||
char *strAppendUnsigned(char *dest, uint32_t value, uint8_t digits = 0,
|
||||
|
||||
@@ -117,13 +117,14 @@ void processRegisterFrame(uint8_t module, const uint8_t * frame)
|
||||
return;
|
||||
}
|
||||
|
||||
PXX2ModuleSetup& mod = reusableBuffer.moduleSetup.pxx2;
|
||||
switch(frame[3]) {
|
||||
case 0x00:
|
||||
if (reusableBuffer.moduleSetup.pxx2.registerStep == REGISTER_INIT) {
|
||||
if (mod.registerStep == REGISTER_INIT) {
|
||||
// RX_NAME follows, we store it for the next step
|
||||
str2zchar(reusableBuffer.moduleSetup.pxx2.registerRxName, (const char *)&frame[4], PXX2_LEN_RX_NAME);
|
||||
reusableBuffer.moduleSetup.pxx2.registerLoopIndex = frame[12];
|
||||
reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_RX_NAME_RECEIVED;
|
||||
memcpy(mod.registerRxName, (const char *)&frame[4], PXX2_LEN_RX_NAME);
|
||||
mod.registerLoopIndex = frame[12];
|
||||
mod.registerStep = REGISTER_RX_NAME_RECEIVED;
|
||||
#if defined(COLORLCD)
|
||||
pushEvent(EVT_REFRESH);
|
||||
#endif
|
||||
@@ -131,11 +132,11 @@ void processRegisterFrame(uint8_t module, const uint8_t * frame)
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
if (reusableBuffer.moduleSetup.pxx2.registerStep == REGISTER_RX_NAME_SELECTED) {
|
||||
if (mod.registerStep == REGISTER_RX_NAME_SELECTED) {
|
||||
// RX_NAME + PASSWORD follow, we check they are good
|
||||
if (memcmp(&frame[4], reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME) == 0 &&
|
||||
if (memcmp(&frame[4], mod.registerRxName, PXX2_LEN_RX_NAME) == 0 &&
|
||||
memcmp(&frame[12], g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID) == 0) {
|
||||
reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_OK;
|
||||
mod.registerStep = REGISTER_OK;
|
||||
moduleState[module].mode = MODULE_MODE_NORMAL;
|
||||
#if !defined(COLORLCD)
|
||||
POPUP_INFORMATION(STR_REG_OK);
|
||||
|
||||
Reference in New Issue
Block a user