From ba906e1d5d869bf0681a270f7e0b958480c9e265 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 5 Sep 2021 19:24:55 +0200 Subject: [PATCH 1/4] [color] Dynamic MPM proto list --- radio/src/gui/colorlcd/CMakeLists.txt | 4 + radio/src/gui/colorlcd/model_setup.cpp | 189 ++++-------- radio/src/gui/colorlcd/multi_rfprotos.cpp | 130 ++++++++ radio/src/gui/colorlcd/multi_rfprotos.h | 42 +++ radio/src/gui/gui_common.cpp | 4 +- radio/src/gui/gui_common.h | 9 +- radio/src/io/multi_protolist.cpp | 343 ++++++++++++++++++++++ radio/src/io/multi_protolist.h | 88 ++++++ radio/src/pulses/multi.cpp | 51 +++- radio/src/pulses/pulses.cpp | 7 + radio/src/storage/storage_common.cpp | 11 +- radio/src/targets/horus/CMakeLists.txt | 10 +- radio/src/telemetry/multi.cpp | 48 ++- radio/src/telemetry/multi.h | 10 + radio/src/telemetry/telemetry.cpp | 14 + 15 files changed, 795 insertions(+), 165 deletions(-) create mode 100644 radio/src/gui/colorlcd/multi_rfprotos.cpp create mode 100644 radio/src/gui/colorlcd/multi_rfprotos.h create mode 100644 radio/src/io/multi_protolist.cpp create mode 100644 radio/src/io/multi_protolist.h diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index edf9ca05b..339bd39b7 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -93,6 +93,10 @@ if(PXX2 OR MULTIMODULE) add_gui_src(radio_spectrum_analyser.cpp) endif() +if(MULTIMODULE) + add_gui_src(multi_rfprotos.cpp) +endif() + if(GHOST) add_gui_src(radio_ghost_module_config.cpp) endif() diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index a5aea3cf7..dade2057b 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -19,6 +19,9 @@ */ #include "model_setup.h" +#include "multi_rfprotos.h" +#include "io/multi_protolist.h" + #include "opentx.h" #include "libopenui.h" #include "storage/modelslist.h" @@ -26,87 +29,6 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -class RFProtocols -{ -private: - const char * protocols; - int maxProtocols; - std::vector labels; - uint8_t getProtoStringLength(); - -public: - RFProtocols(const char * rfProtocols, int max); - int findProtocolIndex(int rfProtcolIndex); - int lookupProtocolIndex(int selectedIndex); - std::vector &getLabels(); -}; - -uint8_t RFProtocols::getProtoStringLength() -{ - return protocols[0]; -} - -RFProtocols::RFProtocols(const char *protocols, int max) -{ - this->protocols = protocols; - maxProtocols = max; - - uint8_t len = protocols[0]; - const char * value = &protocols[1]; - - for (int i = 0; i <= max; i++) - { - std::string label = std::string(value, min(len, strlen(value))); - labels.emplace_back(label); - - value += len; - } - std::sort(labels.begin(), labels.end()); -} - -std::vector &RFProtocols::getLabels() -{ - return labels; -} - -int RFProtocols::lookupProtocolIndex(int selectedIndex) -{ - uint8_t len = getProtoStringLength(); - const char *p = &protocols[1]; - - const char *protocolLabel = labels.at(selectedIndex).c_str(); - - int protocolIndex = 0; - for (; protocolIndex < maxProtocols; protocolIndex++) - { - if (strncmp(p, protocolLabel, min((int)strlen(protocolLabel), (int)len)) == 0) - break; - - p += len; - } - assert(protocolIndex < maxProtocols); - - return protocolIndex; -} - -int RFProtocols::findProtocolIndex(int rfProtocolIndex) -{ - uint8_t len = getProtoStringLength(); - const char *protocolLabel = &protocols[1] + len * rfProtocolIndex; - - auto result = std::find_if(labels.begin(), labels.end(), [protocolLabel, len](std::string &a) { - auto theSame = strncmp(a.c_str(), protocolLabel, max((int)a.length(), (int)len)) == 0; - return theSame; - }); - assert(result != labels.end()); - return result - labels.begin(); -} - -// global pointer intialized once on the first call to update() -RFProtocols *rfProtocols = nullptr; - - - std::string switchWarninglabel(swsrc_t index) { static const char switchPositions[] = { @@ -720,70 +642,65 @@ class ModuleWindow : public FormGroup { } #if defined(MULTIMODULE) else if (isModuleMultimodule(moduleIdx)) { - Choice * mmSubProtocol = nullptr; + Choice * mmSubProto = nullptr; grid.nextLine(); - new StaticText(this, grid.getLabelSlot(true), STR_RF_PROTOCOL, 0, COLOR_THEME_PRIMARY1); + new StaticText(this, grid.getLabelSlot(true), STR_RF_PROTOCOL, 0, + COLOR_THEME_PRIMARY1); - // Multi type (CUSTOM, brand A, brand B,...) - int multiRfProto = g_model.moduleData[moduleIdx].getMultiProtocol(); + // Grid count for narrow/wide screen + int count = + LCD_W < LCD_H + ? 1 + : (g_model.moduleData[moduleIdx].multi.customProto ? 3 : 2); - // initialize rfProtocols if it is not initialized already. This is a hack - // because i dont know how to construct it at the exact right time after the module - // is loaded - if (rfProtocols == nullptr) - rfProtocols = new RFProtocols(STR_MULTI_PROTOCOLS, MODULE_SUBTYPE_MULTI_LAST); - - multiRfProto = rfProtocols->findProtocolIndex(multiRfProto); - - // Grid count for narrow/wide screen - int count = LCD_W < LCD_H ? 1 : (g_model.moduleData[moduleIdx].multi.customProto ? 3 : 2); - - rfChoice = new Choice( - this, - grid.getFieldSlot(count, 0), - rfProtocols->getLabels(), MODULE_SUBTYPE_MULTI_FIRST, - MODULE_SUBTYPE_MULTI_LAST, GET_DEFAULT(multiRfProto), + rfChoice = new MultiProtoChoice( + this, grid.getFieldSlot(count, 0), moduleIdx, [=](int32_t newValue) { - newValue = rfProtocols->lookupProtocolIndex(newValue); - g_model.moduleData[moduleIdx].setMultiProtocol(newValue); g_model.moduleData[moduleIdx].subType = 0; - if (mmSubProtocol != nullptr) mmSubProtocol->invalidate(); // always null ??? resetMultiProtocolsOptions(moduleIdx); + + MultiModuleStatus& status = getMultiModuleStatus(moduleIdx); + status.invalidate(); + + uint32_t startUpdate = RTOS_GET_MS(); + while(!status.isValid() && (RTOS_GET_MS() - startUpdate < 250)); + SET_DIRTY(); update(); - rfChoice->setFocus(SET_FOCUS_DEFAULT);// really valid ??? - }); - if (MULTIMODULE_HAS_SUBTYPE(moduleIdx)) { + if (rfChoice) + rfChoice->setFocus(SET_FOCUS_DEFAULT); + }, + [=]() { update(); }); + + auto *rfProto = + MultiRfProtocols::instance(moduleIdx)->getProto( + g_model.moduleData[moduleIdx].getMultiProtocol()); + + if (rfProto && !rfProto->subProtos.empty()) { // Subtype (D16, DSMX,...) - - // Grid count for narrow/wide screen - count = LCD_W < LCD_H ? 1 : 2; - int index = 1; - if (count == 1) - { - grid.nextLine(); - index = 0; - } - const mm_protocol_definition *pdef = getMultiProtocolDefinition( - g_model.moduleData[moduleIdx].getMultiProtocol()); - if (pdef->maxSubtype > 0) { - mmSubProtocol = new Choice( - this, grid.getFieldSlot(count, index), pdef->subTypeString, 0, - pdef->maxSubtype, + + // Grid count for narrow/wide screen + count = LCD_W < LCD_H ? 1 : 2; + int index = 1; + if (count == 1) { + grid.nextLine(); + index = 0; + } + + mmSubProto = new Choice( + this, grid.getFieldSlot(count, index), + rfProto->subProtos, 0, rfProto->subProtos.size() - 1, [=]() { return g_model.moduleData[moduleIdx].subType; }, [=](int16_t newValue) { g_model.moduleData[moduleIdx].subType = newValue; resetMultiProtocolsOptions(moduleIdx); SET_DIRTY(); - // update(); ??? how to set focus properly ??? update(); - //mmSubProtocol->setFocus(SET_FOCUS_DEFAULT); + if (mmSubProto != nullptr) mmSubProto->setFocus(SET_FOCUS_DEFAULT); }); - - } } grid.nextLine(); @@ -795,24 +712,14 @@ class ModuleWindow : public FormGroup { return std::string(msg); }, COLOR_THEME_PRIMARY1); - // Multimodule sync - /*if (multiSyncStatus.isValid()) { - grid.nextLine(); - new StaticText(this, grid.getLabelSlot(true), STR_MODULE_SYNC, 0, COLOR_THEME_PRIMARY1); - multiSyncStatus.getRefreshString(statusText); - new StaticText(this, grid.getFieldSlot(), statusText, 0, COLOR_THEME_PRIMARY1); - }*/ - const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); - if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { + if (rfProto) { // Multi optional feature row - const char *title = getMultiOptionTitleStatic(moduleIdx); + const char *title = rfProto->getOptionStr(); if (title != nullptr) { grid.nextLine(); new StaticText(this, grid.getLabelSlot(true), title, 0, COLOR_THEME_PRIMARY1); - // int optionValue = - // g_model.moduleData[moduleIdx].multi.optionValue; int8_t min, max; getMultiOptionValues(multi_proto, min, max); @@ -912,7 +819,7 @@ class ModuleWindow : public FormGroup { GET_SET_DEFAULT( g_model.moduleData[moduleIdx].multi.disableTelemetry)); - if (MULTI_DISABLE_CHAN_MAP_ROW_STATIC(moduleIdx) != HIDDEN_ROW) { + if (rfProto && rfProto->supportsDisableMapping()) { // Disable channel mapping grid.nextLine(); new StaticText(this, grid.getLabelSlot(true), STR_DISABLE_CH_MAP, 0, COLOR_THEME_PRIMARY1); @@ -1192,7 +1099,9 @@ class ModuleWindow : public FormGroup { void checkEvents() override { - if (isModuleFailsafeAvailable(moduleIdx) != hasFailsafe) { + if (isModuleFailsafeAvailable(moduleIdx) != hasFailsafe + && rfChoice && !rfChoice->isEditMode()) { + hasFailsafe = isModuleFailsafeAvailable(moduleIdx); update(); } diff --git a/radio/src/gui/colorlcd/multi_rfprotos.cpp b/radio/src/gui/colorlcd/multi_rfprotos.cpp new file mode 100644 index 000000000..c34756f76 --- /dev/null +++ b/radio/src/gui/colorlcd/multi_rfprotos.cpp @@ -0,0 +1,130 @@ +/* + * 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 "io/multi_protolist.h" +#include "multi_rfprotos.h" +#include "strhelpers.h" + +#include "progress.h" + +// TODO: format for LCD_WIDTH > LCD_HEIGHT +constexpr rect_t RFSCAN_DIALOG_RECT = { + 50, 100, LCD_W - 100, LCD_H - 200 +}; + +// TODO: translation +const char* RFSCAN_TITLE = "Scanning protocols"; + +class RfScanDialog : public Dialog +{ + MultiRfProtocols* protos; + uint32_t lastUpdate = 0; + + Progress* progress; + std::function onClose; + + public: + RfScanDialog(Window* parent, MultiRfProtocols* protos, + std::function onClose) : + Dialog(parent, RFSCAN_TITLE, RFSCAN_DIALOG_RECT), + protos(protos), + progress(new Progress(&content->form, + {PAGE_PADDING, PAGE_PADDING, + content->form.width() - 2 * PAGE_PADDING, + content->form.height() - 2 * PAGE_PADDING})), + onClose(std::move(onClose)) + { + // disable canceling dialog + setCloseWhenClickOutside(false); + setFocus(); + } + + void showProgress() + { + progress->setValue((int)(protos->getProgress() * 100.0)); + } + + // disable keys + void onEvent(event_t) override { return; } + + void checkEvents() override + { + if (!protos->isScanning()) { + deleteLater(); + onClose(); + } else if (RTOS_GET_MS() - lastUpdate >= 200) { + showProgress(); + lastUpdate = RTOS_GET_MS(); + } + + Dialog::checkEvents(); + } +}; + +MultiProtoChoice::MultiProtoChoice(FormGroup* parent, const rect_t& rect, + unsigned int moduleIdx, + std::function setValue, + std::function updateForm) : + Choice( + parent, rect, 0, 0, + [=] { return g_model.moduleData[moduleIdx].getMultiProtocol(); }, + setValue), + moduleIdx(moduleIdx) +{ + TRACE("MultiProtoChoice::MultiProtoChoice(%p)", this); + protos = MultiRfProtocols::instance(moduleIdx); + protos->triggerScan(); + + if (protos->isScanning()) { + new RfScanDialog(parent, protos, std::move(updateForm)); + } else { + TRACE("!protos->isScanning()"); + } + + setTextHandler([=](int value) { + return protos->getProtoLabel(value); + }); +} + +void MultiProtoChoice::openMenu() +{ + menu = new Menu(this); + + if (!menuTitle.empty()) menu->setTitle(menuTitle); + menu->setCloseHandler([=]() { setEditMode(false); }); + + setEditMode(true); + invalidate(); + + protos->fillList([=](const MultiRfProtocols::RfProto& p){ + addProto(p.proto, p.label.c_str()); + }); + + int idx = protos->getIndex(g_model.moduleData[moduleIdx].getMultiProtocol()); + if (idx >= 0) menu->select(idx); +} + +void MultiProtoChoice::addProto(unsigned int proto, const char* protocolName) +{ + addValue(protocolName); + menu->addLine(protocolName, [=]() { setValue(proto); }); +} + diff --git a/radio/src/gui/colorlcd/multi_rfprotos.h b/radio/src/gui/colorlcd/multi_rfprotos.h new file mode 100644 index 000000000..2afc43590 --- /dev/null +++ b/radio/src/gui/colorlcd/multi_rfprotos.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 "libopenui.h" + +class MultiRfProtocols; + +class MultiProtoChoice : public Choice +{ + unsigned int moduleIdx; + + MultiRfProtocols* protos = nullptr; + Menu* menu = nullptr; + + public: + MultiProtoChoice(FormGroup *parent, const rect_t &rect, unsigned int moduleIdx, + std::function setValue, std::function updateForm); + + void openMenu() override; + void addProto(unsigned int proto, const char* protocolName); + +}; + diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 969e0e623..47bb95fc9 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -952,7 +952,7 @@ const char STR_SUBTYPE_HUBSAN[] = "\004""H107""H301""H501"; const char STR_SUBTYPE_FRSKY[] = "\011""D16\0 ""D8\0 ""D16 8ch\0 ""V8\0 ""LBT(EU)\0 ""LBT 8ch\0 ""D8Cloned\0""D16Cloned"; const char STR_SUBTYPE_HISKY[] = "\005""Std\0 ""HK310"; const char STR_SUBTYPE_V2X2[] = "\006""Std\0 ""JXD506""MR101\0"; -const char STR_SUBTYPE_DSM[] = "\004""2 1F""2 2F""X 1F""X 2F"; +const char STR_SUBTYPE_DSM[] = "\004""2 1F""2 2F""X 1F""X 2F""Auto""R 1F"; const char STR_SUBTYPE_DEVO[] = "\004""8ch\0""10ch""12ch""6ch\0""7ch\0"; const char STR_SUBTYPE_YD717[] = "\007""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 "; const char STR_SUBTYPE_KN[] = "\006""WLtoys""FeiLun"; @@ -1031,7 +1031,7 @@ const mm_protocol_definition multi_protocols[] = { {MODULE_SUBTYPE_MULTI_FRSKY, 7, false, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_HISKY, 1, false, true, STR_SUBTYPE_HISKY, nullptr}, {MODULE_SUBTYPE_MULTI_V2X2, 2, false, false, STR_SUBTYPE_V2X2, nullptr}, - {MODULE_SUBTYPE_MULTI_DSM2, 3, false, true, STR_SUBTYPE_DSM, STR_MULTI_MAX_THROW}, + {MODULE_SUBTYPE_MULTI_DSM2, 5, false, true, STR_SUBTYPE_DSM, STR_MULTI_MAX_THROW}, {MODULE_SUBTYPE_MULTI_DEVO, 4, false, true, STR_SUBTYPE_DEVO, STR_MULTI_FIXEDID}, {MODULE_SUBTYPE_MULTI_YD717, 4, false, false, STR_SUBTYPE_YD717, nullptr}, {MODULE_SUBTYPE_MULTI_KN, 1, false, false, STR_SUBTYPE_KN, nullptr}, diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index e59b5df57..ab9533f2b 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -274,21 +274,24 @@ inline bool MULTIMODULE_PROTOCOL_KNOWN(uint8_t moduleIdx) inline bool MULTIMODULE_HAS_SUBTYPE(uint8_t moduleIdx) { MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); + int proto = g_model.moduleData[moduleIdx].getMultiProtocol(); - if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY) { + if (proto == MODULE_SUBTYPE_MULTI_FRSKY) { return true; } if (status.isValid()) { + TRACE("(%d) status.protocolSubNbr = %d", proto, status.protocolSubNbr); return status.protocolSubNbr > 0; } else { - if (g_model.moduleData[moduleIdx].getMultiProtocol() > MODULE_SUBTYPE_MULTI_LAST) { + if (proto > MODULE_SUBTYPE_MULTI_LAST) { return true; } else { - return getMultiProtocolDefinition(g_model.moduleData[moduleIdx].getMultiProtocol())->subTypeString != nullptr; + auto subProto = getMultiProtocolDefinition(proto); + return subProto->subTypeString != nullptr; } } } diff --git a/radio/src/io/multi_protolist.cpp b/radio/src/io/multi_protolist.cpp new file mode 100644 index 000000000..830f86eb3 --- /dev/null +++ b/radio/src/io/multi_protolist.cpp @@ -0,0 +1,343 @@ +/* + * 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 "multi_protolist.h" +#include "multi_rfprotos.h" +#include "strhelpers.h" +#include "audio.h" +#include "translations.h" +#include "pulses/modules_helpers.h" +#include "pulses/pulses.h" +#include "gui_common.h" + +#include "opentx.h" // reusableBuffer + +#include + +MultiRfProtocols* MultiRfProtocols::_instance[NUM_MODULES] = {}; + +// MPM telemetry packet format type = MultiProtoDef (0x11) +// +// data[0] = protocol number, 0xFF is an invalid list entry +// (Option value too large) and nothing sent after +// data[1..n] = protocol name null terminated +// data[n+1] = flags +// flags>>4 Option text number to be displayed +// (check multi status for description) +// flags&0x01 failsafe supported +// flags&0x02 Channel Map Disabled supported +// data[n+2] = number of sub protocols +// data[n+3] = sub protocols text length, only sent if nbr_sub != 0 +// data[n+4..] = sub protocol names, only sent if nbr_sub != 0 + +constexpr uint8_t MULTI_INVALID_PROTO = 0xFF; +constexpr uint32_t MULTI_PROTOLIST_TIMEOUT = 100; // 100ms +constexpr uint32_t MULTI_PROTOLIST_START_TIMEOUT = 3000; // 3s + +bool MultiRfProtocols::RfProto::parse(const uint8_t* data, uint8_t len) +{ + const char* s = (const char*)data; + + uint8_t subProtoNr = 0; + uint8_t subProtoLen = 0; + + // special case handling for Frsky protos + if (proto == MODULE_SUBTYPE_MULTI_FRSKY) { + const mm_protocol_definition* def = getMultiProtocolDefinition(proto); + if (!def) return false; + + label = "Frsky"; + flags = 0x20; + subProtoNr = def->maxSubtype + 1; + subProtoLen = def->subTypeString[0]; + s = def->subTypeString + 1; + + } else { + // proto label string + while (*s && len--) s++; + if (*s || !len) return false; + label = (const char*)data; + s++; + len--; + + // flags, subProtoNr, subProto label max length + if (len < 2) return false; + flags = (uint8_t) * (s++); + //TRACE("flags: 0x%02X", flags); + + subProtoNr = (uint8_t) * (s++); + len -= 2; + + if (!len) return true; + subProtoLen = (uint8_t) * (s++); + len--; + + if (len < subProtoNr * subProtoLen) return false; + } + + fillSubProtoList(s, subProtoNr, subProtoLen); + return true; +} + +void MultiRfProtocols::RfProto::fillSubProtoList(const char* str, int n, int len) +{ + char tmp[len + 1]; + subProtos.reserve(n); + + for (int i = 0; i < n; i++, str += len) { + strncpy(tmp, str, len); + tmp[len] = '\0'; + subProtos.emplace_back((const char*)tmp); + //TRACE("%s: '%s'", label.c_str(), subProtos.back().c_str()); + } +} + +uint8_t MultiRfProtocols::RfProto::getOption() const +{ + uint8_t opt = flags >> 4; + if (opt >= getMaxMultiOptions()) { + opt = 1; // Unknown options are defaulted to type 1 (basic option) + } + return opt; +} + +const char* MultiRfProtocols::RfProto::getOptionStr() const +{ + return mm_options_strings::options[getOption()]; +} + +MultiRfProtocols* MultiRfProtocols::instance(unsigned int moduleIdx) +{ + if (moduleIdx >= NUM_MODULES) return nullptr; + if (!_instance[moduleIdx]) { + _instance[moduleIdx] = new MultiRfProtocols(moduleIdx); + } + return _instance[moduleIdx]; +} + +void MultiRfProtocols::removeInstance(unsigned int moduleIdx) +{ + delete _instance[moduleIdx]; + _instance[moduleIdx] = nullptr; +} + +int MultiRfProtocols::getIndex(unsigned int proto) const +{ + auto it = proto2idx.find(proto); + return it != proto2idx.end() ? it->second : -1; +} + +const MultiRfProtocols::RfProto* MultiRfProtocols::getProto(unsigned int proto) const +{ + int idx = getIndex(proto); + if (idx >= 0 && (unsigned)idx < protoList.size()) + return &protoList[idx]; + + return nullptr; +} + +std::string MultiRfProtocols::getProtoLabel(unsigned int proto) const +{ + if (scanState != ScanEnd) { + MultiModuleStatus& status = getMultiModuleStatus(moduleIdx); + if (status.protocolName[0] && status.isValid()) { + return std::string(status.protocolName); + } else if (proto <= MODULE_SUBTYPE_MULTI_LAST) { + char tmp[8]; + getStringAtIndex(tmp, STR_MULTI_PROTOCOLS, proto); + return std::string(tmp); + } + } else { + int idx = getIndex(proto); + if (idx >= 0 && (unsigned)idx < protoList.size()) { + return protoList[idx].label; + } + } + return std::string(); +} + +std::string MultiRfProtocols::getLastProtoLabel() const +{ + if (!protoList.empty()) + return protoList.back().label; + + return std::string(); +} + +float MultiRfProtocols::getProgress() const +{ + constexpr float WAIT_TIME_RATIO = 0.7; // wait time accounts for 70% + constexpr float PROTOS_RATIO = 1.0 - WAIT_TIME_RATIO; + + if (scanState == ScanStop) return 0.0; + if (scanState == ScanBegin) { + float t = (float)(RTOS_GET_MS() - scanStart) / (float)MULTI_PROTOLIST_START_TIMEOUT; + return t * WAIT_TIME_RATIO; + } + + return WAIT_TIME_RATIO + ((float)getNProtos() / (float)totalProtos) * PROTOS_RATIO; +} + +void MultiRfProtocols::fillList(std::function addProto) const +{ + for (const auto& p : protoList) { + addProto(p); + } +} + +bool MultiRfProtocols::triggerScan() +{ + if (scanState == ScanStop) { + proto2idx.clear(); + protoList.clear(); + scanState = ScanBegin; + moduleState[moduleIdx].mode = MODULE_MODE_GET_HARDWARE_INFO; + moduleState[moduleIdx].counter = MULTI_INVALID_PROTO; + scanStart = lastScan = RTOS_GET_MS(); + return true; + } + + return false; +} + +bool MultiRfProtocols::scanReply(const uint8_t* packet, uint8_t len) +{ + switch (scanState) { + case ScanBegin: + case Scanning: + if (packet && len) { + uint8_t replyProtoId = *packet; + len--; + packet++; + + // new status received + if (replyProtoId != MULTI_INVALID_PROTO) { + if (moduleState[moduleIdx].counter == MULTI_INVALID_PROTO) { + //TRACE("# of protos: %d", totalProtos); + totalProtos = replyProtoId; + scanState = Scanning; + protoList.reserve(totalProtos); + } else { + //TRACE("Proto = %d; label = '%s'", replyProtoId, + // (const char*)packet); + + int proto = convertMultiToOtx(replyProtoId); + if (proto != MODULE_SUBTYPE_MULTI_CONFIG && + proto != MODULE_SUBTYPE_MULTI_SCANNER) { + bool insertProto = true; + if (proto == MODULE_SUBTYPE_MULTI_FRSKY) { + auto it = std::find_if(protoList.begin(), protoList.end(), + [=](const RfProto& p) { + return p.proto == (const int)proto; + }); + if (it != protoList.end()) { + // FRSKY proto already added + insertProto = false; + } + } + + if (insertProto) { + RfProto rfProto(proto); + if (rfProto.parse(packet, len)) { + proto2idx[proto] = protoList.size(); + protoList.emplace_back(rfProto); + } else { + TRACE("Error parsing proto [%d]", proto); + } + } + } else { + // do not count excluded protocols + totalProtos--; + } + } + + moduleState[moduleIdx].counter++; + lastScan = RTOS_GET_MS(); + return true; + + } else { + scanState = ScanEnd; + setModuleMode(moduleIdx, MODULE_MODE_NORMAL); + break; + } + } else { + uint32_t timeout = MULTI_PROTOLIST_TIMEOUT; + + if (scanState == ScanBegin) { + // Timeout = 3s solely because of the very slow start time of the + // MPM... + timeout = MULTI_PROTOLIST_START_TIMEOUT; + } + + if (RTOS_GET_MS() - lastScan >= timeout) { + TRACE("proto scan timeout!"); + scanState = ScanInvalid; + } + } + break; + + case ScanInvalid: { + // TODO: fill with static list (sorted) + const mm_protocol_definition* pdef = + getMultiProtocolDefinition(MODULE_SUBTYPE_MULTI_FIRST); + + // build the list of static protos + protoList.clear(); + protoList.reserve(MODULE_SUBTYPE_MULTI_LAST - MODULE_SUBTYPE_MULTI_FIRST + 1); + for (; pdef->protocol != 0xfe; pdef++) { + RfProto rfProto(pdef->protocol); + + char tmp[8]; + rfProto.label = + getStringAtIndex(tmp, STR_MULTI_PROTOCOLS, pdef->protocol); + rfProto.flags = + (pdef->failsafe ? 0x01 : 0) | (pdef->disable_ch_mapping ? 0x02 : 0); + + if (pdef->subTypeString) { + int st_len = pdef->subTypeString[0]; + const char* s = pdef->subTypeString + 1; + + rfProto.fillSubProtoList(s, pdef->maxSubtype + 1, st_len); + } + + protoList.emplace_back(rfProto); + } + + // sort it by label + std::sort( + protoList.begin(), protoList.end(), + [](const RfProto& a, const RfProto& b) { return a.label < b.label; }); + + // index the sorted list + proto2idx.clear(); + for (unsigned int i = 0; i < protoList.size(); i++) + proto2idx[protoList[i].proto] = i; + + scanState = ScanEnd; + setModuleMode(moduleIdx, MODULE_MODE_NORMAL); + } break; + + default: + break; + } + + return false; +} diff --git a/radio/src/io/multi_protolist.h b/radio/src/io/multi_protolist.h new file mode 100644 index 000000000..513f1a2ee --- /dev/null +++ b/radio/src/io/multi_protolist.h @@ -0,0 +1,88 @@ +/* + * 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 "dataconstants.h" +#include "opentx_types.h" + +#include +#include +#include +#include + +class MultiRfProtocols +{ + static MultiRfProtocols* _instance[NUM_MODULES]; + + unsigned int moduleIdx; + + enum ScanState { ScanStop, ScanBegin, Scanning, ScanInvalid, ScanEnd }; + + ScanState scanState = ScanStop; + + uint32_t scanStart = 0; + uint32_t lastScan = 0; + uint8_t totalProtos = 0; + + MultiRfProtocols(unsigned int moduleIdx) : moduleIdx(moduleIdx) {} + + public: + + struct RfProto { + int proto; + std::string label; + + uint8_t flags = 0; + std::vector subProtos; + + RfProto(int proto) : proto(proto) {} + + bool parse(const uint8_t* data, uint8_t len); + void fillSubProtoList(const char* str, int n, int len); + + uint8_t getOption() const; + const char* getOptionStr() const; + + bool supportsFailsafe() const { return flags & 0x01; } + bool supportsDisableMapping() const { return flags & 0x02; } + }; + + static MultiRfProtocols* instance(unsigned int moduleIdx); + static void removeInstance(unsigned int moduleIdx); + + int getIndex(unsigned int proto) const; + const RfProto* getProto(unsigned int proto) const; + std::string getProtoLabel(unsigned int proto) const; + + bool isScanning() const { return scanState != ScanStop && scanState != ScanEnd; } + int getNProtos() const { return protoList.size(); } + int getTotalProtos() const { return totalProtos; } + float getProgress() const; + + std::string getLastProtoLabel() const; + bool triggerScan(); + + bool scanReply(const uint8_t * packet = nullptr, uint8_t len = 0); + void fillList(std::function addProto) const; + + private: + std::vector protoList; + std::map proto2idx; +}; diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index 1e4c4fcd9..cf87088be 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -21,6 +21,9 @@ #include "opentx.h" #include "multi.h" +#include "io/multi_protolist.h" +#include "telemetry/multi.h" + // for the MULTI protocol definition // see https://github.com/pascallanger/DIY-Multiprotocol-TX-Module // file Multiprotocol/multiprotocol.h @@ -142,25 +145,30 @@ void setupPulsesMulti(uint8_t moduleIdx) }; uint8_t type=MULTI_NORMAL; - // Failsafe packets - if (counter[moduleIdx] % 1000 == 0 && g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_NOT_SET && g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_RECEIVER) { - type|=MULTI_FAILSAFE; + // not scanning protos && not spectrum analyser + if (getModuleMode(moduleIdx) == MODULE_MODE_NORMAL) { + // Failsafe packets + if (counter[moduleIdx] % 1000 == 0 && + g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_NOT_SET && + g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_RECEIVER) { + type |= MULTI_FAILSAFE; + } + + counter[moduleIdx]++; } // Invert telemetry if needed - if (invert[moduleIdx] & 0x80 && !g_model.moduleData[moduleIdx].multi.disableTelemetry) { + if (invert[moduleIdx] & 0x80 && + !g_model.moduleData[moduleIdx].multi.disableTelemetry) { if (getMultiModuleStatus(moduleIdx).isValid()) { - invert[moduleIdx] &= 0x08; // Telemetry received, stop searching - } - else if (counter[moduleIdx] % 100 == 0) { + invert[moduleIdx] &= 0x08; // Telemetry received, stop searching + } else if (counter[moduleIdx] % 100 == 0) { invert[moduleIdx] ^= 0x08; // Try inverting telemetry } } - counter[moduleIdx]++; - // Send header - sendFrameProtocolHeader(moduleIdx, type&MULTI_FAILSAFE); + sendFrameProtocolHeader(moduleIdx, type & MULTI_FAILSAFE); // Send channels if (type & MULTI_FAILSAFE) @@ -169,7 +177,8 @@ void setupPulsesMulti(uint8_t moduleIdx) sendChannels(moduleIdx); // Multi V1.3.X.X -> Send byte 26, Protocol (bits 7 & 6), RX_Num (bits 5 & 4), invert, not used, disable telemetry, disable mapping - if (moduleState[moduleIdx].mode == MODULE_MODE_SPECTRUM_ANALYSER) { + if ((moduleState[moduleIdx].mode == MODULE_MODE_SPECTRUM_ANALYSER) || + (moduleState[moduleIdx].mode == MODULE_MODE_GET_HARDWARE_INFO)) { sendMulti(moduleIdx, invert[moduleIdx] & 0x08); } else { @@ -353,7 +362,8 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe) uint8_t protoByte = 0; - if (moduleState[moduleIdx].mode == MODULE_MODE_SPECTRUM_ANALYSER) { + uint8_t moduleMode = getModuleMode(moduleIdx); + if (moduleMode == MODULE_MODE_SPECTRUM_ANALYSER) { sendMulti(moduleIdx, (uint8_t) 0x54); // Header byte sendMulti(moduleIdx, (uint8_t) 54); // Spectrum custom protocol sendMulti(moduleIdx, (uint8_t) 0); @@ -361,9 +371,21 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe) return; } - if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) + if (moduleMode == MODULE_MODE_GET_HARDWARE_INFO) { + sendMulti(moduleIdx, (uint8_t) 0x55); // Header byte + sendMulti(moduleIdx, (uint8_t) 0); // PROTOLIST custom protocol + sendMulti(moduleIdx, (uint8_t) 0); + + // proto array item + uint8_t protoIdx = moduleState[moduleIdx].counter; + TRACE("scan [%d]", protoIdx); + sendMulti(moduleIdx, protoIdx); + return; + } + + if (moduleMode == MODULE_MODE_BIND) protoByte |= MULTI_SEND_BIND; - else if (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK) + else if (moduleMode == MODULE_MODE_RANGECHECK) protoByte |= MULTI_SEND_RANGECHECK; // rfProtocol @@ -483,3 +505,4 @@ void sendDSM(uint8_t moduleIdx) } } #endif + diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 9086367ad..9120a10a7 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -22,6 +22,7 @@ #include "io/frsky_pxx2.h" #include "pulses/pxx2.h" #include "mixer_scheduler.h" +#include "io/multi_protolist.h" uint8_t s_pulses_paused = 0; ModuleState moduleState[NUM_MODULES]; @@ -291,6 +292,9 @@ void enablePulsesExternalModule(uint8_t protocol) #endif getMultiModuleStatus(EXTERNAL_MODULE).failsafeChecked = false; getMultiModuleStatus(EXTERNAL_MODULE).flags = 0; +#if defined(MULTI_PROTOLIST) + MultiRfProtocols::instance(EXTERNAL_MODULE)->triggerScan(); +#endif break; #endif @@ -468,6 +472,9 @@ static void enablePulsesInternalModule(uint8_t protocol) mixerSchedulerSetPeriod(INTERNAL_MODULE, MULTIMODULE_PERIOD); getMultiModuleStatus(INTERNAL_MODULE).failsafeChecked = false; getMultiModuleStatus(INTERNAL_MODULE).flags = 0; +#if defined(MULTI_PROTOLIST) + MultiRfProtocols::instance(EXTERNAL_MODULE)->triggerScan(); +#endif break; #endif diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index 0a7c81564..4d5591723 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -19,7 +19,13 @@ */ #include "opentx.h" -#include "pulses/multi.h" + +#if defined(MULTIMODULE) + #include "pulses/multi.h" + #if defined(MULTI_PROTOLIST) + #include "io/multi_protolist.h" + #endif +#endif uint8_t storageDirtyMsk; tmr10ms_t storageDirtyTime10ms; @@ -159,6 +165,9 @@ void postModelLoad(bool alarms) #if defined(MULTIMODULE) else if (isModuleMultimodule(EXTERNAL_MODULE)) multiPatchCustom(EXTERNAL_MODULE); +#if defined(MULTI_PROTOLIST) + MultiRfProtocols::removeInstance(EXTERNAL_MODULE); +#endif #endif AUDIO_FLUSH(); diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index eba9b5f26..bed685235 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -303,12 +303,18 @@ if(BOOTLOADER) ) endif() -set(SRC - ${SRC} +set(SRC ${SRC} io/frsky_firmware_update.cpp io/multi_firmware_update.cpp ) +if (MULTIMODULE) + add_definitions(-DMULTI_PROTOLIST) + set(SRC ${SRC} + io/multi_protolist.cpp + ) +endif() + set(STM32LIB_SRC STM32F4xx_StdPeriph_Driver/src/stm32f4xx_sdio.c STM32F4xx_StdPeriph_Driver/src/stm32f4xx_fmc.c diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 8eb7e44b2..5d56edc76 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -20,8 +20,9 @@ #include "opentx.h" #include "telemetry.h" #include "multi.h" +#include "io/multi_protolist.h" -constexpr int32_t MULTI_DESIRED_VERSION = (1 << 24) | (3 << 16) | (1 << 8) | 69; +constexpr int32_t MULTI_DESIRED_VERSION = (1 << 24) | (3 << 16) | (3 << 8) | 0; #define MULTI_CHAN_BITS 11 extern uint8_t g_moduleIdx; @@ -43,7 +44,8 @@ enum MultiPacketTypes : uint8_t MultiRxChannels, HottTelemetry, MLinkTelemetry, - ConfigTelemetry + ConfigTelemetry, + MultiProtoDef }; enum MultiBufferState : uint8_t @@ -144,6 +146,10 @@ uint16_t& getMultiTelemetryLastRxTS(uint8_t module) #endif // INTERNAL_MODULE_MULTI +bool isMultiModeScanning(uint8_t module) +{ + return getModuleMode(module) == MODULE_MODE_GET_HARDWARE_INFO; +} static MultiBufferState guessProtocol(uint8_t module) { @@ -207,7 +213,6 @@ static void processMultiStatusPacket(const uint8_t * data, uint8_t module, uint8 // At least two status packets without bind flag bool wasBinding = status.isBinding(); - status.lastUpdate = get_tmr10ms(); status.flags = data[0]; status.major = data[1]; status.minor = data[2]; @@ -240,6 +245,9 @@ static void processMultiStatusPacket(const uint8_t * data, uint8_t module, uint8 if (wasBinding && !status.isBinding() && getMultiBindStatus(module) == MULTI_BIND_INITIATED) setMultiBindStatus(module, MULTI_BIND_FINISHED); + + // update timestamp last to avoid race conditions + status.lastUpdate = get_tmr10ms(); } static void processMultiSyncPacket(const uint8_t * data, uint8_t module) @@ -317,6 +325,28 @@ static void processConfigPacket(const uint8_t * packet, uint8_t len) } #endif +#if defined(MULTI_PROTOLIST) +static void processMultiProtoDef(uint8_t module, const uint8_t * packet, uint8_t len) +{ + /* + data[0] = protocol number, 0xFF is an invalid list entry + (Option value too large) and nothing sent after + data[1..n] = protocol name null terminated + data[n+1] = flags + flags>>4 Option text number to be displayed + (check multi status for description) + flags&0x01 failsafe supported + flags&0x02 Channel Map Disabled supported + data[n+2] = number of sub protocols + data[n+3] = sub protocols text length, only sent if nbr_sub != 0 + data[n+4..] = sub protocol names, only sent if nbr_sub != 0 + */ + + MultiRfProtocols::instance(module)->scanReply(packet, len); +} +#endif + + static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) { uint8_t type = packet[0]; @@ -450,6 +480,13 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) break; #endif +#if defined(MULTI_PROTOLIST) + case MultiProtoDef: + if (len >= 1) + processMultiProtoDef(module, data, len); + break; +#endif + default: TRACE("[MP] Unkown multi packet type 0x%02X, len %d", type, len); break; @@ -692,6 +729,11 @@ void processMultiTelemetryData(uint8_t data, uint8_t module) } } +bool isMultiTelemReceiving(uint8_t module) +{ + return getMultiTelemetryBufferState(module) != NoProtocolDetected; +} + void checkFailsafeMulti() { for (int i=0; iisScanning()) { + MultiRfProtocols::instance(INTERNAL_MODULE)->scanReply(); + } +#endif #endif #if defined(STM32) @@ -147,6 +153,14 @@ void telemetryWakeup() LOG_TELEMETRY_WRITE_BYTE(data); } while (telemetryGetByte(&data)); } + +#if defined(MULTI_PROTOLIST) + if (isModuleMultimodule(EXTERNAL_MODULE) && + MultiRfProtocols::instance(EXTERNAL_MODULE)->isScanning()) { + MultiRfProtocols::instance(EXTERNAL_MODULE)->scanReply(); + } +#endif + #elif defined(PCBSKY9X) if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { while (telemetrySecondPortReceive(data)) { From fb460ca4dde8eb27ce3e46e0009e18adaae79250 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Sep 2021 11:16:48 +0200 Subject: [PATCH 2/4] Fix NV14 build --- radio/src/targets/nv14/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 303995b96..cf18040ec 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -152,6 +152,13 @@ set(SRC io/multi_firmware_update.cpp ) +if (MULTIMODULE) + add_definitions(-DMULTI_PROTOLIST) + set(SRC ${SRC} + io/multi_protolist.cpp + ) +endif() + set(STM32LIB_SRC STM32F4xx_StdPeriph_Driver/src/stm32f4xx_sdio.c STM32F4xx_StdPeriph_Driver/src/stm32f4xx_fmc.c From 66066181fc6015b67bcb5d9c282edaf3f707972f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Sep 2021 11:33:03 +0200 Subject: [PATCH 3/4] Improved progress bar label --- radio/src/gui/colorlcd/multi_rfprotos.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/multi_rfprotos.cpp b/radio/src/gui/colorlcd/multi_rfprotos.cpp index c34756f76..1c5501b33 100644 --- a/radio/src/gui/colorlcd/multi_rfprotos.cpp +++ b/radio/src/gui/colorlcd/multi_rfprotos.cpp @@ -31,7 +31,7 @@ constexpr rect_t RFSCAN_DIALOG_RECT = { }; // TODO: translation -const char* RFSCAN_TITLE = "Scanning protocols"; +const char* RFSCAN_TITLE = "MPM: Scanning protocols..."; class RfScanDialog : public Dialog { From 34677d0148dd4945b3fc533b63977716d3860744 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Sep 2021 12:10:05 +0200 Subject: [PATCH 4/4] Invalidate MPM cached proto list on firmware upgrade --- radio/src/io/multi_firmware_update.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index 8e60eace2..1d60547ed 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -32,6 +32,10 @@ #include "libopenui/src/libopenui_file.h" #endif +#if defined(MULTI_PROTOLIST) + #include "io/multi_protolist.h" +#endif + #define UPDATE_MULTI_EXT_BIN ".bin" class MultiFirmwareUpdateDriver @@ -648,6 +652,9 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan #if defined(HARDWARE_INTERNAL_MODULE) if (intPwr) { +#if defined(MULTI_PROTOLIST) + MultiRfProtocols::removeInstance(INTERNAL_MODULE); +#endif INTERNAL_MODULE_ON(); setupPulsesInternalModule(); } @@ -655,6 +662,9 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan #if defined(HARDWARE_EXTERNAL_MODULE) if (extPwr) { +#if defined(MULTI_PROTOLIST) + MultiRfProtocols::removeInstance(EXTERNAL_MODULE); +#endif EXTERNAL_MODULE_ON(); setupPulsesExternalModule(); }