Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for latest FrSky receivers #4077

Merged
merged 11 commits into from
Oct 9, 2023
165 changes: 112 additions & 53 deletions radio/src/gui/colorlcd/access_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,27 +707,105 @@ void RxOptions::checkEvents()
Dialog::checkEvents();
}

#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)
class OutputMappingChoice : public Choice
{
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");
protected:
uint32_t capabilities;
uint8_t ch_offset;
uint8_t channels;
uint8_t rx_pin;

int getOutputMapping()
{
auto& hwSettings = getPXX2HardwareAndSettingsBuffer();
return hwSettings.receiverSettings.outputsMapping[rx_pin];
}
return std::string();
}

void setOutputMapping(int val)
{
auto& hwSettings = getPXX2HardwareAndSettingsBuffer();
hwSettings.receiverSettings.outputsMapping[rx_pin] = val;
}

std::string getChannelText(int val)
{
if (val < channels) {
return std::string("CH") + std::to_string(ch_offset + val + 1);
}
return std::string();
}

void addMenuItem(int item, Menu* menu, int val, int& selectedIx)
{
menu->addLineBuffered(textHandler(item), [=]() { setValue(item); });
if (item == val) { selectedIx = menu->count() - 1; }
}

public:
OutputMappingChoice(Window* parent, uint32_t capabilities, uint8_t rx_model_id,
uint8_t module, uint8_t channels, uint8_t output_pin) :
Choice(parent, rect_t{}, 0, channels - 1,
std::bind(&OutputMappingChoice::getOutputMapping, this),
std::bind(&OutputMappingChoice::setOutputMapping, this,
std::placeholders::_1), 0),
capabilities(capabilities),
ch_offset(getShiftedChannel(module, 0)),
channels(channels),
rx_pin(output_pin)
{
if (isPXX2ReceiverOptionAvailable(rx_model_id, RECEIVER_OPTION_D_TELE_PORT)) {
setTextHandler([=] (int val) {
switch(val) {
case CH_MAP_SBUS_IN:
return std::string(STR_SBUSIN);
case CH_MAP_SBUS_OUT:
return std::string(STR_SBUSOUT);
case CH_MAP_SPORT:
return std::string(STR_SPORT);
case CH_MAP_FBUS:
return std::string(STR_FBUS);
default:
return getChannelText(val);
}
});
setFillMenuHandler([=](Menu* menu, int val, int& selectedIx) {
if (output_pin == 0) {
addMenuItem(CH_MAP_SBUS_IN, menu, val, selectedIx);
}
addMenuItem(CH_MAP_SBUS_OUT, menu, val, selectedIx);
addMenuItem(CH_MAP_SPORT, menu, val, selectedIx);
addMenuItem(CH_MAP_FBUS, menu, val, selectedIx);
});
return;
}

if (capabilities & (1 << RECEIVER_CAPABILITY_ENABLE_PWM_CH5_CH6)) {
if (CH_ENABLE_SPORT == output_pin) {
setTextHandler([=] (int val) {
if (val == channels) return std::string(STR_SPORT);
return getChannelText(val);
});
setMax(channels);
return;
} else if (CH_ENABLE_SBUS == output_pin) {
setTextHandler([=] (int val) {
if (val == channels) return std::string(STR_SBUSOUT);
return getChannelText(val);
});
setMax(channels);
return;
}
}

setTextHandler(std::bind(&OutputMappingChoice::getChannelText, this,
std::placeholders::_1));
}
};

void RxOptions::update()
{
Expand All @@ -736,8 +814,8 @@ void RxOptions::update()

auto& hwSettings = getPXX2HardwareAndSettingsBuffer();
auto& rxInfo = hwSettings.modules[moduleIdx].receivers[receiverIdx].information;
// uint8_t receiverModelId = rxInfo.modelID;
uint8_t receiverVariant = rxInfo.variant;
uint8_t rxModelId = rxInfo.modelID;
uint8_t rxVariant = rxInfo.variant;
uint8_t capabilities = rxInfo.capabilities;

FlexGridLayout grid(line_col_dsc, line_row_dsc, 2);
Expand Down Expand Up @@ -775,7 +853,7 @@ void RxOptions::update()
hwSettings.receiverSettings.telemetryDisabled = val;
});

