diff --git a/companion/src/firmwares/edgetx/yaml_moduledata.cpp b/companion/src/firmwares/edgetx/yaml_moduledata.cpp index 283f5510c..3d77ae941 100644 --- a/companion/src/firmwares/edgetx/yaml_moduledata.cpp +++ b/companion/src/firmwares/edgetx/yaml_moduledata.cpp @@ -259,6 +259,7 @@ Node convert::encode(const ModuleData& rhs) case PULSES_LEMON_DSMP: { Node dsmp; dsmp["flags"] = rhs.dsmp.flags; + dsmp["enableAETR"] = (int)rhs.dsmp.enableAETR; mod["dsmp"] = dsmp; } break; case PULSES_FLYSKY_AFHDS2A: { @@ -419,6 +420,7 @@ bool convert::decode(const Node& node, ModuleData& rhs) } else if (mod["dsmp"]) { Node dsmp = mod["dsmp"]; dsmp["flags"] >> rhs.dsmp.flags; + dsmp["enableAETR"] >> rhs.dsmp.enableAETR; } else if (mod["flysky"]) { Node flysky = mod["flysky"]; for (int i = 0; i < 4; i++) { diff --git a/companion/src/firmwares/moduledata.h b/companion/src/firmwares/moduledata.h index afd95b07a..0b86f59d3 100644 --- a/companion/src/firmwares/moduledata.h +++ b/companion/src/firmwares/moduledata.h @@ -213,6 +213,7 @@ class ModuleData { struct DSMP { unsigned int flags; + bool enableAETR; } dsmp; void clear(); diff --git a/companion/src/modeledit/setup_module.cpp b/companion/src/modeledit/setup_module.cpp index 187e9c4d4..25ce12bcd 100644 --- a/companion/src/modeledit/setup_module.cpp +++ b/companion/src/modeledit/setup_module.cpp @@ -54,6 +54,7 @@ #define MASK_AFHDS (1<<22) #define MASK_CSRF_ARMING_MODE (1<<23) #define MASK_CSRF_ARMING_TRIGGER (1<<24) +#define MASK_ENABLE_AETR (1<<25) quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; @@ -409,7 +410,7 @@ void ModulePanel::update() mask |= MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT | MASK_FAILSAFES | MASK_AFHDS; break; case PULSES_LEMON_DSMP: - mask |= MASK_CHANNELS_RANGE; + mask |= MASK_CHANNELS_RANGE | MASK_ENABLE_AETR; break; default: break; @@ -448,6 +449,8 @@ void ModulePanel::update() ui->channelsCount->setMaximum(module.getMaxChannelCount()); ui->channelsCount->setValue(module.channelsCount); ui->channelsCount->setSingleStep(firmware->getCapability(HasPPMStart) ? 1 : 2); + + // CRSF ui->label_crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); ui->crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); ui->crsfArmingTrigger->setVisible(mask & MASK_CSRF_ARMING_TRIGGER); @@ -611,6 +614,12 @@ void ModulePanel::update() ui->raw12bits->setChecked(module.ghost.raw12bits); } + // DSMP settings fields + ui->enableAETR->setVisible(mask & MASK_ENABLE_AETR); + if (mask & MASK_ENABLE_AETR) { + ui->enableAETR->setChecked(module.dsmp.enableAETR); + } + if (mask & MASK_ACCESS) { ui->rx1->setText(module.access.receiverName[0]); ui->rx2->setText(module.access.receiverName[1]); @@ -632,7 +641,7 @@ void ModulePanel::update() ui->clearRx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); ui->rx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); - // AFHFS + // AFHDS2A / AFHDS3 if (mask & MASK_AFHDS) { if (protocol == PULSES_FLYSKY_AFHDS2A) { ui->label_afhds->setText(tr("Options")); @@ -915,6 +924,11 @@ void ModulePanel::on_raw12bits_stateChanged(int state) module.ghost.raw12bits = (state == Qt::Checked); } +void ModulePanel::on_enableAETR_stateChanged(int state) +{ + module.dsmp.enableAETR = (state == Qt::Checked); +} + void ModulePanel::on_racingMode_stateChanged(int state) { module.access.racingMode = (state == Qt::Checked); diff --git a/companion/src/modeledit/setup_module.h b/companion/src/modeledit/setup_module.h index 56415f888..76a9383db 100644 --- a/companion/src/modeledit/setup_module.h +++ b/companion/src/modeledit/setup_module.h @@ -70,6 +70,7 @@ class ModulePanel : public ModelPanel void on_autoBind_stateChanged(int state); void on_disableChMap_stateChanged(int state); void on_raw12bits_stateChanged(int state); + void on_enableAETR_stateChanged(int state); void on_racingMode_stateChanged(int state); void on_disableTelem_stateChanged(int state); void on_lowPower_stateChanged(int state); diff --git a/companion/src/modeledit/setup_module.ui b/companion/src/modeledit/setup_module.ui index fcbb98b10..94cc274b7 100644 --- a/companion/src/modeledit/setup_module.ui +++ b/companion/src/modeledit/setup_module.ui @@ -399,7 +399,7 @@ - + Qt::Horizontal @@ -429,14 +429,14 @@ - + Arm using - + @@ -446,6 +446,13 @@ + + + + Enable AETR + + + diff --git a/companion/src/print/modelprinter.cpp b/companion/src/print/modelprinter.cpp index 10a46797c..ed0b5c6ca 100644 --- a/companion/src/print/modelprinter.cpp +++ b/companion/src/print/modelprinter.cpp @@ -234,6 +234,9 @@ QString ModelPrinter::printModule(int idx) if (module.protocol == PULSES_GHOST) { str << printLabelValue(tr("Raw 12 bits"), printBoolean(module.ghost.raw12bits, BOOLEAN_YN)); } + if (module.protocol == PULSES_LEMON_DSMP) { + str << printLabelValue(tr("Enable AETR"), printBoolean(module.dsmp.enableAETR, BOOLEAN_YN)); + } if (module.protocol == PULSES_CROSSFIRE) { str << printLabelValue(tr("Arming mode"), module.crsfArmingModeToString()); if (module.crsf.crsfArmingMode == ModuleData::CRSF_ARMING_MODE_SWITCH) { diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index a24ca37c4..c136cde64 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -575,6 +575,7 @@ PACK(struct ModuleData { }) crsf); NOBACKUP(struct { uint8_t flags; + uint8_t enableAETR : 1; } dsmp); } NAME(mod) FUNC(select_mod_type); diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index e9bc4c4fb..d09aecea7 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -194,6 +194,10 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE, ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS, +#endif +#if defined(DSMP) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_STATUS, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_ENABLE_AETR, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, @@ -542,6 +546,7 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) \ AFHDS3_PROTOCOL_ROW(EXTERNAL_MODULE) \ AFHDS3_MODE_ROWS(EXTERNAL_MODULE) \ + DSMP_STATUS_ROWS(EXTERNAL_MODULE) \ MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), \ IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), /* line reused for PPM: PPM settings */ \ IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* RxNum */ \ @@ -2125,12 +2130,13 @@ void menuModelSetup(event_t event) } } #endif - if (isModuleDSMP(moduleIdx) && - (oldFlag != newFlag) && +#if defined(DSMP) + if (isModuleDSMP(moduleIdx) && (oldFlag != newFlag) && (oldFlag == MODULE_MODE_BIND)) { // Restart DSMP module when exiting bind mode restartModule(moduleIdx); } +#endif } } break; @@ -2435,12 +2441,31 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: #endif +#endif +#if defined(AFHDS3) && defined(HARDWARE_EXTERNAL_MODULE) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: +#endif +#if (defined(MULTIMODULE) | defined(DSMP) | defined(AFHDS3)) && defined(HARDWARE_EXTERNAL_MODULE) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_STATUS: { + // MultiModule & LemonDSMP & AFHDS3 Status lcdDrawTextIndented(y, STR_MODULE_STATUS); getModuleStatusString(moduleIdx, reusableBuffer.moduleSetup.msg); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, reusableBuffer.moduleSetup.msg); break; } +#endif +#if defined(DSMP) && defined(HARDWARE_EXTERNAL_MODULE) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_ENABLE_AETR: + g_model.moduleData[EXTERNAL_MODULE].dsmp.enableAETR = + editCheckBox(g_model.moduleData[EXTERNAL_MODULE].dsmp.enableAETR, + MODEL_SETUP_2ND_COLUMN, y, STR_DSMP_ENABLE_AETR, attr, + event, INDENT_WIDTH); + break; +#endif + + +#if defined(MULTIMODULE) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS: #endif @@ -2456,14 +2481,6 @@ void menuModelSetup(event_t event) #endif #if defined(AFHDS3) && defined(HARDWARE_EXTERNAL_MODULE) - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: { - lcdDrawTextIndented(y, STR_MODULE_STATUS); - - char statusText[64]; - getModuleStatusString(moduleIdx, statusText); - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); - break; - } case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS: { lcdDrawTextIndented(y, STR_AFHDS3_POWER_SOURCE); char statusText[64]; diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index db48b1fe4..e6b34900a 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -139,6 +139,10 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE, ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS, +#endif +#if defined(DSMP) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_STATUS, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_ENABLE_AETR, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, @@ -561,6 +565,7 @@ void menuModelSetup(event_t event) MULTIMODULE_TYPE_ROW(EXTERNAL_MODULE) // ITEM_MODEL_SETUP_EXTERNAL_MODULE_PROTOCOL MULTIMODULE_DSM_CLONED_RAW(EXTERNAL_MODULE), // ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSM_CLONED MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) // ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS + ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS + DSMP_STATUS_ROWS(EXTERNAL_MODULE) // ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_STATUS AFHDS3_MODE_ROWS(EXTERNAL_MODULE) // ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), // ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE @@ -1619,12 +1624,13 @@ void menuModelSetup(event_t event) } } #endif - if (isModuleDSMP(moduleIdx) && - (oldFlag != newFlag) && +#if defined(DSMP) + if (isModuleDSMP(moduleIdx) && (oldFlag != newFlag) && (oldFlag == MODULE_MODE_BIND)) { // Restart DSMP module when exiting bind mode restartModule(moduleIdx); } +#endif } } break; @@ -1834,11 +1840,33 @@ void menuModelSetup(event_t event) #if defined (MULTIMODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: +#endif +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: +#endif +#if defined(DSMP) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_STATUS: +#endif +#if (defined(DSMP) || defined(MULTIMODULE) || defined(AFHDS3)) + { + // MultiModule & LemonDSMP & AFHDS Status lcdDrawTextIndented(y, STR_MODULE_STATUS); getModuleStatusString(EXTERNAL_MODULE, reusableBuffer.moduleSetup.msg); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, reusableBuffer.moduleSetup.msg); break; + } +#endif +#if defined(DSMP) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_ENABLE_AETR: + g_model.moduleData[EXTERNAL_MODULE].dsmp.enableAETR = + editCheckBox(g_model.moduleData[EXTERNAL_MODULE].dsmp.enableAETR, + MODEL_SETUP_2ND_COLUMN, y, STR_DSMP_ENABLE_AETR, attr, + event, INDENT_WIDTH); + break; +#endif + +#if defined(MULTIMODULE) && defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: lcdDrawTextIndented(y, STR_MODULE_SYNC); getModuleSyncStatusString(EXTERNAL_MODULE, reusableBuffer.moduleSetup.msg); @@ -1852,14 +1880,6 @@ void menuModelSetup(event_t event) lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, g_model.moduleData[EXTERNAL_MODULE].afhds3.telemetry ? STR_AFHDS3_ONE_TO_ONE_TELEMETRY : TR_AFHDS3_ONE_TO_MANY); break; - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: - { - lcdDrawTextIndented(y, TR_MODULE_STATUS); - char statusText[64]; - getModuleStatusString(EXTERNAL_MODULE, statusText); - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); - break; - } case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS: { lcdDrawTextIndented(y, STR_AFHDS3_POWER_SOURCE); diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index fa415bc7e..935aa10e2 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -109,6 +109,10 @@ macro(add_gui_src src) ) endmacro(add_gui_src) +if(DSMP) + add_gui_src(module/dsmp_settings.cpp) +endif() + if(LUA) add_gui_src(standalone_lua.cpp) set(SRC ${SRC} diff --git a/radio/src/gui/colorlcd/controls/channel_range.cpp b/radio/src/gui/colorlcd/controls/channel_range.cpp index 12bd64a10..5530d1eff 100644 --- a/radio/src/gui/colorlcd/controls/channel_range.cpp +++ b/radio/src/gui/colorlcd/controls/channel_range.cpp @@ -118,6 +118,13 @@ void ModuleChannelRange::update() { ChannelRange::update(); +#if defined(DSMP) + if (isModuleDSMP(moduleIdx)) { + // Disable Ch start, module asume starting in Ch1 + chStart->enable(false); + } +#endif + auto min_mod_ch = minModuleChannels(moduleIdx); auto max_mod_ch = maxModuleChannels(moduleIdx); chEnd->enable(min_mod_ch < max_mod_ch); diff --git a/radio/src/gui/colorlcd/module/dsmp_settings.cpp b/radio/src/gui/colorlcd/module/dsmp_settings.cpp new file mode 100644 index 000000000..2ece07ada --- /dev/null +++ b/radio/src/gui/colorlcd/module/dsmp_settings.cpp @@ -0,0 +1,79 @@ +/* + * 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 "dsmp_settings.h" + +#include "choice.h" +#include "edgetx.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +struct DSMPEnableAETR : public FormLine { + DSMPEnableAETR(Window* form, FlexGridLayout& layout, uint8_t moduleIdx) : + FormLine(form, layout) + { + new StaticText(this, rect_t{}, STR_DSMP_ENABLE_AETR); + + auto md = &g_model.moduleData[moduleIdx]; + + cb = new ToggleSwitch(this, rect_t{}, GET_SET_DEFAULT(md->dsmp.enableAETR)); + } + + void update() + { + show(); + cb->update(); + } + + private: + ToggleSwitch* cb; +}; + +DSMPSettings::DSMPSettings(Window* parent, + const FlexGridLayout& g, + uint8_t moduleIdx) : + Window(parent, rect_t{}), + md(&g_model.moduleData[moduleIdx]), + moduleIdx(moduleIdx) +{ + FlexGridLayout grid(g); + setFlexLayout(); + + // DSMP status + auto line = newLine(grid); + new StaticText(line, rect_t{}, STR_MODULE_STATUS); + new DynamicText( + line, rect_t{}, + [=] { + char msg[64] = ""; + getModuleStatusString(moduleIdx, msg); + return std::string(msg); + }); + + enableAETR_line = new DSMPEnableAETR(this, grid, moduleIdx); + + // Ensure elements properly initalised + update(); +} + +void DSMPSettings::update() { + enableAETR_line->update(); +} diff --git a/radio/src/gui/colorlcd/module/dsmp_settings.h b/radio/src/gui/colorlcd/module/dsmp_settings.h new file mode 100644 index 000000000..4c86a9d1b --- /dev/null +++ b/radio/src/gui/colorlcd/module/dsmp_settings.h @@ -0,0 +1,42 @@ +/* + * 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 "window.h" +#include "module_setup.h" + +class ToggleSwitch; + +struct ModuleData; +struct DSMPEnableAETR; + +class DSMPSettings : public Window, public ModuleOptions +{ + ModuleData* md; + uint8_t moduleIdx; + DSMPEnableAETR *enableAETR_line; + + public: + DSMPSettings(Window* parent, const FlexGridLayout& g, + uint8_t moduleIdx); + void update() override; +}; diff --git a/radio/src/gui/colorlcd/module/module_setup.cpp b/radio/src/gui/colorlcd/module/module_setup.cpp index 51974be47..b2cafa257 100644 --- a/radio/src/gui/colorlcd/module/module_setup.cpp +++ b/radio/src/gui/colorlcd/module/module_setup.cpp @@ -65,6 +65,10 @@ #include "multi_rfprotos.h" #endif +#if defined(DSMP) +#include "dsmp_settings.h" +#endif + #define SET_DIRTY() storageDirty(EE_MODEL) #define ETX_STATE_UNIQUE_ID_WARN LV_STATE_USER_1 @@ -163,6 +167,11 @@ class ModuleWindow : public Window modOpts = new PXX1AntennaSettings(this, grid, moduleIdx); } #endif + #if defined(DSMP) + else if (isModuleDSMP(moduleIdx)) { + modOpts = new DSMPSettings(this, grid, moduleIdx); + } + #endif // Channel Range auto line = newLine(grid); diff --git a/radio/src/gui/common/stdlcd/radio_version.cpp b/radio/src/gui/common/stdlcd/radio_version.cpp index 76ad4c546..1a9f74f04 100644 --- a/radio/src/gui/common/stdlcd/radio_version.cpp +++ b/radio/src/gui/common/stdlcd/radio_version.cpp @@ -152,10 +152,10 @@ if (event == EVT_ENTRY) { y += FH; continue; } -#if defined(MULTIMODULE) - if (isModuleMultimodule(module)) { +#if defined(MULTIMODULE) || defined(DSMP) + if (isModuleMultimodule(module) || isModuleDSMP(module)) { char statusText[64] = ""; - getMultiModuleStatus(module).getStatusString(statusText); + getModuleStatusString(module,statusText); lcdDrawText(COLUMN2_X, y, statusText); y += FH; continue; diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index fbbff716c..b51d456db 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -218,6 +218,12 @@ extern uint8_t MULTIMODULE_HASOPTIONS(uint8_t moduleIdx); #define AFHDS3_MODULE_ROWS(moduleIdx) #endif +#if defined(DSMP) +#define DSMP_STATUS_ROWS(moduleIdx) isModuleDSMP(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, isModuleDSMP(moduleIdx) ? (uint8_t) 0 : HIDDEN_ROW, +#else +#define DSMP_STATUS_ROWS(moduleIdx) +#endif + #define FAILSAFE_ROW(moduleIdx) isModuleFailsafeAvailable(moduleIdx) ? (g_model.moduleData[moduleIdx].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW extern uint8_t MODULE_OPTION_ROW(uint8_t moduleIdx); diff --git a/radio/src/lua/api_model.cpp b/radio/src/lua/api_model.cpp index 41780a01e..82a5a588d 100644 --- a/radio/src/lua/api_model.cpp +++ b/radio/src/lua/api_model.cpp @@ -38,6 +38,10 @@ #include "pulses/multi.h" #endif +#if defined(DSMP) +#include "pulses/dsmp.h" +#endif + /*luadoc @function model.getInfo() @@ -138,6 +142,9 @@ Get RF module parameters * 12 R9M_LITE_PRO_PXX2 * 13 SBUS * 14 XJT_LITE_PXX2 + * 15 MODULE_TYPE_FLYSKY_AFHDS3, + * 16 ?? + * 17 MODULE_TYPE_LEMON_DSMP `subType` values for XJT_PXX1: * -1 OFF @@ -159,6 +166,8 @@ Get RF module parameters * `protocol` (number) protocol number (Multi only) * `subProtocol` (number) sub-protocol number (Multi only) * `channelsOrder` (number) first 4 channels expected order (Multi only) + * if the module type is LemonDSMP additional info is available + * `channelsOrder` (number) first 4 channels expected order (DSMP only) @status current Introduced in 2.2.0 */ @@ -189,6 +198,16 @@ static int luaModelGetModule(lua_State *L) lua_pushtableinteger(L, "channelsOrder", -1); } } +#endif +#if defined(DSMP) + if (module.type == MODULE_TYPE_LEMON_DSMP) { + auto& status = getDSMPStatus(idx); + int ch_order = -1; + if (status.isValid() && status.ch_order != 0xFF) { + ch_order = status.ch_order; + } + lua_pushtableinteger(L, "channelsOrder", ch_order); + } #endif } else { diff --git a/radio/src/pulses/dsm2.cpp b/radio/src/pulses/dsm2.cpp index df6806758..6a5d82970 100644 --- a/radio/src/pulses/dsm2.cpp +++ b/radio/src/pulses/dsm2.cpp @@ -24,7 +24,6 @@ #include "mixer_scheduler.h" #include "edgetx.h" -#include "telemetry/spektrum.h" #define DSM2_SEND_BIND (1 << 7) #define DSM2_SEND_RANGECHECK (1 << 5) @@ -41,11 +40,48 @@ static bool _bind_restart = false; -static inline void sendByte(uint8_t*& p_buf, uint8_t b) +const etx_serial_init dsmUartParams = { + .baudrate = 0, + .encoding = ETX_Encoding_8N1, + .direction = ETX_Dir_TX, + .polarity = ETX_Pol_Inverted, +}; + +etx_module_state_t* dsmInit(uint8_t module, uint32_t baudrate, + uint16_t period, bool telemetry) { - *p_buf++ = b; + // only external module supported + if (module == INTERNAL_MODULE) return nullptr; + + etx_serial_init params(dsmUartParams); + params.baudrate = baudrate; + auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, true); + if (!mod_st) return nullptr; + + if (telemetry) { + params.direction = ETX_Dir_RX; + modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms, true); + } + + mixerSchedulerSetPeriod(module, period); + return mod_st; } +void dsmDeInit(void* ctx) +{ + auto mod_st = (etx_module_state_t*)ctx; + modulePortDeInit(mod_st); +} + +static void* dsm2Init(uint8_t module) +{ + auto mod_st = dsmInit(module, DSM2_BITRATE, DSM2_PERIOD, false); + mod_st->user_data = (void*)(uintptr_t)g_model.moduleData[module].subType; + return (void*)mod_st; +} + +static inline void sendByte(uint8_t*& p_buf, uint8_t b) { *p_buf++ = b; } + static void setupPulsesDSM2(uint8_t module, uint8_t type, uint8_t*& p_buf) { uint8_t dsmDat[DSM2_FRAME_SIZE]; @@ -99,160 +135,20 @@ static void setupPulsesDSM2(uint8_t module, uint8_t type, uint8_t*& p_buf) } } -#define DSMP_BITRATE 115200 -static void setupPulsesLemonDSMP(uint8_t module, uint8_t*& p_buf) +static void dsm2SendPulses(void* ctx, uint8_t* buffer, int16_t* channels, + uint8_t nChannels) { - static uint8_t pass = 0; - - const auto& md = g_model.moduleData[module]; - - uint8_t start_channel = md.channelsStart; - auto channels = md.getChannelsCount(); - auto flags = md.dsmp.flags & 0x3F; - - // Force setup packet in Bind mode. - auto module_mode = getModuleMode(module); - - sendByte(p_buf, 0xAA); - sendByte(p_buf, pass); - - // Setup packet - if (pass == 0) { - - if (module_mode == MODULE_MODE_BIND) { - flags = DSM2_SEND_BIND | (1 << 6 /* AUTO */); - channels = 12; - } - sendByte(p_buf, flags); - - uint8_t pwr = 7; - if (module_mode == MODULE_MODE_RANGECHECK) { - pwr = 4; - } - sendByte(p_buf, pwr); - sendByte(p_buf, channels ); - - // Model number - sendByte(p_buf, 1); - - // Send only 1 single Setup packet - pass = 1; - - } else { - - uint8_t current_channel = 0; - if (pass == 2) { - current_channel += 7; - } - - // Send channels - for (int i=0; i<7; i++) { - - if (current_channel < channels) { - - uint8_t channel = start_channel + current_channel; - int value = channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER; - uint16_t pulse; - - // Use 11-bit ? - if (flags & (1 << 2)) { - pulse = limit(0, ((value*349)>>9)+1024, 2047) | (current_channel << 11); - } else { - pulse = limit(0, ((value*13)>>5)+512, 1023) | (current_channel << 10); - } - - sendByte(p_buf, pulse >> 8); - sendByte(p_buf, pulse & 0xFF); - } else { - // Outside of announced number of channels: - // -> send invalid value - sendByte(p_buf, 0xFF); - sendByte(p_buf, 0xFF); - } - current_channel++; - } - } - - if (++pass > 2) pass = 1; - if (channels < 8) pass = 1; - - if (module_mode == MODULE_MODE_BIND) { - // bind packet is setup - pass = 0; - } - else if (--moduleState[module].counter == 0) { - // every 100th packet is setup - pass = 0; - moduleState[module].counter = 100; - } -} - -const etx_serial_init dsmUartParams = { - .baudrate = 0, - .encoding = ETX_Encoding_8N1, - .direction = ETX_Dir_TX, - .polarity = ETX_Pol_Inverted, -}; - -static etx_module_state_t* dsmInit(uint8_t module, uint32_t baudrate, - uint16_t period, bool telemetry) -{ - // only external module supported - if (module == INTERNAL_MODULE) return nullptr; - - etx_serial_init params(dsmUartParams); - params.baudrate = baudrate; - auto mod_st = modulePortInitSerial(module, ETX_MOD_PORT_UART, ¶ms, true); - if (!mod_st) return nullptr; - - if (telemetry) { - params.direction = ETX_Dir_RX; - modulePortInitSerial(module, ETX_MOD_PORT_SPORT, ¶ms, true); - } - - mixerSchedulerSetPeriod(module, period); - return mod_st; -} - -static void* dsm2Init(uint8_t module) -{ - auto mod_st = dsmInit(module, DSM2_BITRATE, DSM2_PERIOD, false); - mod_st->user_data = (void*)(uintptr_t)g_model.moduleData[module].subType; - return (void*)mod_st; -} - -static void* dsmpInit(uint8_t module) -{ - return (void*)dsmInit(module, DSMP_BITRATE, 11 * 1000 /* 11ms in us */, true); -} - -static void dsmDeInit(void* ctx) -{ - auto mod_st = (etx_module_state_t*)ctx; - modulePortDeInit(mod_st); -} - -static void _dsm_send(etx_module_state_t* st, uint8_t* buffer, uint32_t len) -{ - auto drv = modulePortGetSerialDrv(st->tx); - auto ctx = modulePortGetCtx(st->tx); - drv->sendBuffer(ctx, buffer, len); -} - -static void dsm2SendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels) -{ - // TODO: - (void)channels; - (void)nChannels; - auto mod_st = (etx_module_state_t*)ctx; auto module = modulePortGetModule(mod_st); + auto drv = modulePortGetSerialDrv(mod_st->tx); + auto drvCtx = modulePortGetCtx(mod_st->tx); + auto type = (uint8_t)(uintptr_t)mod_st->user_data; auto p_data = buffer; setupPulsesDSM2(module, type, p_data); - _dsm_send(mod_st, buffer, p_data - buffer); + drv->sendBuffer(drvCtx, buffer, p_data - buffer); } static void dsm2ConfigChange(void* ctx) @@ -267,28 +163,6 @@ static void dsm2ConfigChange(void* ctx) } } -static void dsmpSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels) -{ - // TODO: - (void)channels; - (void)nChannels; - - auto mod_st = (etx_module_state_t*)ctx; - auto module = modulePortGetModule(mod_st); - - auto p_data = buffer; - setupPulsesLemonDSMP(module, p_data); - _dsm_send(mod_st, buffer, p_data - buffer); -} - -static void dsmpProcessData(void* ctx, uint8_t data, uint8_t* buffer, uint8_t* len) -{ - auto mod_st = (etx_module_state_t*)ctx; - auto module = modulePortGetModule(mod_st); - - processSpektrumTelemetryData(module, data, buffer, *len); -} - // No telemetry const etx_proto_driver_t DSM2Driver = { .protocol = PROTOCOL_CHANNELS_DSM2, @@ -299,15 +173,4 @@ const etx_proto_driver_t DSM2Driver = { .processFrame = nullptr, .onConfigChange = dsm2ConfigChange, .txCompleted = modulePortSerialTxCompleted, -}; - -const etx_proto_driver_t DSMPDriver = { - .protocol = PROTOCOL_CHANNELS_DSMP, - .init = dsmpInit, - .deinit = dsmDeInit, - .sendPulses = dsmpSendPulses, - .processData = dsmpProcessData, - .processFrame = nullptr, - .onConfigChange = nullptr, - .txCompleted = modulePortSerialTxCompleted, -}; +}; \ No newline at end of file diff --git a/radio/src/pulses/dsm2.h b/radio/src/pulses/dsm2.h index ae8edf0c9..6c7e1639c 100644 --- a/radio/src/pulses/dsm2.h +++ b/radio/src/pulses/dsm2.h @@ -21,10 +21,13 @@ #pragma once +#include "hal/module_port.h" #include "hal/module_driver.h" +#include "timers_driver.h" // DSM2/DSMX -extern const etx_proto_driver_t DSM2Driver; -// Lemon RX DSMP -extern const etx_proto_driver_t DSMPDriver; +extern etx_module_state_t* dsmInit(uint8_t module, uint32_t baudrate, + uint16_t period, bool telemetry); +extern void dsmDeInit(void* ctx); +extern const etx_proto_driver_t DSM2Driver; \ No newline at end of file diff --git a/radio/src/pulses/dsmp.cpp b/radio/src/pulses/dsmp.cpp new file mode 100644 index 000000000..ff179fcb6 --- /dev/null +++ b/radio/src/pulses/dsmp.cpp @@ -0,0 +1,537 @@ +/* + * 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 "dsmp.h" +#include "hal/module_port.h" +#include "mixer_scheduler.h" + +#include "edgetx.h" +#include "telemetry/spektrum.h" + +#define DSMP_SEND_X_PLUS 0 + +#define DSMP_BITRATE 115200 + +#define MAX_REG_CHANNELS 12 +#define MAX_CHANNELS 20 + +// DSMP Flags +#define DSMP_FLAGS_DSMX 0x01 +#define DSMP_FLAGS_DSM2 0x00 +#define DSMP_FLAGS_11mS 0x02 +#define DSMP_FLAGS_22mS 0x00 +#define DSMP_FLAGS_2048 0x04 // 11 bits +#define DSMP_FLAGS_1024 0x00 // 10 bits +#define DSMP_FLAGS_FUTABA 0x10 // Translate to AETR -> TAER +// #define DSMP_FLAGS_DEVO 0x20 +#define DSMP_FLAGS_AUTO 0x40 +#define DSMP_FLAGS_BIND 0x80 + +#define DSMP_FLAGS_TXMODE (DSMP_FLAGS_DSMX | DSMP_FLAGS_11mS | DSMP_FLAGS_2048) + +// DSMP Power Settings +#define DSMP_POWER_NORMAL 7 +#define DSMP_POWER_RANGE 4 + +// Same Encoding as MultiModule (What channel is each of the functions) +// R | T | E | A +#define DSMP_CH_AETR ((3<<6)|(2<<4)|(1<<2)|0) +#define DSMP_CH_TAER ((3<<6)|(0<<4)|(2<<2)|1) + +#define DSMP_NO_CHANNEL 0xFF + +// MAP AETR->TAER: +static uint8_t AETR_TAER_MAP[] = {2, 0, 1}; + +static DSMPModuleStatus dsmpStatus = DSMPModuleStatus(); + +// Power cycle of the DSMP module +// The DSMP module code has the channel data initialized to 0 and initial number of bits as 10b (ch resolution 1024) +// The Setup message pass the last BIND configuration to the Module via the setup package (for example, 2048 it was bound to DSMX). +// Scenario 1: Setup package processed by DSMP before Ch data +// When the Setup package is received, will send the existing CH values (0), so servos will move to the side until valid +// Ch data is received. +// Scenario 2: Channel packed process by DSMP before Setup package +// In this case, it will assume that the Ch data is encoded in 10bits instead of 2048 (if DSMX), then the servos move to +// to extreme too. +// +// Both scenarios are not right, so initially send more frequently the Setup package (usualy every 2.2 sec), but will +// be sent every 2 messages (66ms). There is still jump, but for an instant. +// The only way to properly fix it is at the DSMP firmware level.. (fixed on DSMP V2). + + +#define FAST_INITIAL_SETUP_COUNT 50 // 3*22*50=3.3 sec of sending fast status after module activation + +// Using 22.06ms instead of 22ms for V1 module.. This is becuase the module try to send 4 RF +// packets to the RX with with spacing 4/7/4/7 (22ms cycle) just after receiving +// the message from the TX (perfect world). But the timing is not perfect on the +// DSMP module, is really 4.025/7.033/4.025/6.x and the last will be cut too short +// to 6.88 by the new TX message (needs to be at least 6.92, up to 7.05). That +// can create problems in some RXs (Servos not moving smoth). Compensating for +// that little extra delay to make the last closer to 7ms spacing. + +#define SCHEDULER_PERIOD_V1 22060 +#define SCHEDULER_PERIOD_V2 11000 + +static uint8_t pass; +static uint8_t pass0_counter; +static uint8_t pass0_period; + +static uint16_t fastSetup_count; + +#if DSMP_SEND_X_PLUS +static uint8_t x_plus_ch = 0; +#endif + +DSMPModuleStatus& getDSMPStatus(uint8_t module) +{ + return dsmpStatus; +} + +static void* dsmpInit(uint8_t module) +{ + TRACE("[DSMP] dsmpInit()"); + pass0_counter = 2; // Send Setup every 2 ch data packages + pass = 1; // Start Sending channels + pass0_period = 100; // Send pass0 every 100 messages + fastSetup_count = FAST_INITIAL_SETUP_COUNT; + return (void*)dsmInit(module, DSMP_BITRATE, SCHEDULER_PERIOD_V1, true); +} + + +static inline void sendByte(uint8_t*& p_buf, uint8_t b) +{ + *p_buf++ = b; +} + +#if defined(LUA) +static uint8_t isFPDataReady() +{ + // Check for Forward Programming data + // Multi_Buffer[0..2]=="DSM" -> Lua script is running + // Multi_Buffer[3]==0x70 + len -> TX to RX data ready to be sent + // Multi_Buffer[4..9]=6 bytes of TX to RX data + return (Multi_Buffer && (Multi_Buffer[3] & 0xF8) == 0x70 && + memcmp(Multi_Buffer, "DSM", 3) == 0); +} + +static void sendFPLemonDSMP(uint8_t*& p_buf) +{ + auto forwardProgLen = Multi_Buffer[3] & 0x0F; + TRACE("LemonDSMP: DSMP FwdProg Send data len = [%d]", forwardProgLen); + // Send Forward Prog Data (Includes Len + 6 bytes).. 9 bytes totat + sendByte(p_buf, 0xAA); + sendByte(p_buf, 3); // Pass=3 + + for (uint8_t i = 0; i < 7; i++) { + sendByte(p_buf, Multi_Buffer[3 + i]); + } + + Multi_Buffer[3] = 0x00; // Data sent, clear LUA to send more +} +#endif + +static void updateModuleStatus(uint8_t flags) +{ + // Update Status + dsmpStatus.lastUpdate = get_tmr10ms(); + dsmpStatus.flags = flags; + dsmpStatus.ch_order = (flags & DSMP_FLAGS_FUTABA) ? DSMP_CH_AETR : DSMP_CH_TAER; +} + +static uint16_t getDSMPChannelValue(uint8_t module, uint8_t flags, uint8_t channel) +{ + uint8_t txChannel = channel; + // Map from AETR->TAER ??? + if ((flags & DSMP_FLAGS_FUTABA) && (channel < 3)) { + txChannel = AETR_TAER_MAP[channel]; + } + + int value = channelOutputs[txChannel] + 2 * PPM_CH_CENTER(txChannel) - 2 * PPM_CENTER; + uint16_t pulse; + + if (flags & DSMP_FLAGS_2048) { // Use 11-bit ? + // Scale to 349/512=0.681, MultiModule is about 0.667 + pulse = limit(0, ((value * 349) >> 9) + 1024, 2047) | (channel << 11); + } else { + pulse = limit(0, ((value * 13) >> 5) + 512, 1023) | (channel << 10); + } + + return pulse; +} + +static void setupPulsesLemonDSMP(uint8_t module, uint8_t*& p_buf) +{ + const auto modelId = g_model.header.modelId[module]; + /* const*/ auto& md = g_model.moduleData[module]; + + //uint8_t start_channel = md.channelsStart; + auto channels = md.getChannelsCount(); + auto flags = md.dsmp.flags; + auto module_mode = getModuleMode(module); + auto version = dsmpStatus.version[0]; + + if (md.dsmp.enableAETR) { // Move GUI settings to flags + flags |= DSMP_FLAGS_FUTABA; + } + + if (pass0_counter == pass0_period/2) { + // update status String.. about each second + updateModuleStatus(flags); + } + +#if defined(LUA) + if ((version> 1) && isFPDataReady()) { // Sent any forward prog data?? + sendFPLemonDSMP(p_buf); + Multi_Buffer[3] = 0x00; // Data sent, clear LUA to send more + return; + } +#endif + + // Send channel data + sendByte(p_buf, 0xAA); + sendByte(p_buf, pass); + + // Setup packet + if (pass == 0) { + flags &= DSMP_FLAGS_TXMODE; // Only the TXMODE part + + if (module_mode == MODULE_MODE_BIND) { + flags = DSMP_FLAGS_BIND | DSMP_FLAGS_AUTO; + channels = min(channels, MAX_REG_CHANNELS); + md.dsmp.flags = DSMP_FLAGS_DSMX | DSMP_FLAGS_11mS | DSMP_FLAGS_2048; + storageDirty(EE_MODEL); + } + sendByte(p_buf, flags); + + uint8_t pwr = DSMP_POWER_NORMAL; + if (module_mode == MODULE_MODE_RANGECHECK) { + pwr = DSMP_POWER_RANGE; + } + sendByte(p_buf, pwr); + sendByte(p_buf, channels ); + sendByte(p_buf, modelId); // V1.0 ignores it, V2.0 use it + + pass = 1; // move to send Ch data + return; + } else { +#if DSMP_SEND_X_PLUS + uint8_t xPlusEnabled = (version > 1) && (channels >= MAX_REG_CHANNELS) && (flags & DSMP_FLAGS_DSMX); +#endif + // Channel DATA messages + uint8_t current_channel = 0; + if (pass == 2) { + current_channel += 7; + } + + // Send channels + for (int i=0; i<7; i++) { + uint16_t pulse = 0xFFFF; // NO-DATA + uint8_t ch_to_send = DSMP_NO_CHANNEL; + + if (current_channel < min(channels,MAX_REG_CHANNELS)) { + ch_to_send = current_channel; + } + +#if DSMP_SEND_X_PLUS + else if (xPlusEnabled && (current_channel >= MAX_REG_CHANNELS)) { + // X-plus channel multiplexing (Ch13-20) + ch_to_send = MAX_REG_CHANNELS + x_plus_ch; + x_plus_ch = (x_plus_ch + 1) % 8; // 8 X-plus channels + } +#endif + + if (ch_to_send != DSMP_NO_CHANNEL) { + pulse = getDSMPChannelValue(module, flags, ch_to_send); + } + + sendByte(p_buf, pulse >> 8); + sendByte(p_buf, pulse & 0xFF); + current_channel++; + } // For + } + + if (++pass > 2) pass = 1; + if (channels < 8) pass = 1; + + if (module_mode == MODULE_MODE_BIND) { // Force setup packet in Bind mode. + pass = 0; + } else if (--pass0_counter == 0) { + pass = 0; + updateModuleStatus(flags); + if ((version == 1) && (fastSetup_count > 0)) { + fastSetup_count--; + pass0_counter = 2; // Keep sendint setup every 2 ch messages + } else { + pass0_counter = pass0_period; // restar counter + } + } +} + +static void dsmpSendPulses(void* ctx, uint8_t* buffer, int16_t* channels, uint8_t nChannels) +{ + auto mod_st = (etx_module_state_t*)ctx; + auto module = modulePortGetModule(mod_st); + auto drv = modulePortGetSerialDrv(mod_st->tx); + auto drvCtx = modulePortGetCtx(mod_st->tx); + + auto p_data = buffer; + setupPulsesLemonDSMP(module, p_data); + drv->sendBuffer(drvCtx, buffer, p_data - buffer); +} + +/* The DSMP Bind Return Message contains: +Byte +0 Flags: + Protocol: Maxk 0x01 0=DSM2, 1=DSMX + RefreshCycle: Mask 0x02 0=22ms, 1=11ms + Resolution: Mask 0x04 0=1024, 1=2048 + Bind: Mask 0x80 1=Enter Bind Mode (Output only) + Bind_Auto: Mask 0x40 1=Use AutoBind (Output only) +1 ?? Always 0x00; ?? RX type maybe +2 Number of Channels (Up to 12) +3 TX Mode: + 0x01 - DSM2 1024/22ms, + 0x02 - DSM2 1024/22ms >7 channels, + 0x11 - DSM2 2048/11ms + 0x12 - DSM2 2048/11ms >7 channels, + 0xa2 - DSMX 22ms > 7 channels, + 0xb2 - DSMX 11ms + */ + +static void processDSMPBindPacket(uint8_t module, uint8_t* packet) +{ + uint8_t flags = packet[0]; + uint8_t rxType = packet[1]; + uint8_t channels = packet[2]; + uint8_t txMode = packet[3]; + + // save flags, only the TXMode Part + g_model.moduleData[module].dsmp.flags = flags & DSMP_FLAGS_TXMODE; + + // save number of channels + if (channels > MAX_REG_CHANNELS) { + channels = MAX_REG_CHANNELS; + } + g_model.moduleData[module].channelsCount = channels - 8; + if (dsmpStatus.version[0] == 1) { // V1.0 always use modelId=0 + g_model.header.modelId[module] = 0; + } + + TRACE("[DSMP] DSMP bind packet: flags:0x%X / Ch: %i / TxMode =0x%X", flags & 0x3F, channels, txMode); + + storageDirty(EE_MODEL); + + /* Finally stop binding as the rx just told us that it is bound */ + if (getModuleMode(module) == MODULE_MODE_BIND) { + setModuleMode(module, MODULE_MODE_NORMAL); + } + + // Convert DSMP bind to Spektrum compatible bind and continue with common Spektrum.cpp bind processing + uint8_t newPacket[10] = {0, 0, 0, 0, rxType, channels, txMode, 0, 0, 0}; + processDSMBindPacket(newPacket); + + // FARZU:.. The real problem was using module[moduleID].counter.. is actually used by the restart mechanism + restartModuleAsync(module, 10); // ~100ms +} + + +static void processDSMPManufacturerData(uint8_t module, uint8_t* packet) +{ + // Format M,M,M,M, V, V where M is 4 byte manufacturer data, and V is 2 byte + dsmpStatus.version[0] = packet[4]; // Major + dsmpStatus.version[1] = packet[5]; // Minor + + TRACE("LemonDSMP: Ver [%d.%d]", dsmpStatus.version[0], dsmpStatus.version[1]); + + if (dsmpStatus.version[0] > 1 && mixerSchedulerGetPeriod(module) != SCHEDULER_PERIOD_V2) { + // V2 suppors 11ms + mixerSchedulerSetPeriod(module, SCHEDULER_PERIOD_V2); + pass0_period = 200; // extend period + } +} + +static void dsmpTelemetryData(uint8_t module, uint8_t data, + uint8_t* rxBuffer, uint8_t& rxBufferCount) +{ + dsmpStatus.lastUpdate = get_tmr10ms(); + + if (rxBuffer[1] == 0x80 && rxBufferCount >= DSM_BIND_PACKET_LENGTH) { + processDSMPBindPacket(module, rxBuffer + 2); // Skip 0xAA 0x80 + rxBufferCount = 0; + return; + } + + if (rxBuffer[1] == 0xFF && rxBufferCount >= SPEKTRUM_TELEMETRY_LENGTH) { + processDSMPManufacturerData(module, rxBuffer + 2); // Skip 0xAA 0xFF + rxBufferCount = 0; + return; + } + + if (rxBufferCount >= SPEKTRUM_TELEMETRY_LENGTH) { + // Debug print content of Telemetry to console +#if 0 + debugPrintf("[DSMP] Packet 0x%02X rssi 0x%02X: ic2 0x%02x, %02x: ", + rxBuffer[0], rxBuffer[1], rxBuffer[2], rxBuffer[3]); + for (int i=4; i + if (rxBufferCount < 2) return; // Keep buiding + + auto expectedLen = rxBuffer[1]; + + // Overflow??? + if (expectedLen + 2 > TELEMETRY_RX_PACKET_SIZE) { + expectedLen = TELEMETRY_RX_PACKET_SIZE - 2; + } + + if (rxBufferCount >= expectedLen + 2) { + // msg Completed + auto dataType = rxBuffer[2]; + + if (dataType == 1) { // Debug Msg + TRACE_NOCRLF("[DSMP] Debug: "); + for (auto i = 0; i < expectedLen - 1; i++) { + TRACE_NOCRLF("%c", rxBuffer[3 + i]); + } + TRACE(CRLF); + } + + rxBufferCount = 0; // Build new message + } +} + + +static void dsmpProcessData(void* ctx, uint8_t data, uint8_t* buffer, + uint8_t* len) +{ + auto mod_st = (etx_module_state_t*)ctx; + auto module = modulePortGetModule(mod_st); + + uint8_t& rxBufferCount = *len; + + if (rxBufferCount == 0 && (data != 0xAA && data != 0xAB)) { + TRACE("[DSMP] invalid start byte 0x%02X", data); + return; + } + + if (rxBufferCount < TELEMETRY_RX_PACKET_SIZE) { + + TRACE("[DSMP] Data 0x%02X, len = %d", data, rxBufferCount); + buffer[rxBufferCount++] = data; // Keep building message + } else { + TRACE("[DSMP] array size %d error", rxBufferCount); + rxBufferCount = 0; + } + + if (buffer[0] == 0xAA) { // Telemetry data or Bind + dsmpTelemetryData(module, data, buffer, *len); + } else if (buffer[0] == 0xAB) { // Status + dsmpDebugData(module, data, buffer, *len); + } +} + +void DSMPModuleStatus::getStatusString(char* statusText) const +{ + if (!isValid()) { + strcpy(statusText, STR_MODULE_NO_TELEMETRY); + return; + } + + const auto& md = g_model.moduleData[EXTERNAL_MODULE]; + auto channels = md.getChannelsCount(); + + char* tmp = statusText; + + // Version + *tmp = 0; + tmp = strAppend(tmp, "v", 1); + tmp = strAppendUnsigned(tmp, dsmpStatus.version[0]); + tmp = strAppend(tmp, ".", 1); + tmp = strAppendUnsigned(tmp, dsmpStatus.version[1]); + + char b[] = "? "; + if (ch_order != 0xFF) { // Same encoding as MultiModule + uint8_t temp = ch_order; + b[temp & 0x03] = 'A'; + temp >>= 2; + b[temp & 0x03] = 'E'; + temp >>= 2; + b[temp & 0x03] = 'T'; + temp >>= 2; + b[temp & 0x03] = 'R'; + } + + tmp = strAppend(tmp, " ", 1); + tmp = strAppend(tmp, b, strlen(b)); + + const char* mode; + + mode = (flags & DSMP_FLAGS_DSMX) ? " X" : " 2"; // DSMX or DSM2 + tmp = strAppend(tmp, mode, strlen(mode)); + + mode = (flags & DSMP_FLAGS_11mS) ? "_2F" : "_1F"; // 1 or 2 Frames (11ms/22ms) + tmp = strAppend(tmp, mode, strlen(mode)); + +#if 0 + // Good for Debugging, but not much for regular users + + //mode = (flags & DSMP_FLAGS_2048) ? " 2048" : " 1024"; + //tmp = strAppend(tmp, mode, strlen(mode)); + + tmp = strAppend(tmp, " S:", 4); + uint16_t servoRefresh = (mixerSchedulerGetPeriod(EXTERNAL_MODULE) / 1000); + if (channels > 7) servoRefresh *= 2; + tmp = strAppendUnsigned(tmp, servoRefresh); + tmp = strAppend(tmp, "ms", 2); + + servoRefresh = mixerSchedulerGetPeriod(EXTERNAL_MODULE); + tmp = strAppend(tmp, " TX:", 5); + tmp = strAppendUnsigned(tmp, servoRefresh); + tmp = strAppend(tmp, "us", 2); +#endif + +} + +const etx_proto_driver_t DSMPDriver = { + .protocol = PROTOCOL_CHANNELS_DSMP, + .init = dsmpInit, + .deinit = dsmDeInit, + .sendPulses = dsmpSendPulses, + .processData = dsmpProcessData, + .processFrame = nullptr, + .onConfigChange = nullptr, + .txCompleted = modulePortSerialTxCompleted, +}; diff --git a/radio/src/pulses/dsmp.h b/radio/src/pulses/dsmp.h new file mode 100644 index 000000000..608d64242 --- /dev/null +++ b/radio/src/pulses/dsmp.h @@ -0,0 +1,39 @@ +/* + * 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 "dsm2.h" + +struct DSMPModuleStatus { + uint8_t version[2] = {1, 0}; // Default Version 1.0 + tmr10ms_t lastUpdate; + uint8_t ch_order = 0xFF; + uint8_t flags = 0; + + inline bool isValid() const { return (bool)(get_tmr10ms() - lastUpdate < 500);} + void getStatusString(char *statusText) const; +}; + +extern DSMPModuleStatus &getDSMPStatus(uint8_t module); + +// Lemon RX DSMP +extern const etx_proto_driver_t DSMPDriver; diff --git a/radio/src/pulses/modules_helpers.cpp b/radio/src/pulses/modules_helpers.cpp index 36d438a56..d1fe9dfe3 100644 --- a/radio/src/pulses/modules_helpers.cpp +++ b/radio/src/pulses/modules_helpers.cpp @@ -67,9 +67,8 @@ int8_t maxModuleChannels_M8(uint8_t moduleIdx) } } else if (isModuleMultimoduleDSM2(moduleIdx)) { return 4; // 12 channels - } else if (isModuleDSMP(moduleIdx) && - (g_model.moduleData[moduleIdx].dsmp.flags != 0)) { - return g_model.moduleData[moduleIdx].channelsCount; + } else if (isModuleDSMP(moduleIdx)) { + return 4; // 12 channels } else { return maxChannelsModules_M8[g_model.moduleData[moduleIdx].type]; } @@ -129,6 +128,9 @@ void setModuleType(uint8_t moduleIdx, uint8_t moduleType) } else if (moduleData.type == MODULE_TYPE_FLYSKY_AFHDS3) { resetAfhds3Options(moduleIdx); + } + else if (moduleData.type == MODULE_TYPE_LEMON_DSMP) { + restartModule(moduleIdx); // Restart DSMP when switching to it (example PPM->DSMP) } else resetAccessAuthenticationCount(); diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index d3a86bcc5..840b177e1 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -487,7 +487,7 @@ inline int8_t minModuleChannels(uint8_t idx) else if (isModuleSBUS(idx)) return 16; else if (isModuleDSMP(idx)) - return maxModuleChannels(idx); + return 1; // FARZU: module assume always start with Ch1. Was: maxModuleChannels(idx); else return 1; } @@ -532,6 +532,9 @@ inline bool isModuleRxNumAvailable(uint8_t moduleIdx) if (isModuleCrossfire(moduleIdx)) return true; + if (isModuleDSMP(moduleIdx)) + return true; + return false; } @@ -557,6 +560,9 @@ inline bool isModuleModelIndexAvailable(uint8_t idx) if (isModuleAFHDS3(idx)) return true; + + if (isModuleDSMP(idx)) + return true; return false; } diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 24aa1eb64..b04528670 100644 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -31,7 +31,14 @@ #include "pulses/pxx2.h" #include "pulses/flysky.h" + +#if defined(DSM2) #include "pulses/dsm2.h" +#endif + +#if defined(DSMP) +#include "pulses/dsmp.h" +#endif #if defined(PPM) #include "pulses/ppm.h" @@ -228,6 +235,12 @@ void getModuleStatusString(uint8_t moduleIdx, char * statusText) afhds3::getStatusString(moduleIdx, statusText); } #endif + +#if defined(DSMP) + if (isModuleDSMP(moduleIdx)) { + getDSMPStatus(moduleIdx).getStatusString(statusText); + } +#endif } void getModuleSyncStatusString(uint8_t moduleIdx, char * statusText) diff --git a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp index 990ea5c37..0426587a1 100644 --- a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp @@ -633,6 +633,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -646,7 +647,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_f16.cpp b/radio/src/storage/yaml/yaml_datastructs_f16.cpp index 14a79e7ca..bf55d0754 100644 --- a/radio/src/storage/yaml/yaml_datastructs_f16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_f16.cpp @@ -701,6 +701,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -714,7 +715,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp index b803abb19..6bcae7b09 100644 --- a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp @@ -657,6 +657,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -670,7 +671,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp b/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp index 41d0db0e1..3ff70c41d 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp @@ -692,6 +692,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -705,7 +706,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp index 9281afc51..5d827dd77 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp @@ -699,6 +699,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -712,7 +713,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp index 4e66e24b4..2e2487ecb 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp @@ -724,6 +724,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -737,7 +738,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 07e60f939..99dc24f22 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -699,6 +699,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -712,7 +713,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp index c4fff4aab..4fe539d90 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp @@ -692,6 +692,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -705,7 +706,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_st16.cpp b/radio/src/storage/yaml/yaml_datastructs_st16.cpp index 721cbd8ad..682b8fdc8 100644 --- a/radio/src/storage/yaml/yaml_datastructs_st16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_st16.cpp @@ -724,6 +724,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -737,7 +738,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_t15.cpp b/radio/src/storage/yaml/yaml_datastructs_t15.cpp index 1468c6117..425351341 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15.cpp @@ -709,6 +709,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -722,7 +723,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp index 721cbd8ad..682b8fdc8 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp @@ -724,6 +724,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -737,7 +738,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_t20.cpp b/radio/src/storage/yaml/yaml_datastructs_t20.cpp index 8338142e9..d9a6f2e4d 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t20.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t20.cpp @@ -642,6 +642,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -655,7 +656,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp index 4178251a4..ec744cfb4 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp @@ -642,6 +642,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -655,7 +656,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp index 721cbd8ad..682b8fdc8 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp @@ -724,6 +724,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -737,7 +738,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index 071a51f3c..f2293a92c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -700,6 +700,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -713,7 +714,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp index 4802d38d8..48d4034ff 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp @@ -633,6 +633,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -646,7 +647,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp b/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp index 4802d38d8..48d4034ff 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp @@ -633,6 +633,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -646,7 +647,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp index 3820b34c6..0cc9ff215 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp @@ -633,6 +633,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -646,7 +647,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp index 990ea5c37..0426587a1 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp @@ -633,6 +633,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -646,7 +647,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp index 78a079b76..dea9859e5 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp @@ -637,6 +637,7 @@ static const struct YamlNode struct_anonymous_12[] = { }; static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), YAML_END }; static const struct YamlNode union_anonymous_4_elmts[] = { @@ -650,7 +651,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index ab80eed71..2bb691fd4 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -11,6 +11,7 @@ option(DEBUG_TRACE_BUFFER "Debug Trace Screen" OFF) option(XJT "XJT TX Module" ON) option(PPM "PPM TX Module" ON) option(DSM2 "DSM2 TX Module" ON) +option(DSMP "DSMP LemonRX TX Module" ON) option(SBUS "SBUS TX Module" ON) option(CROSSFIRE "Crossfire TX Module" ON) option(AFHDS2 "Support for AFHDS2" OFF) @@ -192,6 +193,12 @@ if(DSM2) add_definitions(-DDSM2) endif() +if(DSMP) + add_definitions(-DDSM2) + add_definitions(-DDSMP) +endif() + + if(SBUS) add_definitions(-DSBUS) endif() @@ -312,6 +319,14 @@ if(DSM2) ) endif() +if(DSMP) + set(PULSES_SRC + ${PULSES_SRC} + dsmp.cpp + ) +endif() + + if(SBUS) set(PULSES_SRC ${PULSES_SRC} diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 425fee9a9..21cf69de2 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -357,6 +357,58 @@ static void processMultiProtoDef(uint8_t module, const uint8_t * packet, uint8_t #endif +static void processMultiDSMBindPacket(uint8_t module, const uint8_t *packet) +{ + if (//g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && + //g_model.moduleData[module].multi.rfProtocol == + // 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]; + if (channels > 12) { + channels = 12; + } else if (channels < 3) { + channels = 3; + } + + 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; + if (channels == 7) { + channels = 12; // change the number of channels if 7 + } + 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; + if (channels == 7) { + channels = 12; // change the number of channels if 7 + } + break; + } + + g_model.moduleData[module].channelsCount = channels - 8; + // clear the 11ms servo refresh rate flag + g_model.moduleData[module].multi.optionValue &= 0xFD; + + storageDirty(EE_MODEL); + } + + /* Finally stop binding as the rx just told us that it is bound */ + if (getModuleMode(module) == MODULE_MODE_BIND) { + setMultiBindStatus(module, MULTI_BIND_FINISHED); + } + + // Continue with the common Spektrum Processing + processDSMBindPacket(packet); +} + static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) { uint8_t type = packet[0]; @@ -372,7 +424,7 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) case DSMBindPacket: if (len >= 10) - processDSMBindPacket(module, data); + processMultiDSMBindPacket(module, data); break; case SpektrumTelemetry: diff --git a/radio/src/telemetry/spektrum.cpp b/radio/src/telemetry/spektrum.cpp index 56ba1aa54..39eafff18 100644 --- a/radio/src/telemetry/spektrum.cpp +++ b/radio/src/telemetry/spektrum.cpp @@ -849,131 +849,16 @@ void processSpektrumPacket(const uint8_t *packet) LemonRX+Sat+tele 0xb2 07 1 */ -void processDSMBindPacket(uint8_t module, const uint8_t *packet) +void processDSMBindPacket(const uint8_t *packet) { uint32_t debugval; - - if (g_model.moduleData[module].type == MODULE_TYPE_LEMON_DSMP) { - - // save flags - g_model.moduleData[module].dsmp.flags = packet[0]; - - // save number of channels - uint8_t channels = packet[2]; - if (channels > 12) { channels = 12; } - g_model.moduleData[module].channelsCount = channels - 8; - - TRACE("[SPK] DSMP bind packet: 0x%X / %i", - packet[0] & 0x3F, packet[2]); - - storageDirty(EE_MODEL); - - moduleState[module].mode = MODULE_MODE_NORMAL; - restartModuleAsync(module, 50); // ~500ms - } -#if defined(MULTIMODULE) - else if (g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && - g_model.moduleData[module].multi.rfProtocol == - 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]; - if (channels > 12) { - channels = 12; - } - else if (channels < 3) { - channels = 3; - } - - 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; - if (channels == 7) { - channels = 12; // change the number of channels if 7 - } - 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; - if (channels == 7) { - channels = 12; // change the number of channels if 7 - } - break; - } - - g_model.moduleData[module].channelsCount = channels - 8; - // clear the 11ms servo refresh rate flag - g_model.moduleData[module].multi.optionValue &= 0xFD; - - storageDirty(EE_MODEL); - } -#endif debugval = packet[7] << 24 | packet[6] << 16 | packet[5] << 8 | packet[4]; /* log the bind packet as telemetry for quick debugging */ setTelemetryValue(PROTOCOL_TELEMETRY_SPEKTRUM, I2C_PSEUDO_TX_BIND, 0, 0, debugval, UNIT_RAW, 0); - - /* Finally stop binding as the rx just told us that it is bound */ - if (getModuleMode(module) == MODULE_MODE_BIND) { - auto module_type = g_model.moduleData[module].type; -#if defined(MULTIMODULE) - if (module_type == MODULE_TYPE_MULTIMODULE && - g_model.moduleData[module].multi.rfProtocol == - MODULE_SUBTYPE_MULTI_DSM2) { - setMultiBindStatus(module, MULTI_BIND_FINISHED); - } else -#endif - if (module_type == MODULE_TYPE_LEMON_DSMP) { - setModuleMode(module, MODULE_MODE_NORMAL); - } - } } -void processSpektrumTelemetryData(uint8_t module, uint8_t data, - uint8_t *rxBuffer, uint8_t &rxBufferCount) -{ - if (rxBufferCount == 0 && data != 0xAA) { - TRACE("[SPK] invalid start byte 0x%02X", data); - return; - } - - if (rxBufferCount < TELEMETRY_RX_PACKET_SIZE) { - rxBuffer[rxBufferCount++] = data; - } - else { - TRACE("[SPK] array size %d error", rxBufferCount); - rxBufferCount = 0; - } - - if (rxBuffer[1] == 0x80 && rxBufferCount >= DSM_BIND_PACKET_LENGTH) { - processDSMBindPacket(module, rxBuffer+2); - rxBufferCount = 0; - return; - } - - if (rxBufferCount >= SPEKTRUM_TELEMETRY_LENGTH) { - // Debug print content of Telemetry to console -#if 0 - debugPrintf("[SPK] Packet 0x%02X rssi 0x%02X: ic2 0x%02x, %02x: ", - rxBuffer[0], rxBuffer[1], rxBuffer[2], rxBuffer[3]); - for (int i=4; iSTR_MENUCUSTOMSCRIPTS #endif -#if defined(DSM2) || defined(PXX) +#if defined(DSM2) || defined(PXX) || defined(DSMP) #define STR_BIND_OK currentLangStrings->STR_BIND_OK #define STR_REBIND currentLangStrings->STR_REBIND #define STR_RECEIVER_NUM currentLangStrings->STR_RECEIVER_NUM @@ -338,6 +338,10 @@ #define STR_REG_OK currentLangStrings->STR_REG_OK #endif +#if defined(DSMP) +#define STR_DSMP_ENABLE_AETR currentLangStrings->STR_DSMP_ENABLE_AETR +#endif + #if defined(AFHDS3) #define STR_AFHDS3_ACTUAL_POWER currentLangStrings->STR_AFHDS3_ACTUAL_POWER #define STR_AFHDS3_ONE_TO_MANY currentLangStrings->STR_AFHDS3_ONE_TO_MANY diff --git a/radio/src/translations/string_list.h b/radio/src/translations/string_list.h index 57dd7395f..bbe7d4f1e 100644 --- a/radio/src/translations/string_list.h +++ b/radio/src/translations/string_list.h @@ -329,7 +329,7 @@ STR(STRENGTH) STR(MENUCUSTOMSCRIPTS) #endif -#if defined(DSM2) || defined(PXX) +#if defined(DSM2) || defined(PXX) || defined(DSMP) STR(BIND_OK) STR(REBIND) STR(RECEIVER_NUM) @@ -337,6 +337,10 @@ STR(RECEIVER) STR(REG_OK) #endif +#if defined(DSMP) +STR(DSMP_ENABLE_AETR) +#endif + #if defined(AFHDS3) STR(AFHDS3_ACTUAL_POWER) STR(AFHDS3_ONE_TO_MANY)