From 044c3bbf86a757afbb82ab3732ac48551851883a Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 8 Jul 2020 14:55:59 +0200 Subject: [PATCH] Multi R9/DSM (#7804) --- companion/src/modeledit/setup.cpp | 2 +- radio/src/gui/128x64/model_setup.cpp | 55 ++++++++++++++++++++------- radio/src/gui/212x64/model_setup.cpp | 51 +++++++++++++++++++------ radio/src/gui/480x272/model_setup.cpp | 54 +++++++++++++++++++------- radio/src/pulses/multi.cpp | 18 ++++----- radio/src/telemetry/spektrum.cpp | 53 +++++++++++++++----------- radio/src/telemetry/telemetry.h | 6 ++- 7 files changed, 167 insertions(+), 72 deletions(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 3fc74a3e7..90036bc57 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -570,7 +570,7 @@ void ModulePanel::update() ui->lowPower->setVisible(mask & MASK_MULTIMODULE); ui->autoBind->setVisible(mask & MASK_MULTIMODULE); if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) - ui->autoBind->setText(tr("Autodetect Format")); + ui->autoBind->setVisible(false); else ui->autoBind->setText(tr("Bind on channel")); diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 3dfca9645..c8eda1387 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1541,18 +1541,35 @@ void menuModelSetup(event_t event) } } + int8_t min, max; + getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - optionValue = 50 + 5 * optionValue; - - if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) + if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); - else - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + optionValue = optionValue & 0x01; + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + } + else { + if (min == 0 && max == 1) + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + else + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } if (attr) { - int8_t min, max; - getMultiOptionValues(multi_proto, min, max); - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, min, max); + CHECK_INCDEC_MODELVAR(event, optionValue, min, max); + if (checkIncDec_Ret) { + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFE) + optionValue; + } + else { + g_model.moduleData[moduleIdx].multi.optionValue = optionValue; + } + } } } #endif @@ -1659,10 +1676,22 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND: #endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) - g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_DSM_AUTODTECT, attr, event); - else - g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_AUTOBIND, attr, event); + if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { + int8_t value = (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) >> 1; + lcdDrawText(INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, value ? 11 : 22, attr); + lcdDrawText(lcdNextPos, y, "ms", attr); + if (attr) { + CHECK_INCDEC_MODELVAR(event, value, 0, 1); + if (checkIncDec_Ret) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFD) + (value << 1); + } + } + } + else { + g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, + STR_MULTI_AUTOBIND, attr, event); + } break; #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM: diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 829a7eee9..2f67a3dcb 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -1306,17 +1306,35 @@ void menuModelSetup(event_t event) } } - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - optionValue = 50 + 5 * optionValue; + int8_t min, max; + getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) + if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); - else - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + optionValue = optionValue & 0x01; + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + } + else { + if (min == 0 && max == 1) + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + else + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } if (attr) { - int8_t min, max; - getMultiOptionValues(multi_proto, min, max); - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, min, max); + CHECK_INCDEC_MODELVAR(event, optionValue, min, max); + if (checkIncDec_Ret) { + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFE) + optionValue; + } + else { + g_model.moduleData[moduleIdx].multi.optionValue = optionValue; + } + } } } #endif @@ -1375,10 +1393,21 @@ void menuModelSetup(event_t event) #if defined (MULTIMODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_DSM_AUTODTECT, attr, event); - else + if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { + int8_t value = (g_model.moduleData[EXTERNAL_MODULE].multi.optionValue & 0x02) >> 1; + lcdDrawText(INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, value ? 11 : 22, attr); + lcdDrawText(lcdNextPos, y, "ms", attr); + if (attr) { + CHECK_INCDEC_MODELVAR(event, value, 0, 1); + if (checkIncDec_Ret) { + g_model.moduleData[EXTERNAL_MODULE].multi.optionValue = (g_model.moduleData[EXTERNAL_MODULE].multi.optionValue & 0xFD) + (value << 1); + } + } + } + else { g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_AUTOBIND, attr, event); + } break; case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM: diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index dee079d96..99bd1492c 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1576,7 +1576,7 @@ bool menuModelSetup(event_t event) if (attr && l_posHorz>0) { if (s_editMode>0) { if (l_posHorz == 1) { - if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || isModuleAFHDS3(moduleIdx)) { + if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || isModuleAFHDS3(moduleIdx) || IS_R9_MULTI(moduleIdx)) { if (event == EVT_KEY_BREAK(KEY_ENTER)) { #if defined(AFHDS3) if (isModuleAFHDS3(moduleIdx)) { @@ -1683,17 +1683,35 @@ bool menuModelSetup(event_t event) } } - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - optionValue = 50 + 5 * optionValue; + int8_t min, max; + getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) + if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); - else - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + optionValue = optionValue & 0x01; + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, LEFT | attr, event); + } + else { + if (min == 0 && max == 1) + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, LEFT | attr, event); + else + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } if (attr) { - int8_t min, max; - getMultiOptionValues(multi_proto, min, max); - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, min, max); + CHECK_INCDEC_MODELVAR(event, optionValue, min, max); + if (checkIncDec_Ret) { + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFE) + optionValue; + } + else { + g_model.moduleData[moduleIdx].multi.optionValue = optionValue; + } + } } } #endif @@ -1761,11 +1779,21 @@ bool menuModelSetup(event_t event) case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND: #endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_DSM_AUTODTECT); - else + if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { + int8_t value = (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) >> 1; + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, value ? 11 : 22, attr, 0, "", "ms"); + if (attr) { + CHECK_INCDEC_MODELVAR(event, value, 0, 1); + if (checkIncDec_Ret) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFD) + (value << 1); + } + } + } + else { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_AUTOBIND); - g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); + g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); + } break; #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM: diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index cfcff15d0..c8170f217 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -178,7 +178,7 @@ void setupPulsesMulti(uint8_t moduleIdx) if (getMultiModuleStatus(moduleIdx).isValid()) { MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); if (status.minor >= 3 && !(status.flags & 0x80)) { //Version 1.3.x.x or more and Buffer not full - if (IS_D16_MULTI(moduleIdx) && moduleState[moduleIdx].mode == MODULE_MODE_BIND) { + if ((IS_D16_MULTI(moduleIdx) || IS_R9_MULTI(moduleIdx)) && moduleState[moduleIdx].mode == MODULE_MODE_BIND) { sendD16BindOption(moduleIdx);//1 byte of additional data } #if defined(LUA) @@ -313,15 +313,14 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe) // rfProtocol if (type == MODULE_SUBTYPE_MULTI_DSM2 +1 ) { - // Autobinding should always be done in DSMX 11ms - if (g_model.moduleData[moduleIdx].multi.autoBindMode && moduleState[moduleIdx].mode == MODULE_MODE_BIND) - subtype = MM_RF_DSM2_SUBTYPE_AUTO; - - // Multi module in DSM mode wants the number of channels to be used as option value - if (optionValue) - optionValue = 0x80 | sentModuleChannels(moduleIdx); // Max throw + // Multi module in DSM mode wants the number of channels to be used as option value along with other flags + if (optionValue & 0x01) + optionValue = 0x80; // Max throw else - optionValue = sentModuleChannels(moduleIdx); + optionValue = 0x00; + if (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) + optionValue |= 0x40; // 11ms servo refresh + optionValue |= sentModuleChannels(moduleIdx); //add number of channels } // Special treatment for the FrSky entry... @@ -336,7 +335,6 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe) if (g_model.moduleData[moduleIdx].getMultiProtocol() == MM_RF_CUSTOM_SELECTED) type = g_model.moduleData[moduleIdx].getMultiProtocol(); - uint8_t headerByte = 0x55; // header, byte 0, 0x55 for proto 0-31, 0x54 for proto 32-63 if (type & 0x20) diff --git a/radio/src/telemetry/spektrum.cpp b/radio/src/telemetry/spektrum.cpp index 60c9f2329..285cd8ac2 100644 --- a/radio/src/telemetry/spektrum.cpp +++ b/radio/src/telemetry/spektrum.cpp @@ -468,17 +468,17 @@ void processSpektrumPacket(const uint8_t *packet) // "I" here means the multi module /* -0-3 4 bytes -> Cyrf ID of the TX xor 0xFF but you don't care as I've checked it already... -4 1 byte -> RX version but you don't care... +0-3 4 bytes -> Cyrf ID of the TX xor 0xFF but don't care... +4 1 byte -> RX version but don't care... 5 1 byte -> number of channels, example 0x06=6 channels 6 1 byte -> max DSM type allowed: - 0x01 => 22ms 1024 DSM2 1 packet => number of channels is <8 and no telemetry - 0x02 => 22ms 1024 DSM2 2 packets => either a number of channel >7 or telemetry enable RX - 0x12 => 11ms 2048 DSM2 2 packets => can be any number of channels with/without telemetry -> this mode might be supported following Mike's trials, note the channels should be duplicated between the packets which is not the case today - 0xa2 => 22ms 2048 DSMX 1 packet => number of channels is <8 and no telemetry - 0xb2 => 11ms 2048 DSMX => can be any number of channels with/without telemetry -> this mode is only half supported since the channels should be duplicated between the packets which is not the case but might be supported following Mike's trials + 0x01 => 1024 DSM2 1 packet => number of channels is <8 and no telemetry + 0x02 => 1024 DSM2 2 packets => either a number of channel >7 or telemetry enable RX + 0x12 => 2048 DSM2 2 packets => can be any number of channels with/without telemetry -> this mode might be supported following Mike's trials, note the channels should be duplicated between the packets which is not the case today + 0xa2 => 2048 DSMX 1 packet => number of channels is <8 and no telemetry + 0xb2 => 2048 DSMX 2 packets => can be any number of channels with/without telemetry -> this mode is only half supported since the channels should be duplicated between the packets which is not the case but might be supported following Mike's trials 7 0x00: not sure of the use of this byte since I've always seen it at 0... -8-9 2 bytes CRC but you don't care as I've checked it already... +8-9 2 bytes CRC but don't care... Examples: DSM #Chan RXver Inductrix 0xa2 07 1 @@ -488,23 +488,32 @@ void processSpektrumPacket(const uint8_t *packet) void processDSMBindPacket(uint8_t module, const uint8_t *packet) { uint32_t debugval; - if (g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2 - && g_model.moduleData[module].multi.autoBindMode) { - + if (g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2 && g_model.moduleData[module].subType == MM_RF_DSM2_SUBTYPE_AUTO) { + // Only sets channel etc when in DSM/AUTO mode int channels = packet[5]; - // Only sets channel etc when in DSM multi mode + if (channels > 12) { + channels = 12; + } + else if (channels < 3) { + channels = 3; + } g_model.moduleData[module].channelsCount = channels - 8; - // bool use11ms = (packet[8] & 0x10) ; - if (packet[6] >= 0xb2) - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_11; - else if (packet[6] >= 0xa2) - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_22; - else if (packet[6] >= 0x12) - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_11; - else - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_22; - + switch(packet[6]) { + case 0xa2: + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_22; + break; + case 0x12: + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_11; + break; + case 0x01: + case 0x02: + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_22; + break; + default: // 0xb2 or unknown + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_11; + break; + } storageDirty(EE_MODEL); } diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 74671bbd4..d35198cdc 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -112,16 +112,18 @@ extern uint8_t telemetryProtocol; #if defined (MULTIMODULE) #define IS_D16_MULTI(module) (((g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY) && (g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16 || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_8CH || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_LBT || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_CLONED)) \ || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKYX2)) + #define IS_R9_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY_R9) #define IS_HOTT_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_HOTT) #define IS_RX_MULTI(module) ((g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_AFHDS2A_RX) || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKYX_RX) \ || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_BAYANG_RX) || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM_RX)) #if defined(HARDWARE_INTERNAL_MODULE) - #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && (IS_D16_MULTI(INTERNAL_MODULE)||IS_D16_MULTI(EXTERNAL_MODULE)))) + #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && (IS_D16_MULTI(INTERNAL_MODULE) || IS_D16_MULTI(EXTERNAL_MODULE) || IS_R9_MULTI(INTERNAL_MODULE) || IS_R9_MULTI(EXTERNAL_MODULE)))) #else - #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && IS_D16_MULTI(EXTERNAL_MODULE))) + #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && (IS_D16_MULTI(EXTERNAL_MODULE) || IS_R9_MULTI(EXTERNAL_MODULE)))) #endif #else #define IS_D16_MULTI(module) false + #define IS_R9_MULTI(module) false #define IS_HOTT_MULTI(module) false #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) #define IS_RX_MULTI(module) false