if (isModuleR9MAccess(moduleIdx) && receiverVariant == PXX2_VARIANT_EU &&
if (isModuleR9MAccess(moduleIdx) && rxVariant == PXX2_VARIANT_EU &&
hwSettings.moduleSettings.txPower > 14 /*25mW*/) {
// read only field in this case
tele25mw->disable();
Expand Down Expand Up @@ -828,48 +906,29 @@ void RxOptions::update()
});
}

if (capabilities & (1 << RECEIVER_CAPABILITY_SBUS24)) {
line = form->newLine(&grid);
new StaticText(line, rect_t{}, STR_SBUS24);
new ToggleSwitch(
line, rect_t{},
[]() {
auto& hwSettings = getPXX2HardwareAndSettingsBuffer();
return hwSettings.receiverSettings.sbus24;
},
[](int val) {
auto& hwSettings = getPXX2HardwareAndSettingsBuffer();
hwSettings.receiverSettings.sbus24 = val;
});
}

auto outputsCount = min<uint8_t>(16, hwSettings.receiverSettings.outputsCount);
for (uint8_t i = 0; i < outputsCount; i++) {
line = form->newLine(&grid);
std::string i_str = std::to_string(i+1);
new StaticText(line, rect_t{}, 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);

// TODO
// 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(
line, rect_t{}, 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));
// lv_obj_clear_flag(chBar->getLvObj(), LV_OBJ_FLAG_HIDDEN);
// } else {
// lv_obj_add_flag(chBar->getLvObj(), LV_OBJ_FLAG_HIDDEN);
// }
});
chDn->setTextHandler(
[=](int val) { return getChannelText(moduleIdx, i, val); });
uint8_t channels = sentModuleChannels(moduleIdx);
new OutputMappingChoice(line, capabilities, rxModelId, moduleIdx, channels, i);
}

line = form->newLine(&grid);
Expand Down
2 changes: 1 addition & 1 deletion radio/src/gui/colorlcd/radio_sdmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class FrskyOtaFlashDialog : public Dialog

