Merge pull request #705 from EdgeTX/mpm-proto-list2
[color] Dynamic MPM proto list
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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<std::string> labels;
|
||||
uint8_t getProtoStringLength();
|
||||
|
||||
public:
|
||||
RFProtocols(const char * rfProtocols, int max);
|
||||
int findProtocolIndex(int rfProtcolIndex);
|
||||
int lookupProtocolIndex(int selectedIndex);
|
||||
std::vector<std::string> &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<uint8_t>(len, strlen(value)));
|
||||
labels.emplace_back(label);
|
||||
|
||||
value += len;
|
||||
}
|
||||
std::sort(labels.begin(), labels.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> &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[] = {
|
||||
@@ -910,70 +832,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();
|
||||
|
||||
@@ -985,24 +902,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);
|
||||
|
||||
@@ -1102,7 +1009,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);
|
||||
@@ -1382,7 +1289,9 @@ class ModuleWindow : public FormGroup {
|
||||
|
||||
void checkEvents() override
|
||||
{
|
||||
if (isModuleFailsafeAvailable(moduleIdx) != hasFailsafe) {
|
||||
if (isModuleFailsafeAvailable(moduleIdx) != hasFailsafe
|
||||
&& rfChoice && !rfChoice->isEditMode()) {
|
||||
|
||||
hasFailsafe = isModuleFailsafeAvailable(moduleIdx);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -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 = "MPM: Scanning protocols...";
|
||||
|
||||
class RfScanDialog : public Dialog
|
||||
{
|
||||
MultiRfProtocols* protos;
|
||||
uint32_t lastUpdate = 0;
|
||||
|
||||
Progress* progress;
|
||||
std::function<void()> onClose;
|
||||
|
||||
public:
|
||||
RfScanDialog(Window* parent, MultiRfProtocols* protos,
|
||||
std::function<void()> 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<void(int)> setValue,
|
||||
std::function<void()> 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); });
|
||||
}
|
||||
|
||||
@@ -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<void(int)> setValue, std::function<void()> updateForm);
|
||||
|
||||
void openMenu() override;
|
||||
void addProto(unsigned int proto, const char* protocolName);
|
||||
|
||||
};
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 <algorithm>
|
||||
|
||||
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<void(const RfProto&)> 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;
|
||||
}
|
||||
@@ -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 <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
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<std::string> 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<void(const RfProto&)> addProto) const;
|
||||
|
||||
private:
|
||||
std::vector<RfProto> protoList;
|
||||
std::map<int,int> proto2idx;
|
||||
};
|
||||
+37
-14
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; i<NUM_MODULES; i++) {
|
||||
|
||||
@@ -125,6 +125,13 @@ struct MultiModuleStatus {
|
||||
inline bool protocolValid() const { return (bool) (flags & 0x04); }
|
||||
inline bool serialMode() const { return (bool) (flags & 0x02); }
|
||||
inline bool inputDetected() const { return (bool) (flags & 0x01); }
|
||||
|
||||
inline void invalidate() {
|
||||
lastUpdate = get_tmr10ms() - 500;
|
||||
protocolSubNbr = 0;
|
||||
protocolName[0] = '\0';
|
||||
protocolSubName[0] = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
MultiModuleStatus& getMultiModuleStatus(uint8_t module);
|
||||
@@ -138,6 +145,9 @@ enum MultiBindStatus : uint8_t {
|
||||
uint8_t getMultiBindStatus(uint8_t module);
|
||||
void setMultiBindStatus(uint8_t module, uint8_t bindStatus);
|
||||
|
||||
bool isMultiModeScanning(uint8_t module);
|
||||
bool isMultiTelemReceiving(uint8_t module);
|
||||
|
||||
void checkFailsafeMulti();
|
||||
|
||||
#endif //OPENTX_MULTI_H
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "multi.h"
|
||||
#include "pulses/afhds3.h"
|
||||
#include "mixer_scheduler.h"
|
||||
#include "io/multi_protolist.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
@@ -137,6 +138,11 @@ void telemetryWakeup()
|
||||
LOG_TELEMETRY_WRITE_BYTE(data);
|
||||
} while (intmoduleFifo.pop(data));
|
||||
}
|
||||
#if defined(MULTI_PROTOLIST)
|
||||
if (MultiRfProtocols::instance(INTERNAL_MODULE)->isScanning()) {
|
||||
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)) {
|
||||
|
||||
Reference in New Issue
Block a user