if (reusableBuffer.sdManager.otaUpdateInformation.step == BIND_INFO_REQUEST) {
uint8_t modelId = reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.modelID;
if (isPXX2ReceiverOptionAvailable(modelId, RECEIVER_OPTION_OTA)) {
if (isPXX2ReceiverOptionAvailable(modelId, RECEIVER_OPTION_OTA_TO_UPDATE_SELF)) {
char *tmp = strAppend(reusableBuffer.sdManager.otaReceiverVersion, TR_CURRENT_VERSION);
tmp = strAppendUnsigned(tmp, 1 + reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.major);
*tmp++ = '.';
Expand Down
99 changes: 73 additions & 26 deletions radio/src/gui/common/stdlcd/model_receiver_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ enum {
ITEM_RECEIVER_SETTINGS_TELEMETRY,
ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW,
ITEM_RECEIVER_SETTINGS_SPORT_MODE,
ITEM_RECEIVER_SETTINGS_SBUS24,
ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1,
ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2,
ITEM_RECEIVER_SETTINGS_PINMAP_FIRST
Expand All @@ -57,9 +58,6 @@ enum {
#define IS_RECEIVER_CAPABILITY_ENABLED(capability) (reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilities & (1 << capability))
#define IF_RECEIVER_CAPABILITY(capability, count) uint8_t(IS_RECEIVER_CAPABILITY_ENABLED(capability) ? count : HIDDEN_ROW)

#define CH_ENABLE_SPORT 4
#define CH_ENABLE_SBUS 5

bool isSPortModeAvailable(int mode)
{
uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId;
Expand All @@ -78,8 +76,17 @@ void menuModelReceiverOptions(event_t event)
if (event == EVT_ENTRY) {
// reusableBuffer.hardwareSettings should have been cleared before calling this menu
#if defined(SIMU)
reusableBuffer.hardwareAndSettings.receiverSettings.state = PXX2_SETTINGS_OK;
reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount = 8;
auto& hwSettings = getPXX2HardwareAndSettingsBuffer();
memclear(&hwSettings, sizeof(hwSettings));
hwSettings.moduleSettings.state = PXX2_SETTINGS_OK;
hwSettings.receiverSettings.state = PXX2_SETTINGS_OK;
moduleState[g_moduleIdx].mode = MODULE_MODE_NORMAL;
auto& rxInfo = hwSettings.modules[0].receivers[0].information;
rxInfo.capabilities = 0x62;
hwSettings.receiverSettings.outputsCount = 6;
for (int i=0; i<6; i++) {
hwSettings.receiverSettings.outputsMapping[i] = i;
}
#endif
}

Expand All @@ -88,13 +95,14 @@ void menuModelReceiverOptions(event_t event)
uint8_t receiverVariant = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.variant;

SUBMENU_NOTITLE(ITEM_RECEIVER_SETTINGS_PINMAP_FIRST + outputsCount, {
0, // PWM rate
isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry
IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0),
uint8_t((IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT) || IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) ? 0 : HIDDEN_ROW),
uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW),
uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW),
0 // channels ...
0, // ITEM_RECEIVER_SETTINGS_PWM_RATE
isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // ITEM_RECEIVER_SETTINGS_TELEMETRY
IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), // ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW
uint8_t((IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT) || IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) ? 0 : HIDDEN_ROW), // ITEM_RECEIVER_SETTINGS_SPORT_MODE
uint8_t(IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_SBUS24) ? 0 : HIDDEN_ROW), // ITEM_RECEIVER_SETTINGS_SBUS24
uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), // ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1
uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), // ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2
0 // ITEM_RECEIVER_SETTINGS_PINMAP_FIRST ...
});

if (menuEvent) {
Expand Down Expand Up @@ -188,6 +196,13 @@ void menuModelReceiverOptions(event_t event)
break;
}

case ITEM_RECEIVER_SETTINGS_SBUS24:
reusableBuffer.hardwareAndSettings.receiverSettings.sbus24 = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.sbus24, RECEIVER_OPTIONS_2ND_COLUMN, y, STR_SBUS24, attr, event);
if (attr && checkIncDec_Ret) {
reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY;
}
break;

case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1:
lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED);
break;
Expand All @@ -201,42 +216,74 @@ void menuModelReceiverOptions(event_t event)
{
uint8_t pin = i - ITEM_RECEIVER_SETTINGS_PINMAP_FIRST;
if (pin < reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount) {
uint8_t & mapping = reusableBuffer.hardwareAndSettings.receiverSettings.outputsMapping[pin];
uint8_t channel = g_model.moduleData[g_moduleIdx].channelsStart + mapping;
uint8_t mapping = reusableBuffer.hardwareAndSettings.receiverSettings.outputsMapping[pin];
lcdDrawText(0, y, STR_PIN);
lcdDrawNumber(lcdLastRightPos + 1, y, pin + 1);

uint8_t channelMax = sentModuleChannels(g_moduleIdx) - 1;
uint8_t selectionMax = channelMax;

if (IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_ENABLE_PWM_CH5_CH6)) {
if (CH_ENABLE_SPORT == pin || CH_ENABLE_SBUS == pin)
selectionMax += 1;
if (mapping <= channelMax) {
uint8_t channel = g_model.moduleData[g_moduleIdx].channelsStart + mapping;
putsChn(7 * FW, y, channel + 1, attr);
}

if (CH_ENABLE_SPORT == pin && selectionMax == channel) {
lcdDrawText(7 * FW, y, "S.PORT", attr);
if (isPXX2ReceiverOptionAvailable(receiverModelId, RECEIVER_OPTION_D_TELE_PORT)) {
if (mapping == CH_MAP_SPORT) {
lcdDrawText(7 * FW, y, STR_SPORT, attr);
mapping = channelMax + 1;
} else if (mapping == CH_MAP_SBUS_OUT) {
lcdDrawText(7 * FW, y, STR_SBUSOUT, attr);
mapping = channelMax + 2;
} else if (mapping == CH_MAP_FBUS) {
lcdDrawText(7 * FW, y, STR_FBUS, attr);
mapping = channelMax + 3;
}
else if (CH_ENABLE_SBUS == pin && selectionMax == channel) {
lcdDrawText(7 * FW, y, "SBUS", attr);
if (pin == 0) {
selectionMax = channelMax + 4;
if (mapping == CH_MAP_SBUS_IN) {
lcdDrawText(7 * FW, y, STR_SBUSIN, attr);
mapping = selectionMax;
}
}
else {
putsChn(7 * FW, y, channel + 1, attr);
selectionMax = channelMax + 3;
}
} else if (IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_ENABLE_PWM_CH5_CH6)) {
if (CH_ENABLE_SPORT == pin) {
if (++selectionMax == mapping) {
lcdDrawText(7 * FW, y, STR_SPORT, attr);
}
} else if (CH_ENABLE_SBUS == pin) {
if (++selectionMax == mapping) {
lcdDrawText(7 * FW, y, STR_SBUSOUT, attr);
}
}
}
else {
putsChn(7 * FW, y, channel + 1, attr);
}

// Channel
if (attr) {
mapping = checkIncDec(event, mapping, 0, selectionMax);
if (checkIncDec_Ret) {
if (isPXX2ReceiverOptionAvailable(receiverModelId, RECEIVER_OPTION_D_TELE_PORT)) {
if (mapping == channelMax + 1) {
mapping = CH_MAP_SPORT;
} else if (mapping == channelMax + 2) {
mapping = CH_MAP_SBUS_OUT;
} else if (mapping == channelMax + 3) {
mapping = CH_MAP_FBUS;
} else if (mapping == channelMax + 4) {
mapping = CH_MAP_SBUS_IN;
}
}
reusableBuffer.hardwareAndSettings.receiverSettings.outputsMapping[pin] = mapping;
reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY;
}
}

// Bargraph
if (channel <= channelMax) {
if (mapping <= channelMax) {
uint8_t channel = g_model.moduleData[g_moduleIdx].channelsStart + mapping;
int32_t channelValue = channelOutputs[channel];
#if !(defined(PCBX7) || defined(PCBX9LITE) || defined(PCBX9LITES)) // X7/X9 LCD doesn't like too many horizontal lines
lcdDrawRect(RECEIVER_OPTIONS_2ND_COLUMN, y + 2, wbar + 1, 4);
Expand Down
2 changes: 1 addition & 1 deletion radio/src/gui/common/stdlcd/radio_sdmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ void onUpdateStateChanged()
{
if (reusableBuffer.sdManager.otaUpdateInformation.step == BIND_INFO_REQUEST) {
uint8_t modelId = reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.modelID;
if (isPXX2ReceiverOptionAvailable(modelId, RECEIVER_OPTION_OTA)) {
if (isPXX2ReceiverOptionAvailable(modelId, RECEIVER_OPTION_OTA_TO_UPDATE_SELF)) {
POPUP_CONFIRMATION(getPXX2ReceiverName(modelId), onUpdateConfirmation);
char *tmp = strAppend(reusableBuffer.sdManager.otaReceiverVersion, TR_CURRENT_VERSION);
tmp = strAppendUnsigned(tmp, 1 + reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.major);
Expand Down
Loading