diff --git a/.gitmodules b/.gitmodules index 7458ffe95..9ce4543c5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "libopenui"] path = radio/src/thirdparty/libopenui - url = https://github.com/opentx/libopenui.git + url = https://github.com/EdgeTX/libopenui.git branch = master [submodule "libACCESS"] path = radio/src/thirdparty/libACCESS diff --git a/CMakeLists.txt b/CMakeLists.txt index 870252da4..80c365e64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "0") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) -set(SDCARD_REVISION "0036") +set(SDCARD_REVISION "0037") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) @@ -35,6 +35,7 @@ set(RADIO_SRC_DIR ${RADIO_DIRECTORY}/src) set(COMPANION_SRC_DIRECTORY ${PROJECT_SOURCE_DIR}/companion/src) set(SIMU_SRC_DIRECTORY ${COMPANION_SRC_DIRECTORY}/simulation) set(TOOLS_DIR ${PROJECT_SOURCE_DIR}/tools) +set(UTILS_DIR ${PROJECT_SOURCE_DIR}/radio/util) set(LIBOPENUI_TOOLS_DIR ${RADIO_SRC_DIR}/thirdparty/libopenui/tools) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) diff --git a/CREDITS.txt b/CREDITS.txt index 3b31f5604..dc9dc238d 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2237,3 +2237,33 @@ Glenn Noonan Michael Jakob Dean Berg David Hankins +Gary Cripps +Devin Crumb +Daniel Naude +James Miller +Pascal Cayol +eDefine +Mario Sette +Hans-Georg Böhm +Robert Oats +Helmut Suhrbier +Johannes Vorster +Deon du Rand +Stephen Hill-Harriss +Regi Pradeeswaran +Gary Busler +Stefano Boggia +Vernon Martin +Alexander W Wilson Jr +Adam Bowersock +Gary Burrows +Mark Gaskievicz +James Miller +Donald Taylor +Paul Makepeace +Thomas Fowler +Walter Szempruch +Charles Starck +Fabian Mächler +David Van Dyne +Karim el Mawardi diff --git a/cmake/Bitmaps.cmake b/cmake/Bitmaps.cmake index 39ec10a9e..48b16f5a9 100644 --- a/cmake/Bitmaps.cmake +++ b/cmake/Bitmaps.cmake @@ -40,7 +40,7 @@ macro(add_truetype_font_target name size subset effect args) add_custom_command( OUTPUT ${target}.png ${target}.specs ${target}.lbm COMMAND ${PYTHON_EXECUTABLE} ${TOOLS_DIR}/build-font-bitmap.py --subset ${subset} --size ${size} --font ${font} --output ${target} - COMMAND ${PYTHON_EXECUTABLE} ${LIBOPENUI_TOOLS_DIR}/encode-bitmap.py --format 8bits --rle --size-format 2 ${args} ${target}.png ${target}.lbm + COMMAND ${PYTHON_EXECUTABLE} ${UTILS_DIR}/bin2lbm.py ${target}.png ${target}.lbm DEPENDS ${TOOLS_DIR}/build-font-bitmap.py ${TOOLS_DIR}/charset.py ${LIBOPENUI_TOOLS_DIR}/encode-bitmap.py ${RADIO_SRC_DIR}/translations/cn.h.txt ${RADIO_SRC_DIR}/fonts/extra_${size}px.png ) add_custom_target(truetype_font_${name} DEPENDS ${target}.png ${target}.specs) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index a201d50ed..5a44d246e 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -20,6 +20,7 @@ #include "boards.h" #include "macros.h" +#include "compounditemmodels.h" // TODO remove all those constants // Update: These are now all only used within this class. @@ -374,7 +375,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case MultiposPotsPositions: return IS_HORUS_OR_TARANIS(board) ? 6 : 0; - case Switches: + case Board::Switches: if (IS_TARANIS_X9E(board)) return 18; else if (board == Board::BOARD_TARANIS_X9LITE) @@ -410,11 +411,11 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) else if (IS_HORUS_X12S(board)) return 8; else - return getCapability(board, Switches); + return getCapability(board, Board::Switches); case SwitchPositions: if (IS_HORUS_OR_TARANIS(board)) - return getCapability(board, Switches) * 3; + return getCapability(board, Board::Switches) * 3; else return 9; @@ -425,10 +426,14 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 4; case NumTrimSwitches: - return getCapability(board, NumTrims) * 2; - } + return getCapability(board, Board::NumTrims) * 2; - return 0; + case HasRTC: + return IS_STM32(board) ? true : false; + + default: + return 0; + } } QString Boards::getAxisName(int index) @@ -444,7 +449,7 @@ QString Boards::getAxisName(int index) if (index < (int)DIM(axes)) return axes[index]; else - return tr("Unknown"); + return CPN_STR_UNKNOWN_ITEM; } QString Boards::getAnalogInputName(Board::Type board, int index) @@ -595,6 +600,96 @@ QString Boards::getBoardName(Board::Type board) case BOARD_RADIOMASTER_T8: return "Radiomaster T8"; default: - return tr("Unknown"); + return CPN_STR_UNKNOWN_ITEM; } } + +// static +QString Boards::potTypeToString(int value) +{ + switch(value) { + case POT_NONE: + return tr("None"); + case POT_WITH_DETENT: + return tr("Pot with detent"); + case POT_MULTIPOS_SWITCH: + return tr("Multi pos switch"); + case POT_WITHOUT_DETENT: + return tr("Pot without detent"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + + +// static +QString Boards::sliderTypeToString(int value) +{ + switch(value) { + case SLIDER_NONE: + return tr("None"); + case SLIDER_WITH_DETENT: + return tr("Slider with detent"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString Boards::switchTypeToString(int value) +{ + switch(value) { + case SWITCH_NOT_AVAILABLE: + return tr("None"); + case SWITCH_TOGGLE: + return tr("2 Positions Toggle"); + case SWITCH_2POS: + return tr("2 Positions"); + case SWITCH_3POS: + return tr("3 Positions"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +AbstractStaticItemModel * Boards::potTypeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_BOARDS_POT_TYPE); + + for (int i = 0; i < POT_TYPE_COUNT; i++) { + mdl->appendToItemList(potTypeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * Boards::sliderTypeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_BOARDS_SLIDER_TYPE); + + for (int i = 0; i < SLIDER_TYPE_COUNT; i++) { + mdl->appendToItemList(sliderTypeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * Boards::switchTypeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_BOARDS_SWITCH_TYPE); + + for (int i = 0; i < SWITCH_TYPE_COUNT; i++) { + mdl->appendToItemList(switchTypeToString(i), i, true, 0, (i < SWITCH_3POS ? SwitchTypeFlag2Pos : SwitchTypeFlag3Pos)); + } + + mdl->loadItemList(); + return mdl; +} diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index e55aeae37..33ac887d9 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -18,13 +18,19 @@ * GNU General Public License for more details. */ -#ifndef _BOARDS_H_ -#define _BOARDS_H_ +#pragma once #include #include #include +class AbstractStaticItemModel; + +// identiying names of static abstract item models +constexpr char AIM_BOARDS_POT_TYPE[] {"boards.pottype"}; +constexpr char AIM_BOARDS_SLIDER_TYPE[] {"boards.slidertype"}; +constexpr char AIM_BOARDS_SWITCH_TYPE[] {"boards.switchtype"}; + // TODO create a Board class with all these functions namespace Board { @@ -64,13 +70,15 @@ namespace Board { POT_NONE, POT_WITH_DETENT, POT_MULTIPOS_SWITCH, - POT_WITHOUT_DETENT + POT_WITHOUT_DETENT, + POT_TYPE_COUNT }; enum SliderType { SLIDER_NONE, - SLIDER_WITH_DETENT + SLIDER_WITH_DETENT, + SLIDER_TYPE_COUNT }; enum SwitchType @@ -78,7 +86,8 @@ namespace Board { SWITCH_NOT_AVAILABLE, SWITCH_TOGGLE, SWITCH_2POS, - SWITCH_3POS + SWITCH_3POS, + SWITCH_TYPE_COUNT }; enum StickAxes { @@ -130,7 +139,8 @@ namespace Board { SwitchPositions, FactoryInstalledSwitches, NumTrims, - NumTrimSwitches + NumTrimSwitches, + HasRTC }; struct SwitchInfo @@ -149,6 +159,13 @@ namespace Board { unsigned int position; }; + enum SwitchTypeMasks { + SwitchTypeFlag2Pos = 0x01, + SwitchTypeFlag3Pos = 0x02, + SwitchTypeContext2Pos = SwitchTypeFlag2Pos, + SwitchTypeContext3Pos = SwitchTypeFlag2Pos | SwitchTypeFlag3Pos + }; + } class Boards @@ -182,6 +199,12 @@ class Boards static QString getAnalogInputName(Board::Type board, int index); static bool isBoardCompatible(Board::Type board1, Board::Type board2); static QString getBoardName(Board::Type board); + static QString potTypeToString(int value); + static QString sliderTypeToString(int value); + static QString switchTypeToString(int value); + static AbstractStaticItemModel * potTypeItemModel(); + static AbstractStaticItemModel * sliderTypeItemModel(); + static AbstractStaticItemModel * switchTypeItemModel(); protected: @@ -361,5 +384,3 @@ inline bool IS_ACCESS_RADIO(Board::Type board, const QString & id) return (IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || board == Board::BOARD_TARANIS_X9DP_2019 || board == Board::BOARD_X10_EXPRESS || IS_TARANIS_X7_ACCESS(board) || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess"))); } - -#endif // _BOARDS_H_ diff --git a/companion/src/firmwares/eeprominterface.h b/companion/src/firmwares/eeprominterface.h index 6cd62f287..00980339c 100644 --- a/companion/src/firmwares/eeprominterface.h +++ b/companion/src/firmwares/eeprominterface.h @@ -134,7 +134,6 @@ enum Capability { VirtualInputs, InputsLength, TrainerInputs, - RtcTime, SportTelemetry, LuaScripts, LuaInputsPerScript, @@ -153,7 +152,13 @@ enum Capability { HasSwitchableJack, HasSportConnector, PwrButtonPress, - Sensors + Sensors, + HasAuxSerialMode, + HasAux2SerialMode, + HasBluetooth, + HasAntennaChoice, + HasADCJitterFilter, + HasTelemetryBaudrate }; class EEPROMInterface diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index f9df851b2..d1575d3e2 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -23,6 +23,7 @@ #include "appdata.h" #include "eeprominterface.h" #include "radiodataconversionstate.h" +#include "compounditemmodels.h" const uint8_t chout_ar[] = { // First number is 0..23 -> template setup, Second is relevant channel out 1,2,3,4 , 1,2,4,3 , 1,3,2,4 , 1,3,4,2 , 1,4,2,3 , 1,4,3,2, @@ -36,7 +37,7 @@ bool GeneralSettings::switchPositionAllowedTaranis(int index) const if (index == 0) return true; - div_t qr = div(abs(index)-1, 3); + div_t qr = div(abs(index) - 1, 3); if (index < 0 && switchConfig[qr.quot] != Board::SWITCH_3POS) return false; @@ -53,14 +54,14 @@ bool GeneralSettings::switchSourceAllowedTaranis(int index) const bool GeneralSettings::isPotAvailable(int index) const { - if (index < 0 || index > getBoardCapability(getCurrentBoard(), Board::Pots)) + if (index < 0 || index > Boards::getCapability(getCurrentBoard(), Board::Pots)) return false; return potConfig[index] != Board::POT_NONE; } bool GeneralSettings::isSliderAvailable(int index) const { - if (index < 0 || index > getBoardCapability(getCurrentBoard(), Board::Sliders)) + if (index < 0 || index > Boards::getCapability(getCurrentBoard(), Board::Sliders)) return false; return sliderConfig[index] != Board::SLIDER_NONE; } @@ -71,7 +72,7 @@ GeneralSettings::GeneralSettings() contrast = 25; - for (int i=0; i < CPN_MAX_ANALOGS; ++i) { + for (int i = 0; i < CPN_MAX_ANALOGS; ++i) { calibMid[i] = 0x200; calibSpanNeg[i] = 0x180; calibSpanPos[i] = 0x180; @@ -128,7 +129,7 @@ GeneralSettings::GeneralSettings() } for (uint8_t i = 0; i < 4; i++) { - trainer.mix[i].mode = 2; // replace (:=) + trainer.mix[i].mode = TrainerMix::TRN_MIX_MODE_SUBST; trainer.mix[i].src = i; trainer.mix[i].weight = 100; } @@ -387,3 +388,265 @@ void GeneralSettings::convert(RadioDataConversionState & cstate) } } + +QString GeneralSettings::antennaModeToString() const +{ + return antennaModeToString(antennaMode); +} + +QString GeneralSettings::bluetoothModeToString() const +{ + return bluetoothModeToString(bluetoothMode); +} + +QString GeneralSettings::auxSerialModeToString() const +{ + return auxSerialModeToString(auxSerialMode); +} + +QString GeneralSettings::telemetryBaudrateToString() const +{ + return telemetryBaudrateToString(telemetryBaudrate); +} + +// static +QString GeneralSettings::antennaModeToString(int value) +{ + Board::Type board = getCurrentBoard(); + + switch(value) { + case ANTENNA_MODE_INTERNAL: + return tr("Internal"); + case ANTENNA_MODE_ASK: + return tr("Ask"); + case ANTENNA_MODE_PER_MODEL: + return tr("Per model"); + case ANTENNA_MODE_EXTERNAL: + // case ANTENNA_MODE_INTERNAL_EXTERNAL: + return IS_HORUS_X12S(board) ? tr("Internal + External") : tr("External"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString GeneralSettings::bluetoothModeToString(int value) +{ + Board::Type board = getCurrentBoard(); + + switch(value) { + case BLUETOOTH_MODE_OFF: + return tr("OFF"); + case BLUETOOTH_MODE_ENABLED: + // case BLUETOOTH_MODE_TELEMETRY: + return IS_TARANIS_X9E(board) ? tr("Enabled") : tr("Telemetry"); + case BLUETOOTH_MODE_TRAINER: + return tr("Trainer"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString GeneralSettings::auxSerialModeToString(int value) +{ + switch(value) { + case AUX_SERIAL_OFF: + return tr("OFF"); + case AUX_SERIAL_TELE_MIRROR: + return tr("Telemetry Mirror"); + case AUX_SERIAL_TELE_IN: + return tr("Telemetry In"); + case AUX_SERIAL_SBUS_TRAINER: + return tr("SBUS Trainer"); + case AUX_SERIAL_LUA: + return tr("LUA"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString GeneralSettings::telemetryBaudrateToString(int value) +{ + switch(value) { + case 0: + return "400000"; + case 1: + return "115200"; + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +FieldRange GeneralSettings::getPPM_MultiplierRange() +{ + FieldRange result; + + result.min = 0; + result.max = 5; + result.decimals = 1; + result.step = 0.1; + result.offset = 10; + + return result; +} + +// static +FieldRange GeneralSettings::getTxVoltageCalibrationRange() +{ + FieldRange result; + + result.decimals = 1; + result.max = 9.9; + result.min = -result.max; + result.step = 0.1; + result.unit = tr("v"); + + return result; +} + +// static +FieldRange GeneralSettings::getTxCurrentCalibration() +{ + FieldRange result; + + result.max = 49; + result.min = -result.max; + result.unit = tr("mA"); + + return result; +} + +// static +AbstractStaticItemModel * GeneralSettings::antennaModeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_ANTENNAMODE); + + for (int i = ANTENNA_MODE_FIRST; i <= ANTENNA_MODE_LAST; i++) { + mdl->appendToItemList(antennaModeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * GeneralSettings::bluetoothModeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_BLUETOOTHMODE); + + for (int i = 0; i < BLUETOOTH_MODE_COUNT; i++) { + mdl->appendToItemList(bluetoothModeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * GeneralSettings::auxSerialModeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_AUXSERIALMODE); + + for (int i = 0; i < AUX_SERIAL_COUNT; i++) { + mdl->appendToItemList(auxSerialModeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * GeneralSettings::telemetryBaudrateItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_TELEMETRYBAUDRATE); + + for (int i = 0; i <= 1; i++) { + mdl->appendToItemList(telemetryBaudrateToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +/* + TrainerMix +*/ + +QString TrainerMix::modeToString() const +{ + return modeToString(mode); +} + +QString TrainerMix::srcToString() const +{ + return srcToString(src); +} + +// static +FieldRange TrainerMix::getWeightRange() +{ + FieldRange result; + + result.decimals = 0; + result.max = 125; + result.min = -result.max; + result.step = 1; + + return result; +} + +// static +QString TrainerMix::modeToString(int value) +{ + switch(value) { + case TRN_MIX_MODE_OFF: + return tr("OFF"); + case TRN_MIX_MODE_ADD: + return tr("+= (Sum)"); + case TRN_MIX_MODE_SUBST: + return tr(":= (Replace)"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString TrainerMix::srcToString(int value) +{ + return tr("CH%1").arg(value + 1); +} + +// static +AbstractStaticItemModel * TrainerMix::modeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_TRAINERMIX_MODE); + + for (int i = 0; i < TRN_MIX_MODE_COUNT; i++) { + mdl->appendToItemList(modeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * TrainerMix::srcItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_TRAINERMIX_SRC); + + for (int i = 0; i < Boards::getCapability(getCurrentBoard(), Board::Sticks); i++) { + mdl->appendToItemList(srcToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} diff --git a/companion/src/firmwares/generalsettings.h b/companion/src/firmwares/generalsettings.h index f7b28931d..466d630af 100644 --- a/companion/src/firmwares/generalsettings.h +++ b/companion/src/firmwares/generalsettings.h @@ -18,13 +18,13 @@ * GNU General Public License for more details. */ -#ifndef GENERALSETTINGS_H -#define GENERALSETTINGS_H +#pragma once #include "boards.h" #include "constants.h" #include "customfunctiondata.h" #include "rawsource.h" +#include "datahelpers.h" #include @@ -32,6 +32,15 @@ class Firmware; class ModelData; class GeneralSettings; class RadioDataConversionState; +class AbstractStaticItemModel; + +// identiying names of static abstract item models +constexpr char AIM_GS_ANTENNAMODE[] {"gs.antennamode"}; +constexpr char AIM_GS_BLUETOOTHMODE[] {"gs.bluetoothmode"}; +constexpr char AIM_GS_AUXSERIALMODE[] {"gs.auxserialmode"}; +constexpr char AIM_GS_TELEMETRYBAUDRATE[] {"gs.telemetrybaudrate"}; +constexpr char AIM_TRAINERMIX_MODE[] {"trainermix.mode"}; +constexpr char AIM_TRAINERMIX_SRC[] {"trainermix.src"}; enum UartModes { UART_MODE_NONE, @@ -42,23 +51,52 @@ enum UartModes { }; class TrainerMix { + Q_DECLARE_TR_FUNCTIONS(TrainerMix) + public: + enum TrainerMixModes { + TRN_MIX_MODE_OFF, + TRN_MIX_MODE_ADD, + TRN_MIX_MODE_SUBST, + TRN_MIX_MODE_COUNT + }; + TrainerMix() { clear(); } + unsigned int src; // 0-7 = ch1-8 RawSwitch swtch; int weight; unsigned int mode; // off, add-mode, subst-mode + void clear() { memset(reinterpret_cast(this), 0, sizeof(TrainerMix)); } + QString modeToString() const; + QString srcToString() const; + + static FieldRange getWeightRange(); + static QString modeToString(int value); + static QString srcToString(int value); + static AbstractStaticItemModel * modeItemModel(); + static AbstractStaticItemModel * srcItemModel(); }; class TrainerData { public: TrainerData() { clear(); } - int calib[4]; - TrainerMix mix[4]; + + int calib[4]; + TrainerMix mix[4]; + void clear() { memset(reinterpret_cast(this), 0, sizeof(TrainerData)); } }; +constexpr int CURR_MODEL_FILENAME_LEN {16}; +constexpr int OWNER_NAME_LEN {10}; +constexpr int BLUETOOTH_NAME_LEN {10}; +constexpr int TTS_LANGUAGE_LEN {2}; +constexpr int HARDWARE_NAME_LEN {3}; +constexpr int THEME_NAME_LEN {8}; +constexpr int REGISTRATION_ID_LEN {8}; + class GeneralSettings { Q_DECLARE_TR_FUNCTIONS(GeneralSettings) @@ -71,6 +109,36 @@ class GeneralSettings { BEEPER_ALL = 1 }; + enum BluetoothMode { + BLUETOOTH_MODE_OFF, + BLUETOOTH_MODE_ENABLED, + BLUETOOTH_MODE_TELEMETRY = BLUETOOTH_MODE_ENABLED, + BLUETOOTH_MODE_TRAINER, + BLUETOOTH_MODE_COUNT + }; + + enum AntennaMode { + ANTENNA_MODE_INTERNAL = -2, + ANTENNA_MODE_FIRST = ANTENNA_MODE_INTERNAL, + ANTENNA_MODE_ASK, + ANTENNA_MODE_PER_MODEL, + ANTENNA_MODE_EXTERNAL, + ANTENNA_MODE_INTERNAL_EXTERNAL = ANTENNA_MODE_EXTERNAL, + ANTENNA_MODE_LAST = ANTENNA_MODE_INTERNAL_EXTERNAL + }; + + enum AuxSerialMode { + AUX_SERIAL_OFF, + AUX_SERIAL_TELE_MIRROR, + AUX_SERIAL_TELE_IN, + AUX_SERIAL_SBUS_TRAINER, + AUX_SERIAL_LUA, + AUX_SERIAL_COUNT + }; + + enum TelemetryBaudrate { + }; + GeneralSettings(); void convert(RadioDataConversionState & cstate); @@ -81,61 +149,61 @@ class GeneralSettings { unsigned int version; unsigned int variant; - int calibMid[CPN_MAX_ANALOGS]; - int calibSpanNeg[CPN_MAX_ANALOGS]; - int calibSpanPos[CPN_MAX_ANALOGS]; - unsigned int currModelIndex; - char currModelFilename[16+1]; - unsigned int contrast; - unsigned int vBatWarn; - int txVoltageCalibration; - int txCurrentCalibration; - int vBatMin; - int vBatMax; - int backlightMode; + int calibMid[CPN_MAX_ANALOGS]; + int calibSpanNeg[CPN_MAX_ANALOGS]; + int calibSpanPos[CPN_MAX_ANALOGS]; + unsigned int currModelIndex; + char currModelFilename[CURR_MODEL_FILENAME_LEN + 1]; + unsigned int contrast; + unsigned int vBatWarn; + int txVoltageCalibration; + int txCurrentCalibration; + int vBatMin; + int vBatMax; + int backlightMode; TrainerData trainer; - unsigned int view; // main screen view // TODO enum - bool disableThrottleWarning; - bool fai; - bool disableMemoryWarning; + unsigned int view; // main screen view // TODO enum + bool disableThrottleWarning; + bool fai; + bool disableMemoryWarning; BeeperMode beeperMode; - bool disableAlarmWarning; - bool disableRssiPoweroffAlarm; - unsigned int usbMode; - unsigned int jackMode; - bool sportPower; + bool disableAlarmWarning; + bool disableRssiPoweroffAlarm; + unsigned int usbMode; + unsigned int jackMode; + bool sportPower; BeeperMode hapticMode; - unsigned int stickMode; // TODO enum - int timezone; - bool adjustRTC; - bool optrexDisplay; - unsigned int inactivityTimer; - unsigned int telemetryBaudrate; - bool minuteBeep; - bool preBeep; - bool flashBeep; - unsigned int splashMode; + unsigned int stickMode; // TODO enum + int timezone; + bool adjustRTC; + bool optrexDisplay; + unsigned int inactivityTimer; + unsigned int telemetryBaudrate; + bool minuteBeep; + bool preBeep; + bool flashBeep; + unsigned int splashMode; int splashDuration; - unsigned int backlightDelay; - unsigned int templateSetup; //RETA order according to chout_ar array - int PPM_Multiplier; - int hapticLength; - unsigned int reNavigation; + unsigned int backlightDelay; + unsigned int templateSetup; //RETA order according to chout_ar array + int PPM_Multiplier; + int hapticLength; + unsigned int reNavigation; unsigned int stickReverse; - unsigned int speakerPitch; - int hapticStrength; - unsigned int speakerMode; - char ownerName[10+1]; - int beeperLength; - unsigned int gpsFormat; - int speakerVolume; - unsigned int backlightBright; - unsigned int backlightOffBright; + unsigned int speakerPitch; + int hapticStrength; + unsigned int speakerMode; + char ownerName[OWNER_NAME_LEN + 1]; + int beeperLength; + unsigned int gpsFormat; + int speakerVolume; + unsigned int backlightBright; + unsigned int backlightOffBright; int switchesDelay; unsigned int mAhWarn; unsigned int mAhUsed; unsigned int globalTimer; - char bluetoothName[10+1]; + char bluetoothName[BLUETOOTH_NAME_LEN + 1]; unsigned int bluetoothBaudrate; unsigned int bluetoothMode; unsigned int sticksGain; @@ -145,7 +213,7 @@ class GeneralSettings { bool rtcCheckDisable; bool keysBacklight; unsigned int imperial; - char ttsLanguage[2+1]; + char ttsLanguage[TTS_LANGUAGE_LEN + 1]; int beepVolume; int wavVolume; int varioVolume; @@ -156,33 +224,47 @@ class GeneralSettings { unsigned int mavbaud; unsigned int switchUnlockStates; unsigned int auxSerialMode; + unsigned int aux2SerialMode; int antennaMode; unsigned int backlightColor; CustomFunctionData customFn[CPN_MAX_SPECIAL_FUNCTIONS]; - char switchName[CPN_MAX_SWITCHES][3+1]; + char switchName[CPN_MAX_SWITCHES][HARDWARE_NAME_LEN + 1]; unsigned int switchConfig[CPN_MAX_SWITCHES]; - char stickName[CPN_MAX_STICKS][3+1]; - char potName[CPN_MAX_KNOBS][3+1]; + char stickName[CPN_MAX_STICKS][HARDWARE_NAME_LEN + 1]; + char potName[CPN_MAX_KNOBS][HARDWARE_NAME_LEN + 1]; unsigned int potConfig[CPN_MAX_KNOBS]; - char sliderName[CPN_MAX_SLIDERS][3+1]; + char sliderName[CPN_MAX_SLIDERS][HARDWARE_NAME_LEN + 1]; unsigned int sliderConfig[CPN_MAX_SLIDERS]; - char themeName[8+1]; - typedef uint8_t ThemeOptionData[8+1]; + char themeName[THEME_NAME_LEN + 1]; + typedef uint8_t ThemeOptionData[8 + 1]; ThemeOptionData themeOptionValue[5]; - - char registrationId[8+1]; + + char registrationId[REGISTRATION_ID_LEN + 1]; int gyroMax; int gyroOffset; int pwrOnSpeed; int pwrOffSpeed; - + bool switchPositionAllowedTaranis(int index) const; bool switchSourceAllowedTaranis(int index) const; bool isPotAvailable(int index) const; bool isSliderAvailable(int index) const; + QString antennaModeToString() const; + QString bluetoothModeToString() const; + QString auxSerialModeToString() const; + QString telemetryBaudrateToString() const; + + static QString antennaModeToString(int value); + static QString bluetoothModeToString(int value); + static QString auxSerialModeToString(int value); + static QString telemetryBaudrateToString(int value); + static FieldRange getPPM_MultiplierRange(); + static FieldRange getTxVoltageCalibrationRange(); + static FieldRange getTxCurrentCalibration(); + static AbstractStaticItemModel * antennaModeItemModel(); + static AbstractStaticItemModel * bluetoothModeItemModel(); + static AbstractStaticItemModel * auxSerialModeItemModel(); + static AbstractStaticItemModel * telemetryBaudrateItemModel(); }; - - -#endif // GENERALSETTINGS_H diff --git a/companion/src/firmwares/moduledata.cpp b/companion/src/firmwares/moduledata.cpp index b36d88e04..49a079d79 100644 --- a/companion/src/firmwares/moduledata.cpp +++ b/companion/src/firmwares/moduledata.cpp @@ -45,19 +45,6 @@ bool ModuleData::isPxx2Module() const } } -bool ModuleData::isPxx1Module() const -{ - switch(protocol){ - case PULSES_PXX_XJT_X16: - case PULSES_PXX_R9M: - case PULSES_PXX_R9M_LITE: - case PULSES_PXX_R9M_LITE_PRO: - return true; - default: - return false; - } -} - bool ModuleData::supportRxNum() const { switch (protocol) { diff --git a/companion/src/firmwares/moduledata.h b/companion/src/firmwares/moduledata.h index 119f44eb8..8acec18b0 100644 --- a/companion/src/firmwares/moduledata.h +++ b/companion/src/firmwares/moduledata.h @@ -215,12 +215,12 @@ class ModuleData { struct Access { unsigned int receivers; char receiverName[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME+1]; + unsigned int racingMode; } access; void clear() { memset(this, 0, sizeof(ModuleData)); } void convert(RadioDataConversionState & cstate); bool isPxx2Module() const; - bool isPxx1Module() const; bool supportRxNum() const; QString polarityToString() const { return ppm.pulsePol ? tr("Positive") : tr("Negative"); } QString rfProtocolToString() const; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index f9fc8c8f5..b82e76795 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -2158,15 +2158,16 @@ class ModuleUnionField: public UnionField { module(module) { internalField.Append(new UnsignedField<3>(this, module.access.receivers)); - internalField.Append(new SpareBitsField<5>(this)); + internalField.Append(new SpareBitsField<4>(this)); + internalField.Append(new UnsignedField<1>(this, module.access.racingMode)); - for (int i=0; i(this, receiverName[i])); memset(receiverName, 0, sizeof(receiverName)); } - bool select(const unsigned int& attr) const override + bool select(const unsigned int & attr) const override { return attr >= PULSES_ACCESS_ISRM && attr <= PULSES_ACCESS_R9M_LITE_PRO; } @@ -2759,7 +2760,7 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type if (version >= 218) { internalField.Append(new UnsignedField<4>(this, generalData.auxSerialMode)); if (IS_FAMILY_HORUS_OR_T16(board) && version >= 219) { - internalField.Append(new SpareBitsField<4>(this)); + internalField.Append(new UnsignedField<4>(this, generalData.aux2SerialMode)); } else { for (uint8_t i=0; isetName("sensordata.unit"); for (int i = 0; i <= UNIT_MAX; i++) { - QString str = unitToString(i); + QString str = unitToString(i, false); if (str != CPN_STR_UNKNOWN_ITEM) mdl->appendToItemList(str, i); } diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 8a032cec5..1581b444b 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -188,7 +188,7 @@ class SensorData { QString typeToString() const; QString formulaToString() const; QString cellIndexToString() const; - QString unitToString() const; + QString unitToString(bool hideRaw = true) const; QString precToString() const; int getMask() const; QString paramsToString(const ModelData * model) const; @@ -203,7 +203,7 @@ class SensorData { static QString typeToString(const int value); static QString formulaToString(const int value); static QString cellIndexToString(const int value); - static QString unitToString(const int value); + static QString unitToString(const int value, bool hideRaw = true); static QString precToString(const int value); static bool isRssiSensorAvailable(const ModelData * model, const int value); static QString rssiSensorToString(const ModelData * model, const int value); diff --git a/companion/src/generaledit/CMakeLists.txt b/companion/src/generaledit/CMakeLists.txt index 2c458c719..a9c12997a 100644 --- a/companion/src/generaledit/CMakeLists.txt +++ b/companion/src/generaledit/CMakeLists.txt @@ -3,16 +3,18 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) set(generaledit_NAMES generaledit generalsetup - trainer - hardware ) set(generaledit_SRCS calibration.cpp + hardware.cpp + trainer.cpp ) set(generaledit_HDRS calibration.h + hardware.h + trainer.h ) set(generaledit_UIS diff --git a/companion/src/generaledit/calibration.cpp b/companion/src/generaledit/calibration.cpp index 05fb30a58..e43b61a53 100644 --- a/companion/src/generaledit/calibration.cpp +++ b/companion/src/generaledit/calibration.cpp @@ -18,93 +18,45 @@ * GNU General Public License for more details. */ -#include -#include -#include #include "calibration.h" - +#include "autospinbox.h" +#include "helpers.h" CalibrationPanel::CalibrationPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware): GeneralPanel(parent, generalSettings, firmware) { + int rows = Boards::getCapability(getCurrentBoard(), Board::MaxAnalogs); - tableWidget = new QTableWidget(); - QVBoxLayout * layout = new QVBoxLayout(); - layout->addWidget(tableWidget); - layout->setContentsMargins(0, 0, 0, 0); - this->setLayout(layout); - - tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - tableWidget->setColumnCount(3); - tableWidget->setShowGrid(false); - tableWidget->setSelectionMode(QAbstractItemView::NoSelection); - tableWidget->setFrameStyle(QFrame::NoFrame | QFrame::Plain); - tableWidget->setStyleSheet("QTableWidget {background-color: transparent;}"); QStringList headerLabels; - headerLabels << tr("Negative span") << tr("Mid value") << tr("Positive span"); - tableWidget->setHorizontalHeaderLabels(headerLabels); + headerLabels << "" << tr("Negative span") << tr("Mid value") << tr("Positive span"); - int rows = getBoardCapability(getCurrentBoard(), Board::MaxAnalogs); - tableWidget->setRowCount(rows); + TableLayout * tableLayout = new TableLayout(this, rows, headerLabels); - for(int i = 0; i < rows; ++i) { + for (int i = 0; i < rows; i++) { + int col = 0; + QLabel * label = new QLabel(this); + label->setText(firmware->getAnalogInputName(i)); + tableLayout->addWidget(i, col++, label); - QTableWidgetItem *newItem = new QTableWidgetItem(firmware->getAnalogInputName(i)); - newItem->setTextAlignment(Qt::AlignLeft); - tableWidget->setVerticalHeaderItem(i, newItem); + QLineEdit * leNeg = new QLineEdit(this); + leNeg->setText(QString("%1").arg(generalSettings.calibSpanNeg[i])); + leNeg->setReadOnly(true); + tableLayout->addWidget(i, col++, leNeg); - for(int j = 0; j < 3; ++j) { - QSpinBox * newItem = new QSpinBox(); - newItem->setMinimum(-9999); - newItem->setMaximum(9999); - newItem->setSingleStep(1); - newItem->setValue(getCalibrationValue(i, j)); - newItem->setProperty("row", i); - newItem->setProperty("column", j); - tableWidget->setCellWidget(i, j, newItem); - connect(newItem, SIGNAL(valueChanged(int)), this, SLOT(onCellChanged(int))); - } + QLineEdit * leMid = new QLineEdit(this); + leMid->setText(QString("%1").arg(generalSettings.calibMid[i])); + leMid->setReadOnly(true); + tableLayout->addWidget(i, col++, leMid); + + QLineEdit * lePos = new QLineEdit(this); + lePos->setText(QString("%1").arg(generalSettings.calibSpanPos[i])); + lePos->setReadOnly(true); + tableLayout->addWidget(i, col++, lePos); } + disableMouseScrolling(); -} - -void CalibrationPanel::onCellChanged(int value) -{ - QSpinBox * sb = qobject_cast(sender()); - int row = sb->property("row").toInt(); - int column = sb->property("column").toInt(); - - if (value != getCalibrationValue(row, column)) { - setCalibrationValue(row,column, value); - qDebug() << "CalibrationPanel::onCellChanged" << row << column << "to" << value; - emit modified(); - } -} - -int CalibrationPanel::getCalibrationValue(int row, int column) -{ - switch(column) { - case 0: - return generalSettings.calibSpanNeg[row]; - case 1: - return generalSettings.calibMid[row]; - case 2: - return generalSettings.calibSpanPos[row]; - } - return 0; -} - -void CalibrationPanel::setCalibrationValue(int row, int column, int value) -{ - switch(column) { - case 0: - generalSettings.calibSpanNeg[row] = value; - break; - case 1: - generalSettings.calibMid[row] = value; - break; - case 2: - generalSettings.calibSpanPos[row] = value; - break; - } + tableLayout->resizeColumnsToContents(); + tableLayout->setColumnWidth(0, QString(15, ' ')); + tableLayout->pushColumnsLeft(headerLabels.count()); + tableLayout->pushRowsUp(rows + 1); } diff --git a/companion/src/generaledit/calibration.h b/companion/src/generaledit/calibration.h index fd28e492b..279b7cf80 100644 --- a/companion/src/generaledit/calibration.h +++ b/companion/src/generaledit/calibration.h @@ -18,20 +18,10 @@ * GNU General Public License for more details. */ -#ifndef _CALIBRATION_H_ -#define _CALIBRATION_H_ +#pragma once -#include #include "generaledit.h" -namespace Ui { - class Calibration; -} - -class QLabel; -class AutoLineEdit; -class AutoComboBox; - class CalibrationPanel : public GeneralPanel { Q_OBJECT @@ -40,14 +30,5 @@ class CalibrationPanel : public GeneralPanel CalibrationPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware); virtual ~CalibrationPanel() {}; - private slots: - void onCellChanged(int value); - private: - QTableWidget * tableWidget; - - int getCalibrationValue(int row, int column); - void setCalibrationValue(int row, int column, int value); }; - -#endif // _CALIBRATION_H_ diff --git a/companion/src/generaledit/generaledit.cpp b/companion/src/generaledit/generaledit.cpp index e80038f9f..99884ed43 100644 --- a/companion/src/generaledit/generaledit.cpp +++ b/companion/src/generaledit/generaledit.cpp @@ -46,26 +46,27 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir } ui->profile_CB->clear(); - for (int i=0; iprofile_CB->addItem(name, i); - if (i==g.id()) { - ui->profile_CB->setCurrentIndex(ui->profile_CB->count()-1); + if (i == g.id()) { + ui->profile_CB->setCurrentIndex(ui->profile_CB->count() - 1); } } } - sharedItemModels = new CompoundItemModelFactory(&generalSettings, nullptr); - sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSource); - sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); - sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); - sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); + editorItemModels = new CompoundItemModelFactory(&generalSettings, nullptr); + // tabs created below expect these item models to be pre-registered + editorItemModels->addItemModel(AbstractItemModel::IMID_RawSource); + editorItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); + editorItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); + editorItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); addTab(new GeneralSetupPanel(this, generalSettings, firmware), tr("Setup")); - addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, sharedItemModels), tr("Global Functions")); - addTab(new TrainerPanel(this, generalSettings, firmware), tr("Trainer")); - addTab(new HardwarePanel(this, generalSettings, firmware), tr("Hardware")); + addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, editorItemModels), tr("Global Functions")); + addTab(new TrainerPanel(this, generalSettings, firmware, editorItemModels), tr("Trainer")); + addTab(new HardwarePanel(this, generalSettings, firmware, editorItemModels), tr("Hardware")); addTab(new CalibrationPanel(this, generalSettings, firmware), tr("Calibration")); ui->tabWidget->setCurrentIndex( g.generalEditTab() ); @@ -74,7 +75,7 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir GeneralEdit::~GeneralEdit() { delete ui; - delete sharedItemModels; + delete editorItemModels; // cleans up item models registered by the tabs } void GeneralEdit::closeEvent(QCloseEvent *event) @@ -90,7 +91,7 @@ void GeneralEdit::addTab(GenericPanel *panel, QString text) VerticalScrollArea * area = new VerticalScrollArea(widget, panel); baseLayout->addWidget(area); ui->tabWidget->addTab(widget, text); - connect(panel, SIGNAL(modified()), this, SLOT(onTabModified())); + connect(panel, &GenericPanel::modified, this, &GeneralEdit::onTabModified); } void GeneralEdit::onTabModified() diff --git a/companion/src/generaledit/generaledit.h b/companion/src/generaledit/generaledit.h index b2e6caa82..2ad27cb1c 100644 --- a/companion/src/generaledit/generaledit.h +++ b/companion/src/generaledit/generaledit.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _GENERALEDIT_H_ -#define _GENERALEDIT_H_ +#pragma once #include #include "eeprominterface.h" @@ -35,7 +34,7 @@ class GeneralPanel : public GenericPanel { public: GeneralPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware): - GenericPanel(parent, NULL, generalSettings, firmware) + GenericPanel(parent, nullptr, generalSettings, firmware) { } }; @@ -73,7 +72,5 @@ class GeneralEdit : public QDialog QVector panels; void addTab(GenericPanel *panel, QString text); void closeEvent(QCloseEvent *event); - CompoundItemModelFactory *sharedItemModels; + CompoundItemModelFactory *editorItemModels; }; - -#endif // _GENERALEDIT_H_ diff --git a/companion/src/generaledit/hardware.cpp b/companion/src/generaledit/hardware.cpp index 9d2e8e452..4f7e52a7a 100644 --- a/companion/src/generaledit/hardware.cpp +++ b/companion/src/generaledit/hardware.cpp @@ -19,326 +19,268 @@ */ #include "hardware.h" -#include "ui_hardware.h" +#include "compounditemmodels.h" +#include "filtereditemmodels.h" +#include "autolineedit.h" +#include "autocombobox.h" +#include "autocheckbox.h" +#include "autospinbox.h" +#include "autodoublespinbox.h" -void HardwarePanel::setupSwitchType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type, bool threePos) -{ - Board::Type board = getCurrentBoard(); - if (IS_STM32(board) && index < getBoardCapability(board, Board::Switches)) { - type->addItem(tr("None"), Board::SWITCH_NOT_AVAILABLE); - type->addItem(tr("2 Positions Toggle"), Board::SWITCH_TOGGLE); - type->addItem(tr("2 Positions"), Board::SWITCH_2POS); - if (threePos) { - type->addItem(tr("3 Positions"), Board::SWITCH_3POS); - } - name->setField(generalSettings.switchName[index], 3, this); - type->setField(generalSettings.switchConfig[index], this); - if (IS_TARANIS_X7(board)) { - if (index == 4) { - label->setText("SF"); - } - else if (index == 5) { - label->setText("SH"); - } - if (index == 6) { - label->setText("SI"); - } - else if (index == 7) { - label->setText("SJ"); - } - } - else if (IS_RADIOMASTER_TX12(board)) { - if (index == 6) { - label->setText("SI"); - } - else if (index == 7) { - label->setText("SJ"); - } - } - else if (IS_FAMILY_T12(board)) { - if (index == 4) { - label->setText("SG"); - } - else if (index == 5) { - label->setText("SH"); - } - } - } - else { - label->hide(); - name->hide(); - type->hide(); - } -} +#include +#include +#include -void HardwarePanel::setupPotType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type) -{ - Board::Type board = firmware->getBoard(); +constexpr char FIM_SWITCHTYPE2POS[] {"Switch Type 2 Pos"}; +constexpr char FIM_SWITCHTYPE3POS[] {"Switch Type 3 Pos"}; - if (IS_STM32(board) && index < getBoardCapability(board, Board::Pots)) { - label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+index).toString()); - type->addItem(tr("None"), Board::POT_NONE); - type->addItem(tr("Pot with detent"), Board::POT_WITH_DETENT); - type->addItem(tr("Multipos switch"), Board::POT_MULTIPOS_SWITCH); - type->addItem(tr("Pot without detent"), Board::POT_WITHOUT_DETENT); - name->setField(generalSettings.potName[index], 3, this); - type->setField(generalSettings.potConfig[index], this); - } - else { - label->hide(); - name->hide(); - type->hide(); - } -} - -void HardwarePanel::setupSliderType(int index, QLabel *label, AutoLineEdit *name, AutoComboBox *type) -{ - Board::Type board = firmware->getBoard(); - - if (IS_STM32(board) && index < getBoardCapability(board, Board::Sliders)) { - label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+getBoardCapability(board, Board::Pots)+index).toString()); - type->addItem(tr("None"), Board::SLIDER_NONE); - type->addItem(tr("Slider with detent"), Board::SLIDER_WITH_DETENT); - name->setField(generalSettings.sliderName[index], 3, this); - type->setField(generalSettings.sliderConfig[index], this); - } - else { - label->hide(); - name->hide(); - type->hide(); - } -} - -bool HardwarePanel::isSwitch3Pos(int idx) -{ - Board::Type board = firmware->getBoard(); - Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board, idx); - - switchInfo.config = Board::SwitchType(generalSettings.switchConfig[idx]); - return switchInfo.config == Board::SWITCH_3POS; -}; - -HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware): +HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): GeneralPanel(parent, generalSettings, firmware), - ui(new Ui::Hardware) + board(firmware->getBoard()), + editorItemModels(sharedItemModels) { - ui->setupUi(this); + editorItemModels->registerItemModel(Boards::potTypeItemModel()); + editorItemModels->registerItemModel(Boards::sliderTypeItemModel()); + int id = editorItemModels->registerItemModel(Boards::switchTypeItemModel()); - Board::Type board = firmware->getBoard(); + tabFilteredModels = new FilteredItemModelFactory(); + tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id), Board::SwitchTypeContext2Pos), FIM_SWITCHTYPE2POS); + tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id), Board::SwitchTypeContext3Pos), FIM_SWITCHTYPE3POS); - if (IS_STM32(board)) { - ui->rudName->setField(generalSettings.stickName[0], 3, this); - ui->eleName->setField(generalSettings.stickName[1], 3, this); - ui->thrName->setField(generalSettings.stickName[2], 3, this); - ui->ailName->setField(generalSettings.stickName[3], 3, this); + int antmodelid = editorItemModels->registerItemModel(GeneralSettings::antennaModeItemModel()); + int btmodelid = editorItemModels->registerItemModel(GeneralSettings::bluetoothModeItemModel()); + int auxmodelid = editorItemModels->registerItemModel(GeneralSettings::auxSerialModeItemModel()); + int baudmodelid = editorItemModels->registerItemModel(GeneralSettings::telemetryBaudrateItemModel()); + + grid = new QGridLayout(this); + int count; + int row = 0; + + count = Boards::getCapability(board, Board::Sticks); + if (count) { + for (int i = 0; i < count; i++) { + addStick(i, row); + } } - else { - ui->rudLabel->hide(); - ui->rudName->hide(); - ui->eleLabel->hide(); - ui->eleName->hide(); - ui->thrLabel->hide(); - ui->thrName->hide(); - ui->ailLabel->hide(); - ui->ailName->hide(); - ui->potsTypeSeparator_1->hide(); - ui->potsTypeSeparator_2->hide(); + + count = Boards::getCapability(board, Board::Pots); + if (count) { + for (int i = 0; i < count; i++) { + addPot(i, row); + } } - + + count = Boards::getCapability(board, Board::Sliders); + if (count) { + for (int i = 0; i < count; i++) { + addSlider(i, row); + } + addLine(row); + } + + count = Boards::getCapability(board, Board::Switches); + if (count) { + for (int i = 0; i < count; i++) { + addSwitch(i, row); + } + addLine(row); + } + + addLabel(tr("Battery Offset"), row, 0); + AutoDoubleSpinBox *txVoltageCalibration = new AutoDoubleSpinBox(this); + FieldRange txVCRng = GeneralSettings::getTxVoltageCalibrationRange(); + txVoltageCalibration->setDecimals(txVCRng.decimals); + txVoltageCalibration->setSingleStep(txVCRng.step); + txVoltageCalibration->setSuffix(txVCRng.unit); + txVoltageCalibration->setField(generalSettings.txVoltageCalibration); + addParams(row, txVoltageCalibration); + + if (Boards::getCapability(board, Board::HasRTC)) { + addLabel(tr("RTC Battery Check"), row, 0); + AutoCheckBox *rtcCheckDisable = new AutoCheckBox(this); + rtcCheckDisable->setField(generalSettings.rtcCheckDisable, this, true); + addParams(row, rtcCheckDisable); + } + + if (firmware->getCapability(HasBluetooth)) { + addLabel(tr("Bluetooth"), row, 0); + + QGridLayout *btlayout = new QGridLayout(); + + AutoComboBox *bluetoothMode = new AutoComboBox(this); + bluetoothMode->setModel(editorItemModels->getItemModel(btmodelid)); + bluetoothMode->setField(generalSettings.bluetoothMode, this); + btlayout->addWidget(bluetoothMode, 0, 0); + + QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ); + btlayout->addItem(spacer, 0, 1); + + QLabel *btnamelabel = new QLabel(this); + btnamelabel->setText(tr("Device Name:")); + btlayout->addWidget(btnamelabel, 0, 2); + + AutoLineEdit *bluetoothName = new AutoLineEdit(this); + bluetoothName->setField(generalSettings.bluetoothName, BLUETOOTH_NAME_LEN, this); + btlayout->addWidget(bluetoothName, 0, 3); + + grid->addLayout(btlayout, row, 1); + row++; + } + + if (firmware->getCapability(HasAntennaChoice)) { + addLabel(tr("Antenna"), row, 0); + AutoComboBox *antennaMode = new AutoComboBox(this); + antennaMode->setModel(editorItemModels->getItemModel(antmodelid)); + antennaMode->setField(generalSettings.antennaMode, this); + addParams(row, antennaMode); + } + + if (firmware->getCapability(HasAuxSerialMode)) { + QString lbl = "Serial Port"; + if (IS_RADIOMASTER_TX16S(board)) + lbl.append(" (TTL)"); + addLabel(tr("%1").arg(lbl), row, 0); + AutoComboBox *serialPortMode = new AutoComboBox(this); + serialPortMode->setModel(editorItemModels->getItemModel(auxmodelid)); + serialPortMode->setField(generalSettings.auxSerialMode); + addParams(row, serialPortMode); + } + + if (firmware->getCapability(HasAux2SerialMode)) { + QString lbl = "Serial Port 2"; + if (IS_RADIOMASTER_TX16S(board)) + lbl.append(" (TTL)"); + addLabel(tr("%1").arg(lbl), row, 0); + AutoComboBox *serialPort2Mode = new AutoComboBox(this); + serialPort2Mode->setModel(editorItemModels->getItemModel(auxmodelid)); + serialPort2Mode->setField(generalSettings.aux2SerialMode); + addParams(row, serialPort2Mode); + } + + if (firmware->getCapability(HasADCJitterFilter)) { + addLabel(tr("ADC Filter"), row, 0); + AutoCheckBox *filterEnable = new AutoCheckBox(this); + filterEnable->setField(generalSettings.jitterFilter, this, true); + addParams(row, filterEnable); + } + if (firmware->getCapability(HasSportConnector)) { - ui->sportPower->setChecked(generalSettings.sportPower); - } - else { - ui->sportPower->hide(); - ui->sportPowerLabel->hide(); + addLabel(tr("S.Port Power"), row, 0); + AutoCheckBox *sportPower = new AutoCheckBox(this); + sportPower->setField(generalSettings.sportPower, this); + addParams(row, sportPower); } - setupPotType(0, ui->pot1Label, ui->pot1Name, ui->pot1Type); - setupPotType(1, ui->pot2Label, ui->pot2Name, ui->pot2Type); - setupPotType(2, ui->pot3Label, ui->pot3Name, ui->pot3Type); - setupPotType(3, ui->pot4Label, ui->pot4Name, ui->pot4Type); - setupPotType(4, ui->pot5Label, ui->pot5Name, ui->pot5Type); - - setupSliderType(0, ui->lsLabel, ui->lsName, ui->lsType); - setupSliderType(1, ui->rsLabel, ui->rsName, ui->rsType); - setupSliderType(2, ui->ls2Label, ui->ls2Name, ui->ls2Type); - setupSliderType(3, ui->rs2Label, ui->rs2Name, ui->rs2Type); - - setupSwitchType(0, ui->saLabel, ui->saName, ui->saType, isSwitch3Pos(0)); - setupSwitchType(1, ui->sbLabel, ui->sbName, ui->sbType, isSwitch3Pos(1)); - setupSwitchType(2, ui->scLabel, ui->scName, ui->scType, isSwitch3Pos(2)); - setupSwitchType(3, ui->sdLabel, ui->sdName, ui->sdType, isSwitch3Pos(3)); - setupSwitchType(4, ui->seLabel, ui->seName, ui->seType, isSwitch3Pos(4)); - setupSwitchType(5, ui->sfLabel, ui->sfName, ui->sfType, isSwitch3Pos(5)); - setupSwitchType(6, ui->sgLabel, ui->sgName, ui->sgType, isSwitch3Pos(6)); - setupSwitchType(7, ui->shLabel, ui->shName, ui->shType, isSwitch3Pos(7)); - setupSwitchType(8, ui->siLabel, ui->siName, ui->siType, isSwitch3Pos(8)); - setupSwitchType(9, ui->sjLabel, ui->sjName, ui->sjType, isSwitch3Pos(9)); - setupSwitchType(10, ui->skLabel, ui->skName, ui->skType); // Here starts X9E, only 3 switches - setupSwitchType(11, ui->slLabel, ui->slName, ui->slType); - setupSwitchType(12, ui->smLabel, ui->smName, ui->smType); - setupSwitchType(13, ui->snLabel, ui->snName, ui->snType); - setupSwitchType(14, ui->soLabel, ui->soName, ui->soType); - setupSwitchType(15, ui->spLabel, ui->spName, ui->spType); - setupSwitchType(16, ui->sqLabel, ui->sqName, ui->sqType); - setupSwitchType(17, ui->srLabel, ui->srName, ui->srType); - - if (IS_TARANIS(board) && !IS_TARANIS_SMALL(board)) { - ui->serialPortMode->setCurrentIndex(generalSettings.auxSerialMode); - } - else { - ui->serialPortMode->setCurrentIndex(0); - ui->serialPortMode->hide(); - ui->serialPortLabel->hide(); + if (firmware->getCapability(HasTelemetryBaudrate)) { + addLabel(tr("Maximum Baud"), row, 0); + AutoComboBox *maxBaudRate = new AutoComboBox(this); + maxBaudRate->setModel(editorItemModels->getItemModel(baudmodelid)); + maxBaudRate->setField(generalSettings.telemetryBaudrate, this); + addParams(row, maxBaudRate); } - if (!IS_SKY9X(board)) { - ui->txCurrentCalibration->hide(); - ui->txCurrentCalibrationLabel->hide(); - } - - if (IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || IS_TARANIS_X9DP_2019(board) || IS_FAMILY_HORUS_OR_T16(board)) { - ui->bluetoothMode->addItem(tr("OFF"), 0); - if (IS_TARANIS_X9E(board)) { - ui->bluetoothMode->addItem(tr("Enabled"), 1); - } - else { - ui->bluetoothMode->addItem(tr("Telemetry"), 1); - ui->bluetoothMode->addItem(tr("Trainer"), 2); - } - ui->bluetoothMode->setField(generalSettings.bluetoothMode, this); - ui->bluetoothName->setField(generalSettings.bluetoothName, 10, this); - } - else { - ui->bluetoothLabel->hide(); - ui->bluetoothWidget->hide(); - } - - if ((IS_FAMILY_HORUS_OR_T16(board) && board != Board::BOARD_X10_EXPRESS) || (IS_TARANIS_XLITE(board) && !IS_TARANIS_XLITES(board))) { - ui->antennaMode->addItem(tr("Internal"), -2); - ui->antennaMode->addItem(tr("Ask"), -1); - ui->antennaMode->addItem(tr("Per model"), 0); - ui->antennaMode->addItem(IS_HORUS_X12S(board) ? tr("Internal + External") : tr("External"), 1); - ui->antennaMode->setField(generalSettings.antennaMode, this); - } - else { - ui->antennaLabel->hide(); - ui->antennaMode->hide(); - } - - if (IS_HORUS_OR_TARANIS(board)) { - ui->filterEnable->setChecked(!generalSettings.jitterFilter); - } - else { - ui->filterEnable->hide(); - ui->filterLabel->hide(); - } - - if (IS_STM32(board)) { - ui->rtcCheckDisable->setChecked(!generalSettings.rtcCheckDisable); - } - else { - ui->rtcCheckDisable->hide(); - ui->rtcCheckLabel->hide(); + if (firmware->getCapability(HastxCurrentCalibration)) { + addLabel(tr("Current Offset"), row, 0); + AutoSpinBox *txCurrentCalibration = new AutoSpinBox(this); + FieldRange txCCRng = GeneralSettings::getTxCurrentCalibration(); + txCurrentCalibration->setSuffix(txVCRng.unit); + txCurrentCalibration->setField(generalSettings.txCurrentCalibration); + addParams(row, txCurrentCalibration); } + addVSpring(grid, 0, grid->rowCount()); + addHSpring(grid, grid->columnCount(), 0); disableMouseScrolling(); - - setValues(); } HardwarePanel::~HardwarePanel() { - delete ui; + delete tabFilteredModels; } -void HardwarePanel::on_filterEnable_stateChanged() +void HardwarePanel::addStick(int index, int & row) { - generalSettings.jitterFilter = !ui->filterEnable->isChecked(); + int col = 0; + addLabel(Boards::getAnalogInputName(board, index), row, col++); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.stickName[index], HARDWARE_NAME_LEN, this); + + addParams(row, name); } -void HardwarePanel::on_sportPower_stateChanged() +void HardwarePanel::addPot(int index, int & row) { - generalSettings.sportPower = ui->sportPower->isChecked(); + addLabel(Boards::getAnalogInputName(board, Boards::getCapability(board, Board::Sticks) + index), row, 0); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.potName[index], HARDWARE_NAME_LEN, this); + + AutoComboBox *type = new AutoComboBox(this); + type->setModel(editorItemModels->getItemModel(AIM_BOARDS_POT_TYPE)); + type->setField(generalSettings.potConfig[index], this); + + addParams(row, name, type); } -void HardwarePanel::on_rtcCheckDisable_stateChanged() +void HardwarePanel::addSlider(int index, int & row) { - generalSettings.rtcCheckDisable = !ui->rtcCheckDisable->isChecked(); + addLabel(Boards::getAnalogInputName(board, Boards::getCapability(board, Board::Sticks) + + Boards::getCapability(board, Board::Pots) + index), row, 0); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.sliderName[index], HARDWARE_NAME_LEN, this); + + AutoComboBox *type = new AutoComboBox(this); + type->setModel(editorItemModels->getItemModel(AIM_BOARDS_SLIDER_TYPE)); + type->setField(generalSettings.sliderConfig[index], this); + + addParams(row, name, type); } -void HardwarePanel::on_PPM_MultiplierDSB_editingFinished() +void HardwarePanel::addSwitch(int index, int & row) { - int val = (int)(ui->PPM_MultiplierDSB->value()*10)-10; - if (generalSettings.PPM_Multiplier != val) { - generalSettings.PPM_Multiplier = val; - emit modified(); - } + addLabel(Boards::getSwitchInfo(board, index).name, row, 0); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.switchName[index], HARDWARE_NAME_LEN, this); + + AutoComboBox *type = new AutoComboBox(this); + Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board, index); + type->setModel(switchInfo.config < Board::SWITCH_3POS ? tabFilteredModels->getItemModel(FIM_SWITCHTYPE2POS) : + tabFilteredModels->getItemModel(FIM_SWITCHTYPE3POS)); + type->setField(generalSettings.switchConfig[index], this); + + addParams(row, name, type); } -void HardwarePanel::on_PPM1_editingFinished() +void HardwarePanel::addLabel(QString text, int row, int col) { - if (generalSettings.trainer.calib[0] != ui->PPM1->value()) { - generalSettings.trainer.calib[0] = ui->PPM1->value(); - emit modified(); - } + QLabel *label = new QLabel(this); + label->setText(text); + grid->addWidget(label, row, col); } -void HardwarePanel::on_PPM2_editingFinished() +void HardwarePanel::addLine(int & row) { - if (generalSettings.trainer.calib[1] != ui->PPM2->value()) { - generalSettings.trainer.calib[1] = ui->PPM2->value(); - emit modified(); - } + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + line->setLineWidth(1); + line->setMidLineWidth(0); + grid->addWidget(line, row, 0, 1, grid->columnCount()); + row++; } -void HardwarePanel::on_PPM3_editingFinished() +void HardwarePanel::addParams(int & row, QWidget * widget1, QWidget * widget2) { - if (generalSettings.trainer.calib[2] != ui->PPM3->value()) { - generalSettings.trainer.calib[2] = ui->PPM3->value(); - emit modified(); - } -} - -void HardwarePanel::on_PPM4_editingFinished() -{ - if (generalSettings.trainer.calib[3] != ui->PPM4->value()) { - generalSettings.trainer.calib[3] = ui->PPM4->value(); - emit modified(); - } -} - - -void HardwarePanel::on_txCurrentCalibration_editingFinished() -{ - if (generalSettings.txCurrentCalibration != ui->txCurrentCalibration->value()) { - generalSettings.txCurrentCalibration = ui->txCurrentCalibration->value(); - emit modified(); - } -} - -void HardwarePanel::setValues() -{ - ui->txVoltageCalibration->setValue((double)generalSettings.txVoltageCalibration/10); - ui->txCurrentCalibration->setValue((double)generalSettings.txCurrentCalibration); - - ui->PPM1->setValue(generalSettings.trainer.calib[0]); - ui->PPM2->setValue(generalSettings.trainer.calib[1]); - ui->PPM3->setValue(generalSettings.trainer.calib[2]); - ui->PPM4->setValue(generalSettings.trainer.calib[3]); - ui->PPM_MultiplierDSB->setValue((qreal)(generalSettings.PPM_Multiplier+10)/10); -} - -void HardwarePanel::on_txVoltageCalibration_editingFinished() -{ - if (generalSettings.txVoltageCalibration != ui->txVoltageCalibration->value()*10) { - generalSettings.txVoltageCalibration = ui->txVoltageCalibration->value()*10; - emit modified(); - } -} - -void HardwarePanel::on_serialPortMode_currentIndexChanged(int index) -{ - generalSettings.auxSerialMode = index; - emit modified(); + QGridLayout *subgrid = new QGridLayout(); + subgrid->addWidget(widget1, 0, 0); + if (widget2) + subgrid->addWidget(widget2, 0, 1); + else + addHSpring(subgrid, 1, 0); + addHSpring(subgrid, 2, 0); + grid->addLayout(subgrid, row, 1); + row++; } diff --git a/companion/src/generaledit/hardware.h b/companion/src/generaledit/hardware.h index 3b63a1d77..71636a2cc 100644 --- a/companion/src/generaledit/hardware.h +++ b/companion/src/generaledit/hardware.h @@ -18,52 +18,33 @@ * GNU General Public License for more details. */ -#ifndef _HARDWARE_H_ -#define _HARDWARE_H_ +#pragma once #include "generaledit.h" -namespace Ui { - class Hardware; -} - -class QLabel; -class AutoLineEdit; -class AutoComboBox; +class CompoundItemModelFactory; +class FilteredItemModelFactory; +class QGridLayout; class HardwarePanel : public GeneralPanel { Q_OBJECT public: - HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware); + HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * editorItemModels); virtual ~HardwarePanel(); - private slots: - void on_PPM1_editingFinished(); - void on_PPM2_editingFinished(); - void on_PPM3_editingFinished(); - void on_PPM4_editingFinished(); - void on_PPM_MultiplierDSB_editingFinished(); - - void on_txCurrentCalibration_editingFinished(); - void on_txVoltageCalibration_editingFinished(); - void on_filterEnable_stateChanged(); - void on_rtcCheckDisable_stateChanged(); - void on_sportPower_stateChanged(); - - void on_serialPortMode_currentIndexChanged(int index); - - protected: - void setupPotType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type); - void setupSliderType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type); - void setupSwitchType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type, bool threePos = true); - private: - Ui::Hardware *ui; + Board::Type board; + CompoundItemModelFactory *editorItemModels; + FilteredItemModelFactory *tabFilteredModels; + QGridLayout *grid; - void setValues(); - bool isSwitch3Pos(int idx); + void addStick(int index, int & row); + void addPot(int index, int & row); + void addSlider(int index, int & row); + void addSwitch(int index, int & row); + void addLabel(QString text, int row, int col); + void addLine(int & row); + void addParams(int & row, QWidget * widget1, QWidget * widget2 = nullptr); }; - -#endif // _HARDWARE_H_ diff --git a/companion/src/generaledit/hardware.ui b/companion/src/generaledit/hardware.ui deleted file mode 100644 index 5224ed785..000000000 --- a/companion/src/generaledit/hardware.ui +++ /dev/null @@ -1,1160 +0,0 @@ - - - Hardware - - - - 0 - 0 - 470 - 1618 - - - - - 0 - 0 - - - - Form - - - - 6 - - - - - - - - - - - - - - - - - - - OFF - - - - - S-Port Mirror - - - - - Telemetry - - - - - SBUS Trainer - - - - - Debug - - - - - - - - - - - - - - - - - - - - - Thr - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - 3 - - - - - - - SD - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 10 - - - - Qt::Horizontal - - - - - - - SO - - - - - - - -49 - - - 49 - - - - - - - 3 - - - - - - - RS - - - - - - - 3 - - - - - - - Ail - - - - - - - S4 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - LS2 - - - - - - - Antenna - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - PPM 1 - - - - - - - LS - - - - - - - Rud - - - - - - - Serial Port - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - SP - - - - - - - 3 - - - - - - - RS2 - - - - - - - - - - - - - - - - - SH - - - - - - - - - - - - - - - - - SM - - - - - - - SE - - - - - - - - - - - - - - - - - - - - 1 - - - 5.000000000000000 - - - 1.000000000000000 - - - - - - - S2 - - - - - - - 3 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - SJ - - - - - - - PPM 2 - - - - - - - 3 - - - - - - - SG - - - - - - - S5 - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 10 - - - - Qt::Horizontal - - - - - - - Current Offset - - - - - - - SK - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - ADC Filter - - - - - - - 3 - - - - - - - 3 - - - - - - - SF - - - - - - - - - - - - - - - - - - - - - - - - - - - S3 - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - SB - - - - - - - - - - - - - - - - - - - - SI - - - - - - - SQ - - - - - - - Ele - - - - - - - 3 - - - - - - - 3 - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - 3 - - - - - - - - - - SA - - - - - - - 3 - - - - - - - - - - - - - - SR - - - - - - - 3 - - - - - - - PPM Multiplier - - - - - - - S1 - - - - - - - - 0 - 0 - - - - Bluetooth - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - PPM 4 - - - - - - - - - - - - - - - - - v - - - 1 - - - -9.900000000000000 - - - 9.900000000000000 - - - 0.100000000000000 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - 3 - - - - - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 20 - - - - - - - - Device Name: - - - - - - - 10 - - - - - - - - - - PPM 3 - - - - - - - RTC Batt Check - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SL - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - 3 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - 3 - - - - - - - SC - - - - - - - 3 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - - 0 - 10 - - - - Qt::Horizontal - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - SN - - - - - - - Battery Offset - - - - - - - 3 - - - - - - - 3 - - - - - - - S.Port Power - - - - - - - - - - - - - - - AutoComboBox - QComboBox -
autocombobox.h
-
- - AutoLineEdit - QLineEdit -
autolineedit.h
-
-
- - -
diff --git a/companion/src/generaledit/trainer.cpp b/companion/src/generaledit/trainer.cpp index 721e2d60a..997f5f456 100644 --- a/companion/src/generaledit/trainer.cpp +++ b/companion/src/generaledit/trainer.cpp @@ -19,103 +19,106 @@ */ #include "trainer.h" -#include "ui_trainer.h" +#include "compounditemmodels.h" +#include "autocombobox.h" +#include "autolineedit.h" +#include "autospinbox.h" +#include "autodoublespinbox.h" -TrainerPanel::TrainerPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware): - GeneralPanel(parent, generalSettings, firmware), - ui(new Ui::Trainer) +#include +#include +#include + +TrainerPanel::TrainerPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * editorItemModels): + GeneralPanel(parent, generalSettings, firmware) { - ui->setupUi(this); + int modeid = editorItemModels->registerItemModel(TrainerMix::modeItemModel()); + int srcid = editorItemModels->registerItemModel(TrainerMix::srcItemModel()); + Board::Type board = getCurrentBoard(); + const int stickcnt = Boards::getCapability(board, Board::Sticks); - ui->trnMode_1->setCurrentIndex(generalSettings.trainer.mix[0].mode); - ui->trnChn_1->setCurrentIndex(generalSettings.trainer.mix[0].src); - ui->trnWeight_1->setValue(generalSettings.trainer.mix[0].weight); - ui->trnMode_2->setCurrentIndex(generalSettings.trainer.mix[1].mode); - ui->trnChn_2->setCurrentIndex(generalSettings.trainer.mix[1].src); - ui->trnWeight_2->setValue(generalSettings.trainer.mix[1].weight); - ui->trnMode_3->setCurrentIndex(generalSettings.trainer.mix[2].mode); - ui->trnChn_3->setCurrentIndex(generalSettings.trainer.mix[2].src); - ui->trnWeight_3->setValue(generalSettings.trainer.mix[2].weight); - ui->trnMode_4->setCurrentIndex(generalSettings.trainer.mix[3].mode); - ui->trnChn_4->setCurrentIndex(generalSettings.trainer.mix[3].src); - ui->trnWeight_4->setValue(generalSettings.trainer.mix[3].weight); + const FieldRange weightrng = TrainerMix::getWeightRange(); + grid = new QGridLayout(this); + + int col = 0; + int row = 0; + + if (stickcnt) { + addLabel(tr("Mode"), row, 1); + addLabel(tr("Weight"), row, 2); + addLabel(tr("Source"), row++, 3); + + for (int i = 0; i < 4; i++, row++) { // TODO constant + col = 0; + addLabel(Boards::getAnalogInputName(board, i), row, col++); + + AutoComboBox *mode = new AutoComboBox(this); + mode->setModel(editorItemModels->getItemModel(modeid)); + mode->setField(generalSettings.trainer.mix[i].mode, this); + grid->addWidget(mode, row, col++); + + AutoSpinBox *weight = new AutoSpinBox(this); + weight->setRange(weightrng.min, weightrng.max); + weight->setField(generalSettings.trainer.mix[i].weight, this); + grid->addWidget(weight, row, col++); + + AutoComboBox *src = new AutoComboBox(this); + src->setModel(editorItemModels->getItemModel(srcid)); + src->setField(generalSettings.trainer.mix[i].src, this); + grid->addWidget(src, row, col++); + } + + addLine(row++, 0, grid->columnCount()); + } + + col = 0; + addLabel(tr("Multiplier"), row, col++); + + const FieldRange ppmmrng = GeneralSettings::getPPM_MultiplierRange(); + + AutoDoubleSpinBox *multi = new AutoDoubleSpinBox(this); + multi->setDecimals(ppmmrng.decimals); + multi->setOffset(ppmmrng.offset); + multi->setSingleStep(ppmmrng.step); + multi->setRange(ppmmrng.min, ppmmrng.max); + multi->setField(generalSettings.PPM_Multiplier, this); + grid->addWidget(multi, row++, col); + + col = 0; + + addLabel(tr("Calibration"), row, col++); + + for (int i = 0; i < stickcnt; i++, col++) { + AutoLineEdit *calib = new AutoLineEdit(this); + calib->setText(QString("%1").arg(generalSettings.trainer.calib[i])); + calib->setReadOnly(true); + grid->addWidget(calib, row, col); + } + + addHSpring(grid, grid->columnCount(), 0); + addVSpring(grid, 0, grid->rowCount()); + disableMouseScrolling(); } TrainerPanel::~TrainerPanel() { - delete ui; } -void TrainerPanel::on_trnMode_1_currentIndexChanged(int index) +void TrainerPanel::addLabel(QString text, int row, int col) { - generalSettings.trainer.mix[0].mode = index; - emit modified(); + QLabel *label = new QLabel(this); + label->setText(text); + grid->addWidget(label, row, col); } -void TrainerPanel::on_trnChn_1_currentIndexChanged(int index) +void TrainerPanel::addLine(int row, int col, int colspan) { - generalSettings.trainer.mix[0].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_1_editingFinished() -{ - generalSettings.trainer.mix[0].weight = ui->trnWeight_1->value(); - emit modified(); -} - -void TrainerPanel::on_trnMode_2_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[1].mode = index; - emit modified(); -} - -void TrainerPanel::on_trnChn_2_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[1].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_2_editingFinished() -{ - generalSettings.trainer.mix[1].weight = ui->trnWeight_2->value(); - emit modified(); -} - -void TrainerPanel::on_trnMode_3_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[2].mode = index; - emit modified(); -} - -void TrainerPanel::on_trnChn_3_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[2].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_3_editingFinished() -{ - generalSettings.trainer.mix[2].weight = ui->trnWeight_3->value(); - emit modified(); -} - -void TrainerPanel::on_trnMode_4_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[3].mode = index; - emit modified(); -} - -void TrainerPanel::on_trnChn_4_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[3].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_4_editingFinished() -{ - generalSettings.trainer.mix[3].weight = ui->trnWeight_4->value(); - emit modified(); + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + line->setLineWidth(1); + line->setMidLineWidth(0); + grid->addWidget(line, row, col, 1, colspan); } diff --git a/companion/src/generaledit/trainer.h b/companion/src/generaledit/trainer.h index 56442dc8b..e0c777af4 100644 --- a/companion/src/generaledit/trainer.h +++ b/companion/src/generaledit/trainer.h @@ -18,42 +18,23 @@ * GNU General Public License for more details. */ -#ifndef _TRAINER_H_ -#define _TRAINER_H_ +#pragma once #include "generaledit.h" #include "eeprominterface.h" -namespace Ui { - class Trainer; -} +class CompoundItemModelFactory; class TrainerPanel : public GeneralPanel { Q_OBJECT public: - TrainerPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware); + TrainerPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * editorItemModels); virtual ~TrainerPanel(); - private slots: - void on_trnMode_1_currentIndexChanged(int index); - void on_trnChn_1_currentIndexChanged(int index); - void on_trnWeight_1_editingFinished(); - void on_trnMode_2_currentIndexChanged(int index); - void on_trnChn_2_currentIndexChanged(int index); - void on_trnWeight_2_editingFinished(); - void on_trnMode_3_currentIndexChanged(int index); - void on_trnChn_3_currentIndexChanged(int index); - void on_trnWeight_3_editingFinished(); - void on_trnMode_4_currentIndexChanged(int index); - void on_trnChn_4_currentIndexChanged(int index); - void on_trnWeight_4_editingFinished(); - private: - Ui::Trainer *ui; - - void setValues(); + QGridLayout * grid; + void addLabel(QString text, int row, int col); + void addLine(int row, int col, int colspan); }; - -#endif // _TRAINER_H_ diff --git a/companion/src/generaledit/trainer.ui b/companion/src/generaledit/trainer.ui deleted file mode 100644 index 312999a0d..000000000 --- a/companion/src/generaledit/trainer.ui +++ /dev/null @@ -1,475 +0,0 @@ - - - Trainer - - - - 0 - 0 - 400 - 300 - - - - Form - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - Mode - - - - - - - Input - - - - - - - Weight - - - - - - - - 0 - 0 - - - - Rud - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - - 0 - 0 - - - - Ele - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - - 0 - 0 - - - - Thr - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - 0 - 0 - - - - Ail - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index c1cd7de79..5f749fa33 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -620,6 +620,17 @@ void TableLayout::setColumnWidth(int col, int width) #if defined(TABLE_LAYOUT) tableWidget->setColumnWidth(col, width); #else + gridWidget->setColumnMinimumWidth(col, width); +#endif +} + +void TableLayout::setColumnWidth(int col, QString str) +{ +#if defined(TABLE_LAYOUT) +#else + QFontMetrics *f = new QFontMetrics(QFont()); + QSize sz = f->size(Qt::TextSingleLine, str); + setColumnWidth(col, sz.width()); #endif } @@ -628,12 +639,19 @@ void TableLayout::pushRowsUp(int row) #if defined(TABLE_LAYOUT) #else // Push the rows up - QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ); + QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); gridWidget->addItem(spacer, row, 0); #endif - // Push rows upward - // addDoubleSpring(gridLayout, 5, num_fsw+1); +} +void TableLayout::pushColumnsLeft(int col) +{ +#if defined(TABLE_LAYOUT) +#else + // Push the columns to the left + QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + gridWidget->addItem(spacer, 0, col); +#endif } QString Helpers::getChecklistsPath() diff --git a/companion/src/helpers.h b/companion/src/helpers.h index 8bb1cc6b8..98783439b 100644 --- a/companion/src/helpers.h +++ b/companion/src/helpers.h @@ -191,7 +191,9 @@ public: void resizeColumnsToContents(); void setColumnWidth(int col, int width); + void setColumnWidth(int col, QString str); void pushRowsUp(int row); + void pushColumnsLeft(int col); private: #if defined(TABLE_LAYOUT) diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 7f8b80219..600f58e97 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -33,20 +33,20 @@ float curveLinear(float x, float coeff, float yMin, float yMid, float yMax) { - float a = (yMax-yMin) / 200.0; - return yMin + a * (x+100.0); + float a = (yMax - yMin) / 200.0; + return yMin + a * (x + 100.0); } float c9xexpou(float point, float coeff) { - float x = point*1024.0/100.0; - float k = coeff*256.0/100.0; - return ((k*x*x*x/(1024*1024) + x*(256-k) + 128) / 256) / 1024.0 * 100; + float x = point * 1024.0 / 100.0; + float k = coeff * 256.0 / 100.0; + return ((k * x * x * x / (1024 * 1024) + x * (256 - k) + 128) / 256) / 1024.0 * 100; } float curveExpo(float x, float coeff, float yMin, float yMid, float yMax) { - float a = (yMax-yMin) / 100.0; + float a = (yMax - yMin) / 100.0; x += 100.0; x /= 2.0; @@ -57,14 +57,14 @@ float curveExpo(float x, float coeff, float yMin, float yMid, float yMax) else { coeff = -coeff; x = 100 - x; - return round((100.0 - c9xexpou(x, coeff))*a + yMin); + return round((100.0 - c9xexpou(x, coeff)) * a + yMin); } } float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax) { bool invert; - if (x<0) { + if (x < 0) { x = -x; invert = 1; } @@ -74,12 +74,12 @@ float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax float y; if (coeff >= 0) { - y = round(c9xexpou(x, coeff) * (yMax/100.0)); + y = round(c9xexpou(x, coeff) * (yMax / 100.0)); } else { coeff = -coeff; x = 100.0 - x; - y = round((100.0-c9xexpou(x, coeff)) * (yMax/100.0)); + y = round((100.0 - c9xexpou(x, coeff)) * (yMax / 100.0)); } if (invert) { @@ -91,9 +91,9 @@ float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax) { - float a = (yMax-yMid) / 100.0; + float a = (yMax - yMid) / 100.0; - if (x<0) + if (x < 0) x = -x; float y; @@ -102,8 +102,8 @@ float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax } else { coeff = -coeff; - x = 100-x; - y = round((100.0-c9xexpou(x, coeff)) * a + yMid); + x = 100 - x; + y = round((100.0 - c9xexpou(x, coeff)) * a + yMid); } return y; @@ -148,17 +148,14 @@ CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & // The edit curve button QPushButton * edit = new QPushButton(this); edit->setProperty("index", i); - QPalette palette; - palette.setBrush(QPalette::Active, QPalette::Button, QBrush(colors[i])); - palette.setBrush(QPalette::Active, QPalette::ButtonText, QBrush(Qt::white)); -#ifdef __APPLE__ - edit->setStyleSheet(QString("color: %1;").arg(colors[i].name())); -#elif defined WIN32 || !defined __GNUC__ - edit->setStyleSheet(QString("background-color: %1; color: white;").arg(colors[i].name())); -#else + +#ifdef __GNUC__ + // creates more compact and likely consistent buttons across OS flavors edit->setStyleSheet(QString("background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;").arg(colors[i].name())); +#else + edit->setStyleSheet(QString("background-color: %1; color: white;").arg(colors[i].name())); #endif - edit->setPalette(palette); + edit->setText(tr("Curve %1").arg(i + 1)); edit->setContextMenuPolicy(Qt::CustomContextMenu); edit->setToolTip(tr("Popup menu available")); @@ -173,7 +170,6 @@ CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & // The curve plot checkbox QCheckBox * plot = new QCheckBox(this); plot->setProperty("index", i); - plot->setPalette(palette); connect(plot, SIGNAL(toggled(bool)), this, SLOT(plotCurve(bool))); if (i < limit) { ui->curvesLayout->addWidget(plot, i, 2, 1, 1); @@ -332,9 +328,15 @@ void CurvesPanel::updateCurve() int numpoints = model->curves[k].count; for (int i = 0; i < numpoints - 1; i++) { if (model->curves[k].type == CurveData::CURVE_TYPE_CUSTOM) - scene->addLine(centerX + (qreal)model->curves[k].points[i].x*width/200, centerY - (qreal)model->curves[k].points[i].y*height/200,centerX + (qreal)model->curves[k].points[i+1].x*width/200,centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen); + scene->addLine(centerX + (qreal)model->curves[k].points[i].x * width / 200, + centerY - (qreal)model->curves[k].points[i].y * height / 200, + centerX + (qreal)model->curves[k].points[i + 1].x * width / 200, + centerY - (qreal)model->curves[k].points[i + 1].y * height / 200, pen); else - scene->addLine(GFX_MARGIN + i*width/(numpoints-1), centerY - (qreal)model->curves[k].points[i].y*height/200,GFX_MARGIN + (i+1)*width/(numpoints-1),centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen); + scene->addLine(GFX_MARGIN + i * width / (numpoints - 1), + centerY - (qreal)model->curves[k].points[i].y * height / 200, + GFX_MARGIN + (i + 1) * width / (numpoints - 1), + centerY - (qreal)model->curves[k].points[i + 1].y * height / 200, pen); } } } @@ -356,11 +358,13 @@ void CurvesPanel::updateCurve() else { nodex->setFixedX(true); } - nodex->setPos(centerX + (qreal)model->curves[currentCurve].points[i].x * width / 200, centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); + nodex->setPos(centerX + (qreal)model->curves[currentCurve].points[i].x * width / 200, + centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); } else { nodex->setFixedX(true); - nodex->setPos(GFX_MARGIN + i * width / (numpoints - 1), centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); + nodex->setPos(GFX_MARGIN + i * width / (numpoints - 1), + centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); } connect(nodex, SIGNAL(moved(int, int)), this, SLOT(onNodeMoved(int, int))); connect(nodex, SIGNAL(focus()), this, SLOT(onNodeFocus())); @@ -592,7 +596,8 @@ void CurvesPanel::on_curveApply_clicked() } if (apply) { - model->curves[currentCurve].points[i].y = templates[index].function(x, ui->curveCoeff->value(), ui->yMin->value(), ui->yMid->value(), ui->yMax->value()); + model->curves[currentCurve].points[i].y = templates[index].function(x, ui->curveCoeff->value(), ui->yMin->value(), ui->yMid->value(), + ui->yMax->value()); } } diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 3a3a7e910..de6609591 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -182,6 +182,7 @@ void TimerPanel::onCountdownBeepChanged(int index) #define MASK_ACCESS (1<<13) #define MASK_RX_FREQ (1<<14) #define MASK_RF_POWER (1<<15) +#define MASK_RF_RACING_MODE (1<<16) quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; @@ -371,9 +372,9 @@ void ModulePanel::setupFailsafes() void ModulePanel::update() { - const PulsesProtocol protocol = (PulsesProtocol)module.protocol; - const Board::Type board = firmware->getBoard(); - const Multiprotocols::MultiProtocolDefinition & pdef = multiProtocols.getProtocol(module.multi.rfProtocol); + const auto protocol = (PulsesProtocol)module.protocol; + const auto board = firmware->getBoard(); + const auto & pdef = multiProtocols.getProtocol(module.multi.rfProtocol); unsigned int mask = 0; unsigned int max_rx_num = 63; @@ -406,6 +407,8 @@ void ModulePanel::update() mask |= MASK_RX_NUMBER | MASK_ACCESS; if (moduleIdx == 0 && HAS_EXTERNAL_ANTENNA(board) && generalSettings.antennaMode == 0 /* per model */) mask |= MASK_ANTENNA; + if (protocol == PULSES_ACCESS_ISRM && module.channelsCount == 8) + mask |= MASK_RF_RACING_MODE; break; case PULSES_LP45: case PULSES_DSM2: @@ -447,8 +450,6 @@ void ModulePanel::update() mask |= MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT | MASK_FAILSAFES; mask |= MASK_SUBTYPES | MASK_RX_FREQ | MASK_RF_POWER; break; - case PULSES_OFF: - break; default: break; } @@ -512,6 +513,14 @@ void ModulePanel::update() ui->antennaMode->hide(); } + if (mask & MASK_RF_RACING_MODE) { + ui->racingMode->show(); + ui->racingMode->setChecked(module.access.racingMode); + } + else { + ui->racingMode->hide(); + } + // R9M options ui->r9mPower->setVisible(mask & MASK_RF_POWER); ui->label_r9mPower->setVisible(mask & MASK_RF_POWER); @@ -803,6 +812,11 @@ void ModulePanel::on_disableChMap_stateChanged(int state) module.multi.disableMapping = (state == Qt::Checked); } +void ModulePanel::on_racingMode_stateChanged(int state) +{ + module.access.racingMode = (state == Qt::Checked); +} + void ModulePanel::on_autoBind_stateChanged(int state) { module.multi.autoBindMode = (state == Qt::Checked); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 82e69ec75..955f2189b 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -95,6 +95,7 @@ class ModulePanel : public ModelPanel void onSubTypeChanged(); void on_autoBind_stateChanged(int state); void on_disableChMap_stateChanged(int state); + void on_racingMode_stateChanged(int state); void on_disableTelem_stateChanged(int state); void on_lowPower_stateChanged(int state); void on_r9mPower_currentIndexChanged(int index); diff --git a/companion/src/modeledit/setup_module.ui b/companion/src/modeledit/setup_module.ui index 654af2a54..652975acd 100644 --- a/companion/src/modeledit/setup_module.ui +++ b/companion/src/modeledit/setup_module.ui @@ -1077,7 +1077,21 @@ - + + + + Disable Telemetry + + + + + + + Racing Mode + + + + Qt::Horizontal @@ -1090,13 +1104,6 @@ - - - - Disable Telemetry - - - diff --git a/companion/src/shared/CMakeLists.txt b/companion/src/shared/CMakeLists.txt index ffd5942bf..7bedeebf6 100644 --- a/companion/src/shared/CMakeLists.txt +++ b/companion/src/shared/CMakeLists.txt @@ -1,25 +1,33 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -set(shared_SRCS - genericpanel.cpp - hexspinbox.cpp - verticalscrollarea.cpp +set(shared_NAMES + genericpanel + verticalscrollarea + autobitsetcheckbox + autocheckbox + autocombobox + autodoublespinbox + autohexspinbox + autolabel + autolineedit + autoprecisioncombobox + autoslider + autospinbox + autotimeedit ) -set(shared_HDRS - autobitsetcheckbox.h - autocheckbox.h - autocombobox.h - autodoublespinbox.h - autohexspinbox.h - autolineedit.h - genericpanel.h - hexspinbox.h - autoprecisioncombobox.h - autotimeedit.h -) +foreach(name ${shared_NAMES}) + set(shared_SRCS ${shared_SRCS} ${name}.cpp) + set(shared_HDRS ${shared_HDRS} ${name}.h) +endforeach() qt5_wrap_cpp(shared_SRCS ${shared_HDRS}) +# add after moc generation to stop warnings +set(shared_SRCS ${shared_SRCS} + autowidget.h + autowidget.cpp +) + add_library(shared ${shared_SRCS}) target_link_libraries(shared PRIVATE Qt5::Widgets ${CPN_COMMON_LIB}) diff --git a/companion/src/shared/autobitsetcheckbox.cpp b/companion/src/shared/autobitsetcheckbox.cpp new file mode 100644 index 000000000..93faf35e5 --- /dev/null +++ b/companion/src/shared/autobitsetcheckbox.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autobitsetcheckbox.h" + +AutoBitsetCheckBox::AutoBitsetCheckBox(QWidget * parent) : + AutoBitsetCheckBox(QString(), parent) +{ +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + init(); +} + +// int field constructors +AutoBitsetCheckBox::AutoBitsetCheckBox(int & field, int bitmask, const QString & text, QWidget * parent) : + AutoBitsetCheckBox(field, bitmask, false, text, parent) +{ +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, invert); + init(); +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, false, toggleMask); + init(); +} + +// unsigned field constructors +AutoBitsetCheckBox::AutoBitsetCheckBox(unsigned & field, int bitmask, const QString & text, QWidget * parent) : + AutoBitsetCheckBox(field, bitmask, false, text, parent) +{ +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, invert); + init(); +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, false, toggleMask); + init(); +} + +AutoBitsetCheckBox::~AutoBitsetCheckBox() +{ +} + +void AutoBitsetCheckBox::setField(int & field, int bitmask, bool invert, int toggleMask) +{ + m_field = &field; + initField(bitmask, invert, toggleMask); +} + +void AutoBitsetCheckBox::setField(unsigned & field, int bitmask, bool invert, int toggleMask) +{ + m_field = (int *)&field; + initField(bitmask, invert, toggleMask); +} + +void AutoBitsetCheckBox::setBitmask(int bitmask) +{ + m_bitmask = bitmask; + updateValue(); +} + +void AutoBitsetCheckBox::setToggleMask(int toggleMask) +{ + m_toggleMask = toggleMask; + updateValue(); +} + +void AutoBitsetCheckBox::setInverted(bool invert) +{ + m_invert = invert; + updateValue(); +} + +void AutoBitsetCheckBox::updateValue() +{ + if (m_field) + setLock(true); + setChecked((m_invert && !m_toggleMask) != bool(*m_field & m_bitmask)); + setLock(false); +} + +void AutoBitsetCheckBox::onToggled(bool checked) +{ + if (!m_field || lock()) + return; + + if (m_toggleMask) { + if (checked) { + *m_field &= ~m_toggleMask; + *m_field |= m_bitmask; + } + else { + *m_field &= ~m_bitmask; + *m_field |= m_toggleMask; + } + } + else { + if (m_invert) + checked = !checked; + if (checked) + *m_field |= m_bitmask; + else + *m_field &= ~m_bitmask; + } + + emit currentDataChanged(checked); + dataChanged(); +} + +void AutoBitsetCheckBox::init() +{ + connect(this, &QCheckBox::toggled, this, &AutoBitsetCheckBox::onToggled); +} + +void AutoBitsetCheckBox::initField(int bitmask, bool invert, int toggleMask) +{ + m_bitmask = bitmask; + m_toggleMask = toggleMask; + m_invert = toggleMask ? false : invert; + updateValue(); +} diff --git a/companion/src/shared/autobitsetcheckbox.h b/companion/src/shared/autobitsetcheckbox.h index af831948e..cf54bae85 100644 --- a/companion/src/shared/autobitsetcheckbox.h +++ b/companion/src/shared/autobitsetcheckbox.h @@ -20,165 +20,50 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" -class AutoBitsetCheckBox: public QCheckBox +#include + +class AutoBitsetCheckBox: public QCheckBox, public AutoWidget { Q_OBJECT - //! \property bitmask The value (shifted bit(s)) to set/clear based on checked state. - Q_PROPERTY(int bitmask READ bitmask WRITE setBitmask) - //! \property inverted If \e true, then the \p bitmask is cleared when the checkbox is checked, and vice versa. Default behavior is to set the bitmask when checked. - Q_PROPERTY(bool inverted READ inverted WRITE setInverted) - //! \property toggleMask If > 0, then set this value when clearing the \p bitmask value (toggles between the two masks being set). \p inverted is ignored if \p toggleMask is set. - Q_PROPERTY(int toggleMask READ toggleMask WRITE setToggleMask) public: - explicit AutoBitsetCheckBox(QWidget *parent = nullptr) : - AutoBitsetCheckBox(QString(), parent) {} - explicit AutoBitsetCheckBox(const QString &text, QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - init(); - } + explicit AutoBitsetCheckBox(QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(const QString &text, QWidget * parent = nullptr); // int field constructors - explicit AutoBitsetCheckBox(int & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : - AutoBitsetCheckBox(field, bitmask, false, text, parent) {} - explicit AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, invert); - init(); - } - explicit AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, false, toggleMask); - init(); - } + explicit AutoBitsetCheckBox(int & field, int bitmask, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString & text = QString(), QWidget * parent = nullptr); // unsigned field constructors - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : - AutoBitsetCheckBox(field, bitmask, false, text, parent) {} - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, invert); - init(); - } - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, false, toggleMask); - init(); - } + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString & text = QString(), QWidget * parent = nullptr); - inline int bitmask() const { return m_bitmask; } - inline int toggleMask() const { return m_toggleMask; } - inline bool inverted() const { return m_toggleMask ? false : m_invert; } + virtual ~AutoBitsetCheckBox(); - public slots: - void setField(int & field, int bitmask = 0, bool invert = false, int toggleMask = 0) - { - m_field = &field; - initField(bitmask, invert, toggleMask); - } + virtual void updateValue() override; - void setField(unsigned & field, int bitmask = 0, bool invert = false, int toggleMask = 0) - { - m_field = reinterpret_cast(&field); - initField(bitmask, invert, toggleMask); - } + void setField(int & field, int bitmask = 0, bool invert = false, int toggleMask = 0); + void setField(unsigned & field, int bitmask = 0, bool invert = false, int toggleMask = 0); + void setBitmask(int bitmask); + void setToggleMask(int toggleMask); + void setInverted(bool invert); - void setBitmask(int bitmask) - { - m_bitmask = bitmask; - updateValue(); - } - - void setToggleMask(int toggleMask) - { - m_toggleMask = toggleMask; - updateValue(); - } - - void setInverted(bool invert) - { - m_invert = invert; - updateValue(); - } - - void updateValue() - { - if (m_panel && m_panel->lock) - return; - if (!m_field) - return; - const bool oldLock = setLocked(true); - setChecked((m_invert && !m_toggleMask) != bool(*m_field & m_bitmask)); - setLocked(oldLock); - } - - void setPanel(GenericPanel * panel) - { - m_panel = panel; - } + signals: + void currentDataChanged(bool checked); protected slots: - void init() - { - connect(this, &AutoBitsetCheckBox::toggled, this, &AutoBitsetCheckBox::onToggled); - } + void onToggled(bool checked); - void initField(int bitmask, bool invert, int toggleMask) - { - m_bitmask = bitmask; - m_toggleMask = toggleMask; - m_invert = toggleMask ? false : invert; - updateValue(); - } - - void onToggled(bool checked) - { - if (!m_field) - return; - - if (m_toggleMask) { - if (checked) { - *m_field &= ~m_toggleMask; - *m_field |= m_bitmask; - } - else { - *m_field &= ~m_bitmask; - *m_field |= m_toggleMask; - } - } - else { - if (m_invert) - checked = !checked; - if (checked) - *m_field |= m_bitmask; - else - *m_field &= ~m_bitmask; - } - - if (m_panel && !m_lock) - emit m_panel->modified(); - } - - bool setLocked(bool lock) - { - const bool ret = m_lock; - m_lock = lock; - return ret; - } - - protected: - int * m_field = nullptr; + private: + int *m_field = nullptr; int m_bitmask = 0; int m_toggleMask = 0; bool m_invert = false; - bool m_lock = false; - GenericPanel * m_panel = nullptr; + + void init(); + void initField(int bitmask, bool invert, int toggleMask); }; diff --git a/companion/src/shared/autocheckbox.cpp b/companion/src/shared/autocheckbox.cpp new file mode 100644 index 000000000..a2af1395c --- /dev/null +++ b/companion/src/shared/autocheckbox.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autocheckbox.h" + +AutoCheckBox::AutoCheckBox(QWidget * parent): + QCheckBox(parent), + AutoWidget(), + m_field(nullptr), + m_invert(false) +{ + connect(this, &QCheckBox::toggled, this, &AutoCheckBox::onToggled); +} + +AutoCheckBox::~AutoCheckBox() +{ +} + +void AutoCheckBox::setField(bool & field, GenericPanel * panel, bool invert) +{ + m_field = &field; + m_invert = invert; + setPanel(panel); + updateValue(); +} + +void AutoCheckBox::setInvert(bool invert) +{ + m_invert = invert; + updateValue(); +} + +void AutoCheckBox::onToggled(bool checked) +{ + if (m_field && !lock()) { + const bool val = m_invert ? !checked : checked; + *m_field = val; + emit currentDataChanged(val); + dataChanged(); + } +} + +void AutoCheckBox::updateValue() +{ + if (m_field) { + setLock(true); + setChecked(m_invert ? !(*m_field) : *m_field); + setLock(false); + } +} diff --git a/companion/src/shared/autocheckbox.h b/companion/src/shared/autocheckbox.h index 1c1a7207c..5eb0a3dfb 100644 --- a/companion/src/shared/autocheckbox.h +++ b/companion/src/shared/autocheckbox.h @@ -20,62 +20,30 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" -class AutoCheckBox: public QCheckBox +#include + +class AutoCheckBox : public QCheckBox, public AutoWidget { Q_OBJECT public: - explicit AutoCheckBox(QWidget * parent = nullptr): - QCheckBox(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(toggled(bool)), this, SLOT(onToggled(bool))); - } + explicit AutoCheckBox(QWidget * parent = nullptr); + virtual ~AutoCheckBox(); - void setField(bool & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + virtual void updateValue() override; - void setEnabled(bool enabled) - { - QCheckBox::setEnabled(enabled); - } - - void updateValue() - { - if (field) { - lock = true; - setChecked(*field); - lock = false; - } - } + void setField(bool & field, GenericPanel * panel = nullptr, bool invert = false); + void setInvert(bool invert); signals: void currentDataChanged(bool value); protected slots: - void onToggled(bool checked) - { - if (panel && panel->lock) - return; - if (field && !lock) { - *field = checked; - emit currentDataChanged(checked); - if (panel) - emit panel->modified(); - } - } + void onToggled(bool checked); - protected: - bool *field = nullptr; - GenericPanel *panel = nullptr; - bool lock = false; + private: + bool *m_field; + bool m_invert; }; diff --git a/companion/src/shared/autocombobox.cpp b/companion/src/shared/autocombobox.cpp new file mode 100644 index 000000000..a14e08f2e --- /dev/null +++ b/companion/src/shared/autocombobox.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autocombobox.h" + +AutoComboBox::AutoComboBox(QWidget * parent): + QComboBox(parent), + AutoWidget(), + m_field(nullptr), + m_next(0), + m_hasModel(false), + m_rawSource(nullptr), + m_rawSwitch(nullptr) +{ + connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); +} + +AutoComboBox::~AutoComboBox() +{ +} + +void AutoComboBox::clear() +{ + if (m_hasModel) + return; + + setLock(true); + QComboBox::clear(); + m_next = 0; + setLock(false); +} + +void AutoComboBox::insertItems(int index, const QStringList & items) +{ + if (m_hasModel) + return; + + foreach(QString item, items) { + addItem(item); + } +} + +void AutoComboBox::addItem(const QString & item) +{ + if (m_hasModel) + return; + + addItem(item, m_next++); +} + +void AutoComboBox::addItem(const QString & item, int value) +{ + if (m_hasModel) + return; + + setLock(true); + QComboBox::addItem(item, value); + setLock(false); + updateValue(); +} + +void AutoComboBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = (int *)&field; + m_rawSource = nullptr; + m_rawSwitch = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setField(int & field, GenericPanel * panel) +{ + m_field = &field; + m_rawSource = nullptr; + m_rawSwitch = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setField(RawSource & field, GenericPanel * panel) +{ + m_rawSource = &field; + m_rawSwitch = nullptr; + m_field = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setField(RawSwitch & field, GenericPanel * panel) +{ + m_rawSwitch = &field; + m_rawSource = nullptr; + m_field = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setFieldInit(GenericPanel * panel) +{ + setPanel(panel); + updateValue(); +} + +void AutoComboBox::setModel(QAbstractItemModel * model) +{ + setLock(true); + QComboBox::setModel(model); + setLock(false); + m_hasModel = true; + updateValue(); +} + +void AutoComboBox::setAutoIndexes() +{ + if (m_hasModel) + return; + + for (int i = 0; i < count(); ++i) + setItemData(i, i); + updateValue(); +} + +void AutoComboBox::updateValue() +{ + if (!m_field && !m_rawSource && !m_rawSwitch) + return; + + setLock(true); + + if (m_field) + setCurrentIndex(findData(*m_field)); + else if (m_rawSource) + setCurrentIndex(findData(m_rawSource->toValue())); + else if (m_rawSwitch) + setCurrentIndex(findData(m_rawSwitch->toValue())); + + setLock(false); +} + +void AutoComboBox::onCurrentIndexChanged(int index) +{ + if (lock() || index < 0) + return; + + bool ok; + const int val = itemData(index).toInt(&ok); + if (!ok) + return; + + if (m_field && *m_field != val) { + *m_field = val; + } + else if (m_rawSource && m_rawSource->toValue() != val) { + *m_rawSource = RawSource(val); + } + else if (m_rawSwitch && m_rawSwitch->toValue() != val) { + *m_rawSwitch = RawSwitch(val); + } + else + return; + + emit currentDataChanged(val); + dataChanged(); +} diff --git a/companion/src/shared/autocombobox.h b/companion/src/shared/autocombobox.h index ca6054555..1502a6176 100644 --- a/companion/src/shared/autocombobox.h +++ b/companion/src/shared/autocombobox.h @@ -20,174 +20,49 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" #include "rawsource.h" #include "rawswitch.h" -class AutoComboBox: public QComboBox +#include + +class AutoComboBox : public QComboBox, public AutoWidget { Q_OBJECT public: - explicit AutoComboBox(QWidget * parent = nullptr): - QComboBox(parent), - field(nullptr), - panel(nullptr), - next(0), - lock(false), - hasModel(false), - rawSource(nullptr), - rawSwitch(nullptr) - { - connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); - } + explicit AutoComboBox(QWidget * parent = nullptr); + virtual ~AutoComboBox(); - void clear() - { - if (!hasModel) { - lock = true; - QComboBox::clear(); - next = 0; - lock = false; - } - } + // QComboBox + virtual void addItem(const QString & item); + virtual void addItem(const QString & item, int value); + virtual void insertItems(int index, const QStringList & items); + // AutoWidget + virtual void updateValue() override; - virtual void insertItems(int index, const QStringList & items) - { - if (!hasModel) { - foreach(QString item, items) { - addItem(item); - } - } - } + void clear(); - virtual void addItem(const QString & item) - { - if (!hasModel) - addItem(item, next++); - } + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setField(int & field, GenericPanel * panel = nullptr); + void setField(RawSource & field, GenericPanel * panel = nullptr); + void setField(RawSwitch & field, GenericPanel * panel = nullptr); - virtual void addItem(const QString & item, int value) - { - if (!hasModel) { - lock = true; - QComboBox::addItem(item, value); - lock = false; - updateValue(); - } - } - - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = (int *)&field; - this->rawSource = nullptr; - this->rawSwitch = nullptr; - this->panel = panel; - updateValue(); - } - - void setField(int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->rawSource = nullptr; - this->rawSwitch = nullptr; - this->panel = panel; - updateValue(); - } - - void setField(RawSource & field, GenericPanel * panel = nullptr) - { - this->rawSource = &field; - this->rawSwitch = nullptr; - this->field = nullptr; - this->panel = panel; - updateValue(); - } - - void setField(RawSwitch & field, GenericPanel * panel = nullptr) - { - this->rawSwitch = &field; - this->rawSource = nullptr; - this->field = nullptr; - this->panel = panel; - updateValue(); - } - - void setModel(QAbstractItemModel * model) - { - lock = true; - QComboBox::setModel(model); - lock = false; - hasModel = true; - updateValue(); - } - - void setAutoIndexes() - { - if (!hasModel) { - for (int i = 0; i < count(); ++i) - setItemData(i, i); - updateValue(); - } - } - - void updateValue() - { - if (!field && !rawSource && !rawSwitch) - return; - - lock = true; - - if (field) - setCurrentIndex(findData(*field)); - else if (rawSource) - setCurrentIndex(findData(rawSource->toValue())); - else if (rawSwitch) - setCurrentIndex(findData(rawSwitch->toValue())); - - lock = false; - } + void setAutoIndexes(); + void setModel(QAbstractItemModel * model); signals: void currentDataChanged(int value); protected slots: - void onCurrentIndexChanged(int index) - { - if (panel && panel->lock) - return; - if (lock || index < 0) - return; + void onCurrentIndexChanged(int index); - bool ok; - const int val = itemData(index).toInt(&ok); - if (!ok) - return; + private: + int *m_field; + int m_next; + bool m_hasModel; + RawSource *m_rawSource; + RawSwitch *m_rawSwitch; - if (field && *field != val) { - *field = val; - } - else if (rawSource && rawSource->toValue() != val) { - *rawSource = RawSource(val); - } - else if (rawSwitch && rawSwitch->toValue() != val) { - *rawSwitch = RawSwitch(val); - } - else - return; - - emit currentDataChanged(val); - if (panel) - emit panel->modified(); - } - - protected: - int *field = nullptr; - GenericPanel *panel = nullptr; - int next = 0; - bool lock = false; - bool hasModel = false; - RawSource *rawSource = nullptr; - RawSwitch *rawSwitch = nullptr; + void setFieldInit(GenericPanel * panel); }; diff --git a/companion/src/shared/autodoublespinbox.cpp b/companion/src/shared/autodoublespinbox.cpp new file mode 100644 index 000000000..a5590035f --- /dev/null +++ b/companion/src/shared/autodoublespinbox.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autodoublespinbox.h" + +AutoDoubleSpinBox::AutoDoubleSpinBox(QWidget * parent): + QDoubleSpinBox(parent), + AutoWidget(), + m_field(nullptr), + m_offset(0) +{ + connect(this, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double))); +} + +AutoDoubleSpinBox::~AutoDoubleSpinBox() +{ +} + +void AutoDoubleSpinBox::setField(int & field, GenericPanel * panel) +{ + m_field = &field; + setPanel(panel); + updateValue(); +} + +void AutoDoubleSpinBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = (int *)&field; + setPanel(panel); + updateValue(); +} + +void AutoDoubleSpinBox::setDecimals(int prec) +{ + QDoubleSpinBox::setDecimals(prec); + updateValue(); +} + +void AutoDoubleSpinBox::setOffset(int offset) +{ + m_offset = offset; + updateValue(); +} + +void AutoDoubleSpinBox::updateValue() +{ + if (m_field) { + setLock(true); + setValue(float(*m_field + m_offset) / multiplier()); + setLock(false); + } +} + +int AutoDoubleSpinBox::multiplier() +{ + switch (decimals()) { + case 1: + return 10; + case 2: + return 100; + case 3: + return 1000; + default: + return 1; + } + } + +void AutoDoubleSpinBox::onValueChanged(double value) +{ + if (m_field && !lock()) { + *m_field = round(value * multiplier() - m_offset); + emit currentDataChanged(*m_field); + dataChanged(); + } +} diff --git a/companion/src/shared/autodoublespinbox.h b/companion/src/shared/autodoublespinbox.h index a6466269e..31eef93c4 100644 --- a/companion/src/shared/autodoublespinbox.h +++ b/companion/src/shared/autodoublespinbox.h @@ -20,87 +20,38 @@ #pragma once +#include "autowidget.h" + #include -#include "genericpanel.h" + #if __GNUC__ #include #endif -class AutoDoubleSpinBox: public QDoubleSpinBox +class AutoDoubleSpinBox : public QDoubleSpinBox, public AutoWidget { Q_OBJECT public: - explicit AutoDoubleSpinBox(QWidget * parent = nullptr): - QDoubleSpinBox(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double))); - } + explicit AutoDoubleSpinBox(QWidget * parent = nullptr); + virtual ~AutoDoubleSpinBox(); - void setField(int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + virtual void updateValue() override; - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = (int *)&field; - this->panel = panel; - updateValue(); - } - - void updateValue() - { - if (field) { - lock = true; - setValue(float(*field) / multiplier()); - lock = false; - } - } - - void setDecimals(int prec) - { - QDoubleSpinBox::setDecimals(prec); - updateValue(); - } - - protected: - int multiplier() - { - switch (decimals()) { - case 1: - return 10; - case 2: - return 100; - default: - return 1; - } - } + void setField(int & field, GenericPanel * panel = nullptr); + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setDecimals(int prec); + void setOffset(int offset); signals: void currentDataChanged(double value); protected slots: - void onValueChanged(double value) - { - if (panel && panel->lock) - return; - if (field && !lock) { - *field = round(value * multiplier()); - emit currentDataChanged(value); - if (panel) { - emit panel->modified(); - } - } - } + void onValueChanged(double value); - protected: - int * field = nullptr; - GenericPanel * panel = nullptr; - bool lock = false; + private: + int *m_field; + int m_offset; + + int multiplier(); }; diff --git a/companion/src/shared/autohexspinbox.cpp b/companion/src/shared/autohexspinbox.cpp new file mode 100644 index 000000000..3096a1c2f --- /dev/null +++ b/companion/src/shared/autohexspinbox.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autohexspinbox.h" + +AutoHexSpinBox::AutoHexSpinBox(QWidget * parent): + QSpinBox(parent), + AutoWidget(), + m_field(nullptr) +{ + setRange(0, AUTOHEXSPINBOX_MAX_VALUE); + connect(this, QOverload::of(&QSpinBox::valueChanged), this, &AutoHexSpinBox::onValueChanged); +} + +AutoHexSpinBox::~AutoHexSpinBox() +{ +} + +void AutoHexSpinBox::setField(unsigned int & field, const unsigned int min, const unsigned int max, GenericPanel * panel) +{ + m_field = &field; + setRange(min, max); + setPanel(panel); + updateValue(); +} + +void AutoHexSpinBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = &field; + setRange(0, AUTOHEXSPINBOX_MAX_VALUE); + setPanel(panel); + updateValue(); +} + +void AutoHexSpinBox::setRange(const unsigned int min, const unsigned int max) +{ + QSpinBox::setRange(min, max); + + m_length = QString("%1").arg(max, 0, 16).size(); + + m_validator = new QRegExpValidator(QRegExp(QString("[0-9A-Fa-f]{1,%1}").arg(m_length)), this); +} + +void AutoHexSpinBox::updateValue() +{ + if (m_field) { + setLock(true); + setValue(*m_field); + setLock(false); + } +} + +void AutoHexSpinBox::onValueChanged(int value) +{ + if (m_field && !lock()) { + if (*m_field != (unsigned)value) { + *m_field = (unsigned)value; + emit currentDataChanged(value); + dataChanged(); + } + } +} + +// reimplemented +QValidator::State AutoHexSpinBox::validate(QString &text, int &pos) const +{ + return m_validator->validate(text, pos); +} + +int AutoHexSpinBox::valueFromText(const QString &text) const +{ + return text.toInt(0, 16); +} + +QString AutoHexSpinBox::textFromValue(int value) const +{ + return QString("%1").arg(value, m_length, 16, QChar('0')).toUpper(); +} diff --git a/companion/src/shared/autohexspinbox.h b/companion/src/shared/autohexspinbox.h index 40cc24da4..6d7711c32 100644 --- a/companion/src/shared/autohexspinbox.h +++ b/companion/src/shared/autohexspinbox.h @@ -20,58 +20,42 @@ #pragma once -#include "hexspinbox.h" -#include "genericpanel.h" +#include "autowidget.h" -class AutoHexSpinBox: public HexSpinBox +#include +#include + +class QRegExpValidator; + +class AutoHexSpinBox : public QSpinBox, public AutoWidget { Q_OBJECT public: - explicit AutoHexSpinBox(QWidget * parent = nullptr): - HexSpinBox(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); - } + constexpr static unsigned int AUTOHEXSPINBOX_MAX_VALUE {65535}; - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + explicit AutoHexSpinBox(QWidget * parent = nullptr); + virtual ~AutoHexSpinBox(); - void updateValue() - { - if (field) { - lock = true; - setValue(*field); - lock = false; - } - } + virtual void updateValue(); + + void setField(unsigned int & field, const unsigned int min = 0, const unsigned int max = AUTOHEXSPINBOX_MAX_VALUE, GenericPanel * panel = nullptr); + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setRange(unsigned int min = 0, unsigned int max = AUTOHEXSPINBOX_MAX_VALUE); + + protected: + QValidator::State validate(QString &text, int &pos) const; + int valueFromText(const QString &text) const; + QString textFromValue(int value) const; signals: void currentDataChanged(int value); protected slots: - void onValueChanged(int value) - { - if (panel && panel->lock) - return; - if (field && !lock) { - *field = value; - emit currentDataChanged(value); - if (panel) { - emit panel->modified(); - } - } - } + void onValueChanged(int value); - protected: - unsigned int * field = nullptr; - GenericPanel * panel = nullptr; - bool lock = false; + private: + unsigned int *m_field; + QRegExpValidator *m_validator; + unsigned int m_length; }; diff --git a/companion/src/shared/autolabel.cpp b/companion/src/shared/autolabel.cpp new file mode 100644 index 000000000..61099a9a7 --- /dev/null +++ b/companion/src/shared/autolabel.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autolabel.h" + +#include + +AutoLabel::AutoLabel(QWidget * parent): + QLabel(parent), + AutoWidget(), + m_charField(NULL), + m_strField(nullptr) +{ +} + +AutoLabel::~AutoLabel() +{ +} + +void AutoLabel::setField(char * field, GenericPanel * panel) +{ + m_charField = field; + setPanel(panel); + updateValue(); +} + +void AutoLabel::setField(QString & field, GenericPanel * panel) +{ + m_strField = &field; + setPanel(panel); + updateValue(); +} + +void AutoLabel::setWidth(int numChars) +{ + QFontMetrics *f = new QFontMetrics(this->font()); + QSize sz = f->size(Qt::TextSingleLine, QString(numChars, QChar('X'))); + setMinimumWidth(sz.width()); + delete f; +} + +void AutoLabel::updateValue() +{ + setLock(true); + if (m_strField) + setText(*m_strField); + else if (m_charField) + setText(m_charField); + setLock(false); +} diff --git a/companion/src/shared/autolabel.h b/companion/src/shared/autolabel.h new file mode 100644 index 000000000..369a1e680 --- /dev/null +++ b/companion/src/shared/autolabel.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autowidget.h" + +#include + +class AutoLabel: public QLabel, public AutoWidget +{ + Q_OBJECT + + public: + explicit AutoLabel(QWidget * parent = nullptr); + virtual ~AutoLabel(); + + virtual void updateValue() override; + + void setField(char * field, GenericPanel * panel = nullptr); + void setField(QString & field, GenericPanel * panel = nullptr); + void setWidth(int numChars); + + private: + char *m_charField; + QString *m_strField; +}; diff --git a/companion/src/shared/autolineedit.cpp b/companion/src/shared/autolineedit.cpp new file mode 100644 index 000000000..79e92d482 --- /dev/null +++ b/companion/src/shared/autolineedit.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autolineedit.h" + +AutoLineEdit::AutoLineEdit(QWidget * parent, bool updateOnChange): + QLineEdit(parent), + AutoWidget(), + m_charField(NULL), + m_strField(nullptr) +{ + if (updateOnChange) + connect(this, &QLineEdit::textChanged, this, &AutoLineEdit::onEdited); + else + connect(this, &QLineEdit::editingFinished, this, &AutoLineEdit::onEdited); +} + +AutoLineEdit::~AutoLineEdit() +{ +} + +void AutoLineEdit::setField(char * field, int len, GenericPanel * panel) +{ + m_charField = field; + setPanel(panel); + setValidator(new QRegExpValidator(QRegExp("[ A-Za-z0-9_.-,]*"), this)); + if (len) + setMaxLength(len); + updateValue(); +} + +void AutoLineEdit::setField(QString & field, int len, GenericPanel * panel) +{ + m_strField = &field; + if (len) + setMaxLength(len); + setPanel(panel); + updateValue(); +} + +void AutoLineEdit::updateValue() +{ + setLock(true); + if (m_strField) + setText(*m_strField); + else if (m_charField) + setText(m_charField); + setLock(false); +} + +void AutoLineEdit::onEdited() +{ + if (lock()) + return; + + if (m_strField && *m_strField != text()) + *m_strField = text(); + else if (m_charField && strcmp(m_charField, text().toLatin1())) + strcpy(m_charField, text().toLatin1()); + else + return; + + emit currentDataChanged(); + dataChanged(); +} diff --git a/companion/src/shared/autolineedit.h b/companion/src/shared/autolineedit.h index b2815796c..be1cccaa3 100644 --- a/companion/src/shared/autolineedit.h +++ b/companion/src/shared/autolineedit.h @@ -20,83 +20,31 @@ #pragma once +#include "autowidget.h" + #include #include -#include "genericpanel.h" -class AutoLineEdit: public QLineEdit +class AutoLineEdit: public QLineEdit, public AutoWidget { Q_OBJECT public: - explicit AutoLineEdit(QWidget * parent = nullptr, bool updateOnChange = false): - QLineEdit(parent), - field(NULL), - strField(nullptr), - panel(nullptr), - lock(false) - { - if (updateOnChange) - connect(this, &AutoLineEdit::textChanged, this, &AutoLineEdit::onEdited); - else - connect(this, &AutoLineEdit::editingFinished, this, &AutoLineEdit::onEdited); - } + explicit AutoLineEdit(QWidget * parent = nullptr, bool updateOnChange = false); + virtual ~AutoLineEdit(); - void setField(char * field, int len, GenericPanel * panel = nullptr) - { - this->field = field; - this->panel = panel; - setValidator(new QRegExpValidator(QRegExp("[ A-Za-z0-9_.-,]*"), this)); - setMaxLength(len); - updateValue(); - } + virtual void updateValue() override; - void setField(QString & field, int len = 0, GenericPanel * panel = nullptr) - { - strField = &field; - this->panel = panel; - if (len) - setMaxLength(len); - updateValue(); - } - - void updateValue() - { - lock = true; - if (field) - setText(field); - else if (strField) - setText(*strField); - lock = false; - } + void setField(char * field, int len, GenericPanel * panel = nullptr); + void setField(QString & field, int len = 0, GenericPanel * panel = nullptr); signals: void currentDataChanged(); protected slots: - void onEdited() - { - if (panel && panel->lock) - return; - if (lock) - return; + void onEdited(); - if (field) - strcpy(field, text().toLatin1()); - else if (strField) - *strField = text(); - else - return; - - emit currentDataChanged(); - - if (panel) - emit panel->modified(); - } - - protected: - char * field; - QString * strField = nullptr; - GenericPanel * panel = nullptr; - bool lock = false; + private: + char *m_charField; + QString *m_strField; }; diff --git a/companion/src/shared/autoprecisioncombobox.cpp b/companion/src/shared/autoprecisioncombobox.cpp new file mode 100644 index 000000000..6add0ff92 --- /dev/null +++ b/companion/src/shared/autoprecisioncombobox.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autoprecisioncombobox.h" + +constexpr unsigned int APCB_DECIMALS_MIN {0}; +constexpr unsigned int APCB_DECIMALS_MAX {6}; + +AutoPrecisionComboBox::AutoPrecisionComboBox(QWidget * parent): + QComboBox(parent), + AutoWidget() +{ + setFieldInit(); + init(); +} + +AutoPrecisionComboBox::AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, QWidget * parent, GenericPanel * panel): + QComboBox(parent), + AutoWidget() +{ + m_field = &field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + init(); +} + +AutoPrecisionComboBox::AutoPrecisionComboBox(int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, QWidget * parent, GenericPanel * panel): + QComboBox(parent), + AutoWidget() +{ + m_field = (unsigned int *)&field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + init(); +} + +AutoPrecisionComboBox::~AutoPrecisionComboBox() +{ +} + +void AutoPrecisionComboBox::setField(int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, GenericPanel * panel) +{ + m_field = (unsigned int *)&field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + setPanel(panel); +} + +void AutoPrecisionComboBox::setField(unsigned int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, GenericPanel * panel) +{ + m_field = &field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + setPanel(panel); +} + +void AutoPrecisionComboBox::setMinDecimals(unsigned int minDecimals) +{ + m_minDecimals = rangecheckDecimals(minDecimals); + if (m_minDecimals > m_maxDecimals) + m_minDecimals = m_maxDecimals; + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setMaxDecimals(unsigned int maxDecimals) +{ + m_maxDecimals = rangecheckDecimals(maxDecimals); + if (m_maxDecimals < m_minDecimals) + m_maxDecimals = m_minDecimals; + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setDecimalsRange(unsigned int min, unsigned int max) +{ + setMinDecimals(min); + setMaxDecimals(max); +} + +void AutoPrecisionComboBox::setPadding(bool padding) +{ + m_padding = padding; + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setSuffix(QString suffix) +{ + m_suffix = suffix.trimmed(); + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setValue(int value) +{ + if (!m_field) + return; + + const unsigned int val = (unsigned int)value; + if (*m_field != val) { + *m_field = rangecheckDecimals(val); + updateValue(); + emit currentDataChanged(value); + dataChanged(); + } +} + +void AutoPrecisionComboBox::setValue(unsigned int value) +{ + if (!m_field) + return; + + if (*m_field != value) { + *m_field = rangecheckDecimals(value); + updateValue(); + emit currentDataChanged((int)value); + dataChanged(); + } +} + +void AutoPrecisionComboBox::updateValue() +{ + if (!m_field) + return; + + if (!isValidDecimals(*m_field)) + return; + + if (*m_field > m_maxDecimals) + setMaxDecimals(*m_field); + + setLock(true); + setCurrentIndex(findData(*m_field)); + setLock(false); +} + +void AutoPrecisionComboBox::setAutoIndexes() +{ + for (int i = 0; i < count(); ++i) + setItemData(i, i); + updateValue(); +} + +void AutoPrecisionComboBox::init() +{ + connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &AutoPrecisionComboBox::onCurrentIndexChanged); +} + +void AutoPrecisionComboBox::setFieldInit(unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, GenericPanel * panel) +{ + setLock(true); + setMinDecimals(minDecimals); + setMaxDecimals(maxDecimals); + setPadding(padding); + setSuffix(suffix); + setLock(false); + setPanel(panel); + updateList(); +} + +void AutoPrecisionComboBox::updateList() +{ + setLock(true); + unsigned int i; + int j; + clear(); + + for (i = APCB_DECIMALS_MIN, j = (int)m_maxDecimals; j >= 0; i++, j--) { + if (i >= m_minDecimals) { + QString str = QString("0.%1").arg(QString(i, '0')); + if (m_padding) + str.append(QString(j, '_')); + if (m_suffix != "") + str.append(QString(" %1").arg(m_suffix)); + addItem(str, i); + } + } + + setSizeAdjustPolicy(QComboBox::AdjustToContents); + setLock(false); +} + +void AutoPrecisionComboBox::onCurrentIndexChanged(int index) +{ + if (!m_field || index < 0 || lock()) + return; + + if (*m_field != itemData(index).toUInt()) { + *m_field = itemData(index).toUInt(); + emit currentDataChanged(index); + dataChanged(); + } +} + +unsigned int AutoPrecisionComboBox::rangecheckDecimals(unsigned int decimals) +{ + unsigned int ret; + if (decimals < APCB_DECIMALS_MIN) + ret = APCB_DECIMALS_MIN; + else if (decimals > APCB_DECIMALS_MAX) + ret = APCB_DECIMALS_MAX; + else + ret = decimals; + return ret; +} + +bool AutoPrecisionComboBox::isValidDecimals(unsigned int value) +{ + if (value >= APCB_DECIMALS_MIN && value <= APCB_DECIMALS_MAX) + return true; + else + return false; +} diff --git a/companion/src/shared/autoprecisioncombobox.h b/companion/src/shared/autoprecisioncombobox.h index 72dd89406..b973c09fd 100644 --- a/companion/src/shared/autoprecisioncombobox.h +++ b/companion/src/shared/autoprecisioncombobox.h @@ -20,235 +20,50 @@ #pragma once -#include "genericpanel.h" +#include "autowidget.h" #include #include -class AutoPrecisionComboBox: public QComboBox +class AutoPrecisionComboBox : public QComboBox, public AutoWidget { - Q_OBJECT - //! \property minDecimals Determines the first list item - Q_PROPERTY(unsigned int minDecimals READ minDecimals WRITE setMinDecimals) - //! \property maxDecimals Determines the last list item - Q_PROPERTY(unsigned int maxDecimals READ maxDecimals WRITE setMaxDecimals) - //! \property padding If true, add trailing underscore placeholders to each list item - Q_PROPERTY(bool padding READ padding WRITE setPadding) - //! \property suffix Optional text suffix to each list value - Q_PROPERTY(QString suffix READ suffix WRITE setSuffix) + Q_OBJECT public: - explicit AutoPrecisionComboBox(QWidget * parent = nullptr): - QComboBox(parent) - { - init(); - } + explicit AutoPrecisionComboBox(QWidget * parent = nullptr); + explicit AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr); + explicit AutoPrecisionComboBox(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr); + virtual ~AutoPrecisionComboBox(); - explicit AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr): - QComboBox(parent) - { - setField(field, minDecimals, maxDecimals, padding, suffix, panel); - init(); - } + virtual void updateValue() override; - explicit AutoPrecisionComboBox(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr): - QComboBox(parent) - { - setField(field, minDecimals, maxDecimals, padding, suffix, panel); - init(); - } - - inline int minDecimals() const { return m_minDecimals; } - inline int maxDecimals() const { return m_maxDecimals; } - inline bool padding() const { return m_padding; } - inline QString suffix() const { return m_suffix; } - - public slots: - void setField(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr) - { - m_field = reinterpret_cast(&field); - initField(minDecimals, maxDecimals, padding, suffix); - setPanel(panel); - } - - void setField(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr) - { - m_field = &field; - initField(minDecimals, maxDecimals, padding, suffix); - setPanel(panel); - } - - void setMinDecimals(unsigned int minDecimals) - { - m_minDecimals = rangecheckDecimals(minDecimals); - if (m_minDecimals > m_maxDecimals) - m_minDecimals = m_maxDecimals; - if (!m_lock) - updateList(); - } - - void setMaxDecimals(unsigned int maxDecimals) - { - m_maxDecimals = rangecheckDecimals(maxDecimals); - if (m_maxDecimals < m_minDecimals) - m_maxDecimals = m_minDecimals; - if (!m_lock) - updateList(); - } - - void setPadding(bool padding) - { - m_padding = padding; - if (!m_lock) - updateList(); - } - - void setSuffix(QString suffix) - { - m_suffix = suffix.trimmed(); - if (!m_lock) - updateList(); - } - - void setPanel(GenericPanel * panel) - { - m_panel = panel; - } - - void setValue(int value) - { - if (!m_field) - return; - const unsigned int val = (unsigned int)value; - if (*m_field != val) { - *m_field = rangecheckDecimals(val); - updateValue(); - emit currentDataChanged(value); - } - } - - void setValue(unsigned int value) - { - if (!m_field) - return; - if (*m_field != value) { - *m_field = rangecheckDecimals(value); - updateValue(); - emit currentDataChanged((int)value); - } - } - - void updateValue() - { - if (!m_field) - return; - if (!isValidDecimals(*m_field)) - return; - if (*m_field > m_maxDecimals) { - setMaxDecimals(*m_field); - } - const bool savedlock = setLocked(true); - setCurrentIndex(findData(*m_field)); - setLocked(savedlock); - } - - void setAutoIndexes() - { - for (int i = 0; i < count(); ++i) - setItemData(i, i); - updateValue(); - } + void setField(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr); + void setField(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr); + void setMinDecimals(unsigned int minDecimals); + void setMaxDecimals(unsigned int maxDecimals); + void setDecimalsRange(unsigned int min, unsigned int max); + void setPadding(bool padding); + void setSuffix(QString suffix); + void setValue(int value); + void setValue(unsigned int value); + void setAutoIndexes(); signals: void currentDataChanged(int index); protected slots: - void init() - { - connect(this, QOverload::of(&AutoPrecisionComboBox::currentIndexChanged), this, &AutoPrecisionComboBox::onCurrentIndexChanged); - } + void onCurrentIndexChanged(int index); - void initField(unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix) - { - const bool savedlock = setLocked(true); - setMinDecimals(minDecimals); - setMaxDecimals(maxDecimals); - setPadding(padding); - setSuffix(suffix); - setLocked(savedlock); - updateList(); - } - - void updateList() - { - const bool savedlock = setLocked(true); - unsigned int i; - int j; - clear(); - - for (i = APCB_DECIMALS_MIN, j = (int)m_maxDecimals; j >= 0; i++, j--) { - if (i >= m_minDecimals) { - QString str = QString("0.%1").arg(QString(i, '0')); - if (m_padding) - str.append(QString(j, '_')); - if (m_suffix != "") - str.append(QString(" %1").arg(m_suffix)); - addItem(str, i); - } - } - - setSizeAdjustPolicy(QComboBox::AdjustToContents); - setLocked(savedlock); - } - - void onCurrentIndexChanged(int index) - { - if (index < 0 || (m_panel && m_panel->lock) || m_lock) - return; - if (m_field) { - *m_field = itemData(index).toUInt(); - emit currentDataChanged(index); - if (m_panel) - emit m_panel->modified(); - } - } - - unsigned int rangecheckDecimals(unsigned int decimals) - { - unsigned int ret; - if (decimals < APCB_DECIMALS_MIN) - ret = APCB_DECIMALS_MIN; - else if (decimals > APCB_DECIMALS_MAX) - ret = APCB_DECIMALS_MAX; - else - ret = decimals; - return ret; - } - - bool isValidDecimals(unsigned int value) - { - if (value >= APCB_DECIMALS_MIN && value <= APCB_DECIMALS_MAX) - return true; - else - return false; - } - - bool setLocked(bool lock) - { - const bool ret = m_lock; - m_lock = lock; - return ret; - } - - protected: - constexpr static unsigned int APCB_DECIMALS_MIN = {0}; - constexpr static unsigned int APCB_DECIMALS_MAX = {6}; - - unsigned int * m_field = nullptr; - GenericPanel * m_panel = nullptr; + private: + unsigned int *m_field = nullptr; unsigned int m_minDecimals = 0; - unsigned int m_maxDecimals = 0; + unsigned int m_maxDecimals = 1; bool m_padding = false; QString m_suffix = ""; - bool m_lock = false; + + void init(); + void setFieldInit(unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr); + bool isValidDecimals(unsigned int value); + unsigned int rangecheckDecimals(unsigned int decimals); + void updateList(); }; diff --git a/companion/src/shared/autoslider.cpp b/companion/src/shared/autoslider.cpp new file mode 100644 index 000000000..5b02b3603 --- /dev/null +++ b/companion/src/shared/autoslider.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autoslider.h" + +AutoSlider::AutoSlider(QWidget * parent): + QSlider(parent), + AutoWidget() +{ + init(); +} + +AutoSlider::AutoSlider(Qt::Orientation orientation, QWidget * parent): + QSlider(orientation, parent), + AutoWidget() +{ + init(); +} + +AutoSlider::~AutoSlider() +{ +} + +void AutoSlider::setField(int & field, int min, int max, GenericPanel * panel) +{ + m_field = &field; + setFieldInit(min, max, panel); +} + +void AutoSlider::setField(unsigned int & field, int min, int max, GenericPanel * panel) +{ + m_field = (int *)&field; + setFieldInit(min, max, panel); +} + +void AutoSlider::setTick(int interval, QSlider::TickPosition position) +{ + setTickInterval(interval); + setTickPosition(position); +} + +void AutoSlider::updateValue() +{ + if (m_field) { + setLock(true); + setValue(*m_field); + setLock(false); + } +} + +void AutoSlider::init() +{ + connect(this, &QSlider::valueChanged, this, &AutoSlider::onValueChanged); +} + +void AutoSlider::setFieldInit(int min, int max, GenericPanel * panel) +{ + setPanel(panel); + setRange(min, max); + updateValue(); +} + +void AutoSlider::onValueChanged(int value) +{ + if (m_field && !lock()) { + if (*m_field != value) { + *m_field = value; + emit currentDataChanged(value); + dataChanged(); + } + } +} diff --git a/companion/src/shared/autoslider.h b/companion/src/shared/autoslider.h new file mode 100644 index 000000000..cb3a2fd11 --- /dev/null +++ b/companion/src/shared/autoslider.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autowidget.h" + +#include + +class AutoSlider : public QSlider, public AutoWidget +{ + Q_OBJECT + + public: + explicit AutoSlider(QWidget * parent = nullptr); + explicit AutoSlider(Qt::Orientation orientation, QWidget * parent = nullptr); + virtual ~AutoSlider(); + + virtual void updateValue() override; + + void setField(int & field, int min, int max, GenericPanel * panel = nullptr); + void setField(unsigned int & field, int min, int max, GenericPanel * panel = nullptr); + void setTick(int interval, QSlider::TickPosition position); + + signals: + void currentDataChanged(int value); + + protected slots: + void onValueChanged(int value); + + private: + int *m_field = nullptr; + + void init(); + void setFieldInit(int min, int max, GenericPanel * panel); +}; diff --git a/companion/src/shared/autospinbox.cpp b/companion/src/shared/autospinbox.cpp new file mode 100644 index 000000000..ecee90edd --- /dev/null +++ b/companion/src/shared/autospinbox.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autospinbox.h" + +AutoSpinBox::AutoSpinBox(QWidget * parent): + QSpinBox(parent), + AutoWidget(), + m_field(nullptr) +{ + connect(this, QOverload::of(&QSpinBox::valueChanged), this, &AutoSpinBox::onValueChanged); +} + +AutoSpinBox::~AutoSpinBox() +{ +} + +void AutoSpinBox::setField(int & field, GenericPanel * panel) +{ + m_field = &field; + setFieldInit(panel); +} + +void AutoSpinBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = (int *)&field; + setFieldInit(panel); +} + +void AutoSpinBox::setFieldInit(GenericPanel * panel) +{ + setPanel(panel); + updateValue(); +} + +void AutoSpinBox::updateValue() +{ + if (m_field) { + setLock(true); + setValue(*m_field); + setLock(false); + } +} + +void AutoSpinBox::onValueChanged(int value) +{ + if (m_field && !lock()) { + if (*m_field != value) { + *m_field = value; + emit currentDataChanged(value); + dataChanged(); + } + } +} diff --git a/companion/src/shared/autospinbox.h b/companion/src/shared/autospinbox.h new file mode 100644 index 000000000..38842fbe0 --- /dev/null +++ b/companion/src/shared/autospinbox.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autowidget.h" + +#include + +class AutoSpinBox : public QSpinBox, public AutoWidget +{ + Q_OBJECT + + public: + explicit AutoSpinBox(QWidget * parent = nullptr); + virtual ~AutoSpinBox(); + + virtual void updateValue() override; + + void setField(int & field, GenericPanel * panel = nullptr); + void setField(unsigned int & field, GenericPanel * panel = nullptr); + + signals: + void currentDataChanged(int value); + + protected slots: + void onValueChanged(int value); + + private: + int *m_field; + + void setFieldInit(GenericPanel * panel); +}; diff --git a/companion/src/shared/autotimeedit.cpp b/companion/src/shared/autotimeedit.cpp new file mode 100644 index 000000000..7b0c4422a --- /dev/null +++ b/companion/src/shared/autotimeedit.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autotimeedit.h" + +AutoTimeEdit::AutoTimeEdit(QWidget * parent): + QTimeEdit(parent), + AutoWidget(), + m_field(nullptr) +{ + connect(this, &QTimeEdit::timeChanged, this, &AutoTimeEdit::onTimeChanged); +} + +AutoTimeEdit::~AutoTimeEdit() +{ +} + +void AutoTimeEdit::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = &field; + setPanel(panel); + updateValue(); +} + +void AutoTimeEdit::setTimeRange(const QTime min, const QTime max) +{ + QTimeEdit::setMinimumTime(min); + QTimeEdit::setMaximumTime(max); +} + +void AutoTimeEdit::updateValue() +{ + if (m_field) { + setLock(true); + int hour = *m_field / 3600; + int min = (*m_field - (hour * 3600)) / 60; + int sec = (*m_field - (hour * 3600)) % 60; + setTime(QTime(hour, min, sec)); + setLock(false); + } +} + +void AutoTimeEdit::onTimeChanged(QTime time) +{ + if (!m_field || lock()) + return; + + unsigned int val = time.hour() * 3600 + time.minute() * 60 + time.second(); + if (*m_field != val) { + *m_field = val; + emit currentDataChanged(val); + dataChanged(); + } +} diff --git a/companion/src/shared/autotimeedit.h b/companion/src/shared/autotimeedit.h index 2ba75d70e..f398fd855 100644 --- a/companion/src/shared/autotimeedit.h +++ b/companion/src/shared/autotimeedit.h @@ -20,80 +20,30 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" -class AutoTimeEdit: public QTimeEdit +#include + +class AutoTimeEdit : public QTimeEdit, public AutoWidget { Q_OBJECT public: - explicit AutoTimeEdit(QWidget * parent = nullptr): - QTimeEdit(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(timeChanged(QTime)), this, SLOT(onTimeChanged(QTime))); - } + explicit AutoTimeEdit(QWidget * parent = nullptr); + virtual ~AutoTimeEdit(); - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + virtual void updateValue() override; - void setMinimumTime(const QTime time) - { - QTimeEdit::setMinimumTime(time); - } - - void setMaximumTime(const QTime time) - { - QTimeEdit::setMaximumTime(time); - } - - void setEnabled(bool enabled) - { - QTimeEdit::setEnabled(enabled); - } - - void updateValue() - { - if (field) { - lock = true; - int hour = *field / 3600; - int min = (*field - (hour * 3600)) / 60; - int sec = (*field - (hour * 3600)) % 60; - setTime(QTime(hour, min, sec)); - lock = false; - } - } + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setTimeRange(const QTime min, const QTime max); signals: - void currentDataChanged(unsigned int value); + void currentDataChanged(unsigned int val); protected slots: - void onTimeChanged(QTime time) - { - if (panel && panel->lock) - return; - if (!field || lock) - return; + void onTimeChanged(QTime time); - unsigned int val = time.hour() * 3600 + time.minute() * 60 + time.second(); - if (*field != val) { - *field = val; - emit currentDataChanged(val); - if (panel) - emit panel->modified(); - } - } - - protected: - unsigned int *field = nullptr; - GenericPanel *panel = nullptr; - bool lock = false; + private: + unsigned int *m_field; }; diff --git a/companion/src/shared/hexspinbox.cpp b/companion/src/shared/autowidget.cpp similarity index 60% rename from companion/src/shared/hexspinbox.cpp rename to companion/src/shared/autowidget.cpp index 63889ef11..75fd1fb26 100644 --- a/companion/src/shared/hexspinbox.cpp +++ b/companion/src/shared/autowidget.cpp @@ -18,30 +18,44 @@ * GNU General Public License for more details. */ -#include "hexspinbox.h" +#include "autowidget.h" +#include "genericpanel.h" -HexSpinBox::HexSpinBox(QWidget *parent): - QSpinBox(parent) +AutoWidget::AutoWidget(): + m_panel(nullptr), + m_lock(false) { - validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this); } -QValidator::State HexSpinBox::validate(QString &text, int &pos) const +AutoWidget::~AutoWidget() { - return validator->validate(text, pos); } -int HexSpinBox::valueFromText(const QString &text) const +bool AutoWidget::lock() { - return text.toInt(0, 16); + return panelLock() | m_lock; } -QString HexSpinBox::textFromValue(int value) const +void AutoWidget::setLock(bool lock) { - QString text; - text.sprintf("%04X", value); - return text; + m_lock = lock; } +void AutoWidget::setPanel(GenericPanel * panel) +{ + m_panel = panel; +} +void AutoWidget::dataChanged() +{ + if (m_panel) + emit m_panel->modified(); +} +bool AutoWidget::panelLock() +{ + if (m_panel) + return m_panel->lock; + else + return false; +} diff --git a/companion/src/shared/autowidget.h b/companion/src/shared/autowidget.h new file mode 100644 index 000000000..820befbdc --- /dev/null +++ b/companion/src/shared/autowidget.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 + +class GenericPanel; + +// Note: cannot be a Qt object otherwise it will create a compiler ambiguity in the inheriting class +class AutoWidget +{ + friend class GenericPanel; + + public: + explicit AutoWidget(); + ~AutoWidget(); + + protected: + virtual void updateValue() = 0; + + bool lock(); + void setLock(bool lock); + void setPanel(GenericPanel * panel); + + void dataChanged(); + bool panelLock(); + + private: + GenericPanel *m_panel; + bool m_lock; +}; diff --git a/companion/src/shared/hexspinbox.h b/companion/src/shared/autowidgets.h similarity index 66% rename from companion/src/shared/hexspinbox.h rename to companion/src/shared/autowidgets.h index 204cc8a55..b2619acdb 100644 --- a/companion/src/shared/hexspinbox.h +++ b/companion/src/shared/autowidgets.h @@ -20,22 +20,13 @@ #pragma once -#include - -class QRegExpValidator; - -class HexSpinBox : public QSpinBox -{ - Q_OBJECT - - public: - HexSpinBox(QWidget *parent = 0); - - protected: - QValidator::State validate(QString &text, int &pos) const; - int valueFromText(const QString &text) const; - QString textFromValue(int value) const; - - private: - QRegExpValidator *validator; -}; +#include "autobitsetcheckbox.h" +#include "autocheckbox.h" +#include "autocombobox.h" +#include "autodoublespinbox.h" +#include "autohexspinbox.h" +#include "autolineedit.h" +#include "autoprecisioncombobox.h" +#include "autoslider.h" +#include "autospinbox.h" +#include "autotimeedit.h" diff --git a/companion/src/shared/genericpanel.cpp b/companion/src/shared/genericpanel.cpp index 5dfad648e..a60ed0cf0 100644 --- a/companion/src/shared/genericpanel.cpp +++ b/companion/src/shared/genericpanel.cpp @@ -19,6 +19,7 @@ */ #include "genericpanel.h" +#include "autowidget.h" #include #include @@ -127,3 +128,13 @@ void GenericPanel::disableMouseScrolling() foreach(QWidget * te, findChildren()) setFocusFilter(te); } + +void GenericPanel::updateAutoWidgets() +{ + foreach(QWidget *wdgt, findChildren()) { + AutoWidget *autowdgt = dynamic_cast(wdgt); + if (autowdgt) { + autowdgt->updateValue(); + } + } +} diff --git a/companion/src/shared/genericpanel.h b/companion/src/shared/genericpanel.h index 88c7f841b..32dfdadc8 100644 --- a/companion/src/shared/genericpanel.h +++ b/companion/src/shared/genericpanel.h @@ -32,15 +32,8 @@ class GenericPanel : public QWidget { Q_OBJECT - friend class AutoComboBox; - friend class AutoDoubleSpinBox; - friend class AutoCheckBox; - friend class AutoHexSpinBox; - friend class AutoLineEdit; friend class GVarGroup; - friend class AutoPrecisionComboBox; - friend class AutoBitsetCheckBox; - friend class AutoTimeEdit; + friend class AutoWidget; public: GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware); @@ -65,4 +58,5 @@ class GenericPanel : public QWidget virtual bool eventFilter(QObject *obj, QEvent *event); void setFocusFilter(QWidget * w); void disableMouseScrolling(); + void updateAutoWidgets(); }; diff --git a/companion/src/shared/verticalscrollarea.h b/companion/src/shared/verticalscrollarea.h index d409a18ca..4f6d2af18 100644 --- a/companion/src/shared/verticalscrollarea.h +++ b/companion/src/shared/verticalscrollarea.h @@ -26,6 +26,8 @@ class GenericPanel; class VerticalScrollArea : public QScrollArea { + Q_OBJECT + public: VerticalScrollArea(QWidget * parent, GenericPanel * panel); diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 2a6eea996..0ece144ea 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -1631,7 +1631,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Trainer Sticks - Manches Écolage + Écolage Manches @@ -1656,7 +1656,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Trainer Channels - Écolage Channels + Écolage Canaux @@ -2233,7 +2233,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import OpenTX ne supporte pas cette fonction sur cette carte - + OpenTX doesn't accept this radio protocol OpenTX ne supporte pas ce protocole radio @@ -8975,28 +8975,33 @@ Mixage actif par défaut si non-renseigné. Maître/Multi - + Disable Telemetry Désactiver la télémétrie - + + Racing Mode + Mode Course/Racing + + + Disable Ch. Map Désactiver "Mapping" des VOIES - + Show values in: Afficher les valeurs en: - + % abbreviation for percent - + μs abbreviation for microseconds @@ -9084,7 +9089,7 @@ Mixage actif par défaut si non-renseigné. ID d'enregistrement - + Failsafe Positions Positions Failsafe @@ -9137,100 +9142,100 @@ Mixage actif par défaut si non-renseigné. Négative - + Trainer Port Port écolage - + Internal Radio System Module HF interne - + External Radio Module Module HF externe - + Extra Radio System Module HF supplémentaire - + Radio System Module HF - + 10mW - 16CH 10mW - 16 VOIES - - + + 100mW - 16CH 100mW - 16 VOIES - + 500mW - 16CH 500mW - 16 VOIES - + Auto <= 1W - 16CH Auto <= 1W - 16 VOIES - - + + 25mW - 8CH 25mW - 8 VOIES - - + + 25mW - 16CH 25mW - 16 VOIES - + 200mW - 16CH (no telemetry) 200mW - 16 VOIES (pas de télémétrie) - + 500mW - 16CH (no telemetry) 500mW - 16 VOIES (pas de télémétrie) - + 100mW - 16CH (no telemetry) 100mW - 16 VOIES (pas de télémétrie) - + 25 mW - + 100 mW - + 500 mW - + 1 W - + 2 W @@ -9238,42 +9243,42 @@ Mixage actif par défaut si non-renseigné. ModulePanel - + Value Valeur - + Hold Maintien - + No Pulse Pas d'impulsion - + Ask Demander - + Internal Interne - + Internal + External Interne + Externe - + External Externe - + Bind on channel Bind avec VOIE @@ -11031,87 +11036,87 @@ Si cette option est cochée, la voie des gaz est inversée: le ralenti est &apo SetupPanel - + Popup menu available Menu contextuel disponible - + Timer %1 Chrono %1 - + Profile Settings Paramètres du profil - + SD structure path not specified or invalid Chemin de la structure SD non spécifié ou invalide - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Timer. Are you sure? Effacer le Chrono. Êtes-vous sûr? - + Clear all Timers. Are you sure? Effacer tous les Chronos. Êtes-vous sûr? - + Cut Timer. Are you sure? Couper le Chrono. Êtes-vous sûr? - + Delete Timer. Are you sure? Supprimer le Chrono. Êtes-vous sûr? diff --git a/companion/src/translations/companion_ja.ts b/companion/src/translations/companion_ja.ts index d423959d4..e276a2539 100644 --- a/companion/src/translations/companion_ja.ts +++ b/companion/src/translations/companion_ja.ts @@ -830,127 +830,127 @@ Mode 4: ChannelsPanel - + Name 名称 - + Subtrim サブトリム - + Min 最小 - + Max 最大 - + Direction 方向 - + Curve カーブ - + PPM Center PPM センター - + Linear Subtrim 線形サブトリム - + CH%1 CH%1 - + Popup menu available 利用可能なポップアップメニュー - + --- --- - + INV リバース - + Delete Channel. Are you sure? チャンネルを削除します。よろしいですか? - + Cut Channel. Are you sure? チャンネルを切り取ります。よろしいですか? - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Channel. Are you sure? チャンネルを消去します。よろしいですか? - + Clear all Channels. Are you sure? チャンネルをすべて消去します。よろしいですか? @@ -1090,37 +1090,37 @@ Mode 4: 送信機と機体モデルの設定 - + Select or create a file for exported Settings: エクスポートした設定ファイルを選択または作成します: - + Press the 'Retry' button to choose another file. 別のファイルを選択するには『再試行』ボタンを押してください。 - + Simulator for this firmware is not yet available このファームウェアのシミュレータはまだ利用できません - + Uknown error during Simulator startup. シミュレータ起動中に不明なエラーが発生しました。 - + Simulator Error シミュレータ エラー - + Data Load Error データロード エラー - + Error occurred while starting simulator. シミュレータ起動中にエラーが発生しました。 @@ -1370,7 +1370,7 @@ If you have a settings backup file, you may import that instead. CurveData - + CV CV @@ -1603,219 +1603,297 @@ If you have a settings backup file, you may import that instead. CustomFunctionData - + GF GF - + SF SF - + Override %1 上書き %1 - + Trainer Sticks トレーナー Sticks - + Trainer RUD トレーナー RUD - + Trainer ELE トレーナー ELE - + Trainer THR トレーナー THR - + Trainer AIL トレーナー AIL - + Trainer Channels トレーナー チャンネル - + Instant Trim トリム - + Play Sound サウンド 再生 - + Haptic - タッチパネル + バイブレーション - + Reset リセット - + Set %1 設定 %1 - + + Lua Script + Luaスクリプト + + + Set Failsafe フェイルセーフ設定 - + + Range Check Int. Module + 内部モジュール レンジチェック + + + + Range Check Ext. Module + 外部モジュール レンジチェック + + + + Beep 1 + ビープ 1 + + + + Beep 2 + ビープ 2 + + + + Beep 3 + ビープ 3 + + + + Warn 1 + 警告 1 + + + + Warn 2 + 警告 2 + + + + Cheep + チープ + + + + Ratata + ラッタッタ + + + + Tick + ティック + + + + Siren + サイレン + + + + Ring + リング + + + + Sci Fi + Sci Fi + + + + Robot + ロボット + + + + Chirp + + + + + Tada + Tada + + + + Cricket + クリケット + + + + Alarm Clock + アラームクロック + + + + Value + 値 + + + + Source + 選択元 + + + + Global Variable + グローバル変数 + + + + Inc/Decrement + 増/減 + + + Vario バリオ - + Play Track トラック 再生 - + Play Both 両方 実行 - + Play Value 値 実行 - - Play Script - スクリプト 実行 - - - + SD Logs SDログ - + Volume 音量 - + Backlight バックライト - + Screenshot 画面キャプチャ - + Background Music BGM - + Background Music Pause BGM一時停止 - + Adjust %1 調整 %1 - - RangeCheck Int. Module - レンジチェック 内部モジュール - - - - RangeCheck Ext. Module - レンジチェック 外部モジュール - - - + Bind Int. Module バインド 内部モジュール - + Bind Ext. Module バインド 外部モジュール - + + Played once, not during startup + 起動時以外 1回実行 + + + + No repeat + リピートなし + + + + Repeat %1s + リピート %1秒 + + + Flight フライト - + Telemetry テレメトリー - - Rotary Encoder - エンコーダ再生 - - - - REa - エンコーダ再生a - - - - REb - エンコーダ再生b - - - + s s - - - - <font color=red><b>Inconsistent parameter</b></font> - <font color=red><b>矛盾しているパラメータ</b></font> - - - - Value - 値 - - - - played once, not during startup - 起動時以外 1回実行 - - - - repeat(%1s) - リピート(%1s) - - - + DISABLED 無効 - + CFN CFN @@ -1823,142 +1901,122 @@ If you have a settings backup file, you may import that instead. CustomFunctionsPanel - + Switch スイッチ - + Action 実行 - + Parameters パラメータ - - Enable - 有効 - - - + Popup menu available 利用可能なポップアップメニュー - + SF%1 SF%1 - + GF%1 GF%1 - + + GV + GV + + + ON ON - + Error occurred while trying to play sound, possibly the file is already opened. (Err: %1 [%2]) サウンド再生中にエラーが発生しました。おそらくファイルは既に開かれています (Err: %1 [%2]) - + Unable to find or open sound file: %1 音源ファイルが見つからない、または開けません。 %1 - + Delete Function. Are you sure? ファンクションを削除します。よろしいですか? - + Cut Special Function. Are you sure? ファンクションを切り取ります。よろしいですか? - + Clear Function. Are you sure? ファンクションを消去します。よろしいですか? - + Clear all Functions. Are you sure? ファンクションをすべて消去します。よろしいですか? - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - - - Value - 値 - - - - Source - 選択元 - - - - GVAR - GVAR - - - - Increment - 増加 - CustomizeSplashDialog @@ -2164,11 +2222,64 @@ If you have a settings backup file, you may import that instead. このボード上のOpenTXはこの機能を受け付けません - + OpenTX doesn't accept this radio protocol OpenTXはこの送信機プロトコルを受け付けません + + DataHelpers + + + Disabled + 無効 + + + + Enabled + 有効 + + + + OFF + OFF + + + + ON + ON + + + + False + False + + + + True + True + + + + N + N + + + + Y + Y + + + + No + いいえ + + + + Yes + はい + + DebugOutput @@ -2374,12 +2485,12 @@ To <b>remove a remembered entry</b> from the filter list, first cho ExpoData - + INP INP - + (@%1) (@%1) @@ -2501,22 +2612,22 @@ If blank then the input is considered to be "ON" all the time.編集 %1 - + Popup menu available 利用可能なポップアップメニュー - + Clear All すべて消去 - + Set All すべて設定 - + Invert All すべてリバース @@ -2605,129 +2716,129 @@ If blank then the input is considered to be "ON" all the time. FileSyncDialog - + Synchronize Files ファイルを同期する - + Are you sure you wish to abort the sync? 同期を中止してよろしいですか? - + Source Folder: 移動元フォルダ: - + Destination Folder: 移動先フォルダ: - + %1%2 Both directions, to destination folder first %1%2の両方を最初の移動先フォルダへ - + %1%2 Both directions, to source folder first %1%2の両方を最初の移動元フォルダへ - + %1 Only from source folder to destination folder %1 移動元フォルダから移動先フォルダのみ - + %1 Only from destination folder to source folder %1 移動先フォルダから移動元フォルダのみ - + How to handle overwriting files which already exist in the destination folder. コピー先フォルダに既に存在するファイルを上書きする方法となります。 - + Copy only if newer and different (compare contents) 新しい場合、または異なる場合のみコピー (内容を比較して) - + Copy only if newer (do not compare contents) 新しい場合のみコピー (内容を比較しない) - + Copy only if different (ignore file time stamps) 異なる場合のみコピー (ファイルのタイムスタンプを無視) - + Always copy (force overwite existing files) 常にコピー (既存のファイルを強制上書き) - + Any size 任意のサイズ - + Skip files larger than this size. Enter zero for unlimited. このサイズより大きいファイルをスキップします。無制限の場合はゼロを入力してください。 - + Minimum reporting level. Events of this type and of higher importance are shown. WARNING: High log rates may make the user interface temporarily unresponsive. 最小レポートレベル このような種類の重要度の高いイベントが表示されます。 警告: ログレベルが高いと、ユーザーインターフェースが一時的に応答しなくなる場合があります。 - + Skipped スキップ - + Created 作成 - + Updated 更新 - + Errors Only エラーのみ - + Test-run only テスト稼働のみ - + Run as normal but do not actually copy anything. Useful for verifying results before real sync. 通常通りに実行しますが、実際には何もコピーしません。実際の同期前に結果を検証するのに役立ちます。 - + Log Level: ログレベル: - + Filters: フィルター: - + The "Include" filter will only copy files which match the pattern(s). The "Exclude" filter will skip files matching the filter pattern(s). The Include filter is evaluated first. @@ -2736,148 +2847,148 @@ The Include filter is evaluated first. Includeフィルターが最初に設定されます。 - + One or more file pattern(s) to exclude, separated by commas. Blank means exclude none. ?, *, and [...] wildcards accepted. 除外する1つ以上のファイルパターンでカンマで区切ります。 空白は何も除外しないことを意味します。 ?, *, および [...] ワイルドカードを使用できます。 - + One or more file pattern(s) to include, separated by commas. Blank means include all. ?, *, and [...] wildcards accepted. 含める1つまたは複数のファイルパターン。カンマで区切ります。 空白は何も除外しないことを意味します。 ?, *, および [...] ワイルドカードを使用できます。 - + Include: - + Exclude: - + Case sensitive 敏感なケース - - + + Follow links フォローリンク - + Include hidden 非表示にする - + Recursive 回帰的 - + Skip empty 空をスキップ - + Apply filters フィルター実行 - + Filter Options: フィルターオプション: - + Folder Options: フォルダーオプション: - - + + Options オプション - + Show extra options 追加オプションを表示 - + Reset to defaults 初期値へリセット - + Close 閉じる - + Sync. Direction: 方向の同期: - + Existing Files: 既存のファイル: - + Max. File Size: 最大ファイルサイズ: - + MB MB - + KB KB - + Abort バージョン情報 - + Start スタート - + Total: <b>%1</b>; Created: <b>%2</b>; Updated: <b>%3</b>; Skipped: <b>%4</b>; Errors: <font color=%6><b>%5</b></font>; Total: <b>%1</b>; 作成: <b>%2</b>; アップデート: <b>%3</b>; スキップ: <b>%4</b>; エラー: <font color=%6><b>%5</b></font>; - + Current: <b>%1</b> of 現在: <b>%1</b> - + Source folder not found. 移動元フォルダが見つかりません。 - + Destination folder not found. 移動先フォルダが見つかりません。 - + Source and destination folders are the same. 移動元と移動先のフォルダは同じです。 @@ -2975,7 +3086,7 @@ Blank means include all. ?, *, and [...] wildcards accepted. Haptic module installed - タッチパネル対応モジュールをインストール + バイブレーション対応モジュールをインストール @@ -3660,7 +3771,7 @@ You are currently using: FlightModeData - + FM FM @@ -4131,12 +4242,12 @@ p, li { white-space: pre-wrap; } - + Own value 独自トリム - + Flight mode %1 value フライトモード %1 値 @@ -4625,7 +4736,7 @@ p, li { white-space: pre-wrap; } Haptic Mode - タッチパネルモード + バイブレーションモード @@ -4758,16 +4869,6 @@ p, li { white-space: pre-wrap; } Optrex オプトレックス(京セラ) - - - Battery warning voltage. -This is the threashhold where the battery warning sounds. - -Acceptable values are 5v..10v - バッテリー警告電圧 -これはバッテリー警告音のしきい値です。 -許容値は5~10vです - Only Alarms @@ -4781,7 +4882,7 @@ Acceptable values are 5v..10v Haptic Length - タッチパネル長押し + バイブレーション時間 @@ -4801,7 +4902,7 @@ Acceptable values are 5v..10v Haptic Strength - タッチパネル感度 + バイブレーション強度 @@ -5004,6 +5105,16 @@ Acceptable values are 5v..10v A T E R ↔AIL ↕THR ↕ELE ↔RUD [A T E R] + + + Battery warning voltage. +This is the threashhold where the battery warning sounds. + +Acceptable values are 3v..12v + バッテリー警告電圧 +これはバッテリー警告音がなる3つの電圧しきい値です。 +許容値は3~12vです + Power Off Delay @@ -5727,27 +5838,27 @@ Are you sure ? InputsPanel - - + + Move Up 上へ移動 - + Ctrl+Up - - + + Move Down 下へ移動 - + Ctrl+Down @@ -5772,131 +5883,131 @@ Are you sure ? 選択したInput行を切り取ります。よろしいですか? - + Lines 直線 - + &Add &追加 - + Ctrl+A - + &Edit &編集 - + Enter Enter - + &Delete &削除 - - + + Delete 削除 - + &Copy &コピー - + Ctrl+C - + &Cut &切り取り - + Ctrl+X - + &Paste &貼り付け - + Ctrl+V - + Du&plicate &複製 - + Ctrl+U - + Input 入力 - + Insert 挿入 - + Clear 消去 - + Clear All すべて消去 - + Clear all Input lines. Are you sure? すべてのInput行を消去します。よろしいですか? - - Clear all lines for the selected Inputs. Are you sure? + + Clear all lines for the selected Input. Are you sure? 選択したすべてのInput行を消去します。よろしいですか? - - Delete all lines for the selected Inputs. Are you sure? + + Delete all lines for the selected Input. Are you sure? 選択したすべてのInput行を削除します。よろしいですか? LimitData - + INV リバース - + NOR ノーマル - + CH CH @@ -5904,7 +6015,7 @@ Are you sure ? LimitsGroup - + GV @@ -7658,12 +7769,12 @@ Do you want to save your changes? MixData - + MIX ミックス - + (@%1) @@ -7880,22 +7991,22 @@ If blank then the mix is considered to be "ON" all the time. - + Click to access popup menu クリックしてポップアップメニューにアクセス - + Clear All すべて消去 - + Set All すべて設定 - + Invert All すべてリバース @@ -7927,25 +8038,25 @@ If blank then the mix is considered to be "ON" all the time. MixesPanel - + Move Up 上へ移動 - + Ctrl+Up - + Move Down 下へ移動 - + Ctrl+Down @@ -7970,92 +8081,92 @@ If blank then the mix is considered to be "ON" all the time. 選択したMix行を切り取ります。よろしいですか? - + &Add &追加 - + Ctrl+A - + &Edit &編集 - + Enter Enter - + &Toggle highlight &ハイライト切替 - + Ctrl+T - + &Delete &削除 - + Delete 削除 - + &Copy &コピー - + Ctrl+C - + C&ut &切り取り - + Ctrl+X - + &Paste &貼り付け - + Ctrl+V - + Du&plicate &複製 - + Ctrl+U - + Clear Mixes? ミキサーを消去しますか? - + Really clear all the mixes? すべてのミキサーを本当に消去しますか? @@ -8063,17 +8174,17 @@ If blank then the mix is considered to be "ON" all the time. ModelData - + Model: 機体モデル: - + Throttle Source スロットル値 - + THR THR @@ -8091,52 +8202,52 @@ If blank then the mix is considered to be "ON" all the time. シミュレート - + Setup セットアップ - + Heli ヘリ - + Flight Modes フライトモード - + Inputs 入力 - + Mixes ミキサー - + Outputs 出力 - + Curves カーブ - + Logical Switches 論理スイッチ - + Special Functions スペシャルファンクション - + Telemetry テレメトリー @@ -8190,9 +8301,8 @@ If blank then the mix is considered to be "ON" all the time. - - - + + OFF OFF @@ -8233,7 +8343,7 @@ If blank then the mix is considered to be "ON" all the time. - + Mode モード @@ -8267,13 +8377,13 @@ If blank then the mix is considered to be "ON" all the time. - + Delay 遅延 - + Receiver 受信機 @@ -8365,397 +8475,342 @@ If blank then the mix is considered to be "ON" all the time. - - - - + + + + None なし - - - Name - 名称 - - Countdown - カウントダウン - - - - Minute call - 音声時報 - - - - Persistent - 持続 - - - - - + + FM%1 - + FM%1%2 - + FM%1+%2 - - + + NoTrim トリムなし - - + + Offset(%1) オフセット(%1) - + MULT! 複数! - + No DR/Expo DR/Expoなし - - + + Weight(%1) ウェイト(%1) - - + + Switch(%1) スイッチ(%1) - + Delay(u%1:d%2) 遅延(u%1:d%2) - + Slow(u%1:d%2) 速度低下(u%1:d%2) - + Warn(%1) 警告(%1) - + Disabled in all flight modes すべてのフライトモードを無効 - + Flight modes フライトモード - + Flight mode フライトモード - + All すべて - + Edge Edge:端 - + instant 簡易 - + Sticky Sticky:追尾 - + Timer タイマー - + missing 間違い - + Duration 期間 - - + + Custom カスタム - + Standard 標準 - + Extended Limits 拡張制限 - + Display Checklist ディスプレイ チェックリスト - + Global Functions グローバルファンクション - + Manual 手動 - + Auto 自動 - + Failsafe Mode フェイルセーフモード - - + + Hold ホールド - + No Pulse パルスなし - + Not set セットなし - + No pulses パルスなし - - Silent - 消音 - - - - Beeps - ビープ - - - - Voice - 音声 - - - - Haptic - タッチパネル - - - - Flight - フライト - - - - Manual reset - 手動リセット - - - + Step ステップ - + Display ディスプレイ - + Extended 拡張 - + Never なし - + On Change 変更 - + Always 常に - - - + + + Source 選択元 - + Trim idle only トリムアイドルのみ - + Warning 警告 - + Reversed リバース - - Tmr - Tmr - - - + FrSky S.PORT - + FrSky D - + FrSky D (cable) - + Alti - + Alti+ - + VSpeed - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells セル - + Min 最小 - + Max 最大 - + Numbers ナンバー - + Bars バー - + Script スクリプト - + Filename ファイル名 - + Error: Unable to open or read file! エラー: ファイルを開けないか、読み取れません! @@ -9018,33 +9073,38 @@ If blank then the mix is considered to be "ON" all the time. マスター/Multi - + Disable Telemetry テレメトリー無効 - + + Racing Mode + レーシングモード + + + Disable Ch. Map チャンネルマップ無効 - + Failsafe Positions フェイルセーフポジション - + Show values in: 値の表示: - + % abbreviation for percent - + μs abbreviation for microseconds @@ -9053,100 +9113,100 @@ If blank then the mix is considered to be "ON" all the time. ModuleData - + Trainer Port トレーナーポート - + Internal Radio System 内部送信システム - + External Radio Module 外部送信モジュール - + Extra Radio System 追加送信システム - + Radio System 送信システム - + 10mW - 16CH - - + + 100mW - 16CH - + 500mW - 16CH - + Auto <= 1W - 16CH - - + + 25mW - 8CH - - + + 25mW - 16CH - + 200mW - 16CH (no telemetry) 200mW - 16CH (テレメトリーなし) - + 500mW - 16CH (no telemetry) 500mW - 16CH (テレメトリーなし) - + 100mW - 16CH (no telemetry) 100mW - 16CH (テレメトリーなし) - + 25 mW 25 mW - + 100 mW 100 mW - + 500 mW 500 mW - + 1 W 1 W - + 2 W 2 W @@ -9164,42 +9224,42 @@ If blank then the mix is considered to be "ON" all the time. ModulePanel - + Value 値 - + Hold ホールド - + No Pulse パルスなし - + Ask 確認 - + Internal 内部 - + Internal + External 内部 + 外部 - + External 外部 - + Bind on channel チャンネルへバインド @@ -9213,8 +9273,8 @@ If blank then the mix is considered to be "ON" all the time. - - + + Name 名称 @@ -9270,7 +9330,7 @@ If blank then the mix is considered to be "ON" all the time. - + Switch スイッチ @@ -9290,313 +9350,318 @@ If blank then the mix is considered to be "ON" all the time. 持続 - + + Start + スタート + + + Modules モジュール - + Trainer port トレーナーポート - + Helicopter ヘリコプター - + Swash Swash - - + + Type タイプ - + Ring リング - + Input 入力 - + Weight ウェイト - + Long. cyc 長周期 - + Lateral cyc 側面周期 - + Collective コレクティブ - + Flight modes フライトモード - - + + Flight mode フライトモード - + F.In F.In - + F.Out F.Out - + Global vars グローバル変数 - - + + GV%1 GV%1 - + RE%1 RE%1 - + Unit ユニット - + Prec 精度 - - + + Min 最小 - - + + Max 最大 - + Popup ポップアップ - + Outputs 出力 - + Channel チャンネル - + Subtrim サブトリム - + Direct ダイレクト - + Curve カーブ - + PPM PPM - + Linear 線形 - + Global Variables グローバル変数 - + Inputs 入力 - + Mixers ミキサー - + Curves カーブ - + L%1 L%1 - + Logical Switches 論理スイッチ - + SF%1 SF%1 - + Special Functions スペシャルファンクション - + Telemetry テレメトリー - + Protocol プロトコル - + RSSI Alarms RSSIアラーム - + Low 低 - + Critical クリティカル - + Telemetry audio テレメトリー音源 - + Altimetry 高度計 - - + + Vario source バリオ 元値 - + Vario limits > バリオ リミット値 > - + Sink max Sink 最大 - + Sink min Sink 最小 - + Climb min クライム 最小 - + Climb max クライム 最大 - + Center silent センター サイレント - + Top Bar トップバー - + Volts source ボルト 元値 - + Altitude source 高度 元値 - + Parameters パラメータ - + Telemetry Sensors テレメトリーセンサー - + Telemetry Screens テレメトリースクリーン - + GF%1 GF%1 - + Global Functions グローバルファンクション - + Checklist チェックリスト @@ -9838,12 +9903,12 @@ If blank then the mix is considered to be "ON" all the time. QObject - + WARNING 警告 - + <p>Importing JumperTX data into OpenTX 2.3 is <b>not supported and dangerous.</b></p> <p>It is unfortunately not possible for us to differentiate JumperTX data from legitimate FrSky X10 data, but <b>You should only continue here if the file you opened comes from a real FrSky X10.</b></p> <p>Do you really want to continue?</p> <p>JumperTXデータをOpenTX 2.3にインポートすることは<b>サポート対象外となり危険な行為です。</b></p> <p>残念ながら、JumperTXデータを正当なFrSky X10データと区別することができませんが、<b>開いたファイルが実際のFrSky X10で生成されたものである場合にのみ、続行する事ができます。</b></p> <p>本当に続行しますか?</p> @@ -9851,7 +9916,7 @@ If blank then the mix is considered to be "ON" all the time. RadioData - + Models 機体モデル @@ -10418,24 +10483,6 @@ X OpenTX リリースノート - - RepeatComboBox - - - Played once, not during startup - 起動時以外 1回実行 - - - - No repeat - リピートなし - - - - %1s - %1秒 - - RudderPage @@ -10474,327 +10521,322 @@ X SensorData - + Formula 定式 - + Id ID - + Instance インスタンス - - + + Sensor センサー - + Sources 選択元 - + GPS GPS - + Alt 高度 - + Unit ユニット - + Precision 精度 - + Ratio レシオ - + Offset オフセット - + Auto Offset 自動オフセット - + Blades ブレード - + Multiplier 乗算 - + Filter フィルター - + Persist 持続 - + Positive ノーマル - + Log ログ - + Custom カスタム - + Calculated 計算値 - + Add 追加 - + Average 平均 - + Minimum 最低値 - + Maximum 最大値 - + Multiply 乗算 - + Totalize 合計 - + Cell セル - + Consumption 消費 - + Distance 距離 - + Lowest より低く - + Cell %1 セル %1 - + Highest より高く - + Delta 差分 - + Raw (-) Raw (-) - - + + V V - + A - + mA - + kts - + m/s - + km/h - + mph - + m - + f - + °C - + °F - + % - + mAh - + W - + mW - + dB - + rpms - + g - + ° - + Rad - + hours - + minutes - + seconds - + ml/minute - + Hertz - + mS - + uS - + + (default) + (初期値) + + + TELE - - - Y - Y - - - - N - N - Setup @@ -10976,92 +11018,87 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi SetupPanel - + Popup menu available 利用可能なポップアップメニュー - + Timer %1 タイマー %1 - - THR - - - - + Profile Settings プロファイル設定 - + SD structure path not specified or invalid SDカード保存先パスが指定されていないか無効です - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Timer. Are you sure? タイマーを消去します。よろしいですか? - + Clear all Timers. Are you sure? タイマーをすべて消去します。よろしいですか? - + Cut Timer. Are you sure? タイマーを切り取ります。よろしいですか? - + Delete Timer. Are you sure? タイマーを削除します。よろしいですか? @@ -12515,22 +12552,22 @@ Too many errors, giving up. 最大 - + None なし - + Numbers ナンバー - + Bars バー - + Script スクリプト @@ -12538,52 +12575,52 @@ Too many errors, giving up. TelemetryPanel - + Telemetry screen %1 テレメトリースクリーン %1 - + FrSky S.PORT - + FrSky D - + FrSky D (cable) - + Source 信号元 - + Low Alarm 低アラーム - + Critical Alarm クリティカルアラーム - + Winged Shadow How High Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (サポートなし) - + Delete Sensor. Are you sure? テレメトリーセンサーを削除します。よろしいですか? @@ -12701,72 +12738,72 @@ Too many errors, giving up. TelemetrySensorPanel - + TELE%1 - + Popup menu available 利用可能なポップアップメニュー - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Cut Telemetry Sensor. Are you sure? テレメトリーセンサーを切り取ります。よろしいですか? - + Clear Telemetry Sensor. Are you sure? テレメトリーセンサーを消去します。よろしいですか? - + Clear all Telemetry Sensors. Are you sure? テレメトリーセンサーのすべてを消去します。よろしいですか? @@ -13140,7 +13177,12 @@ hh:mm:ss カウントダウン - + + Start + スタート + + + Minute Call 音声時報 @@ -13148,21 +13190,91 @@ hh:mm:ss TimerData - + TMR TMR - + Timer %1 タイマー %1 - + TMR as in Timer TMR + + + Silent + 消音 + + + + Beeps + ビープ + + + + Voice + 音声 + + + + Haptic + バイブレーション + + + + 5s + 5秒 + + + + 10s + 10秒 + + + + 20s + 20秒 + + + + 30s + 30秒 + + + + Not persistent + 持続なし + + + + NOT + なし + + + + Persistent (flight) + 持続 (フライト) + + + + Flight + フライト + + + + Persistent (manual reset) + 持続 (手動リセット) + + + + Manual reset + 手動リセット + TimerEdit @@ -13189,44 +13301,6 @@ CTRL+スクロールまたはPAGE UP/DOWNキーを押すと、大きなステ 秒 - - TimerPanel - - - Silent - 消音 - - - - Beeps - ビープ - - - - Voice - 音声 - - - - Haptic - タッチパネル - - - - Not persistent - 持続なし - - - - Persistent (flight) - 持続 (フライト) - - - - Persistent (manual reset) - 持続 (手動リセット) - - Trainer diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt index b03b2c860..305451422 100644 --- a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt @@ -6,6 +6,7 @@ 14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video 41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED 42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED 42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol @@ -34,9 +35,9 @@ 6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH 45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH -45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH 16,0,ESKY,Std,0,Gyro,Pitch 16,1,ESKY,ET4,0,Gyro,Pitch 35,0,ESKY150,4CH,0 @@ -54,6 +55,7 @@ 28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 53,0,Height,5ch,0,Gear 53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 @@ -79,6 +81,8 @@ 65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 55,1,FrSkyRX,CloneTX,0 +55,2,FrSkyRX,EraseTX,0 +55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 58,0,FX816,P38,1 20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib 20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib @@ -99,13 +103,14 @@ 26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib -57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess 2,1,Hubsan,H301,0,RTH,Light,Stab,Video 2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes 22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -71,0,JJRC345,Std,1,Flip,HLess,RTH +71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 49,0,KF606,Std,1,Trim 9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim 9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim @@ -122,6 +127,8 @@ 17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess +17,5,MT99XX,A180,0,3D_6G +17,6,MT99XX,Dragon,0,Mode,RTH 44,0,NCC1701,Std,1,Warp 77,0,OMP,M2,0,THold,IdleUp,6G_3D 60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 @@ -138,10 +145,11 @@ 72,0,Q90C,Std,0,FMode,VTX+ 74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 76,0,Realacc,R11,1,Flip,Light,Calib,HLess,RTH,UNK 50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 +21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8 19,0,Shenqi,Cycle,1 68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch @@ -174,3 +182,11 @@ 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess 52,0,ZSX,280,1,Light +78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10 +80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH +81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE +82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe +83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR +84,0,JOYSWAY,Std,0 +85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiLOLI.lua b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiLOLI.lua new file mode 100644 index 000000000..4d309e37e --- /dev/null +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiLOLI.lua @@ -0,0 +1,221 @@ + +local toolName = "TNS|Multi LOLI RX config|TNE" + +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local loli_nok = false +local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4 + { 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7 + { -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8 + +local sel = 1 +local edit = false + +local blink = 0 +local BLINK_SPEED = 15 + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +local function LOLI_Draw_LCD(event) + local line = 0 + + lcd.clear() + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + drawScreenTitle("Multi - LOLI RX configuration tool") + x_pos = 152 + x_inc = 90 + y_pos = 40 + y_inc = 20 + else + x_pos = 5 + x_inc = 30 + y_pos = 1 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if loli_nok then + if LCD_W == 480 then + lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE) + end + return + end + + --Display current config + if LCD_W == 480 then + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt) + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt) + lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188) + lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0) + lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0) + line = line + 1 + end + + local out + for i = 1, 8 do + out = getValue("ch"..(i+8)) + lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt) + for j = 1, #channels[i], 3 do + if out > channels[i][j] then + if sel == i then + invert = INVERS + if edit == true then + blink = blink + 1 + if blink > BLINK_SPEED then + invert = 0 + if blink > BLINK_SPEED * 2 then + blink = 0 + end + end + end + else + invert = 0 + end + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert) + break + end + end + line = line + 1 + end +end + +local function LOLI_Change_Value(dir) + local pos = 0 + local out + --look for the current position + out = getValue("ch"..(sel+8)) + for j = 1, #channels[sel], 3 do + if out > channels[sel][j] then + pos = j + break + end + end + + --decrement or increment + if dir < 0 and pos > 1 then + pos = pos - 3 + elseif dir > 0 and pos + 3 < #channels[sel] then + pos = pos + 3 + else + return + end + + --delete all mixers for the selected channel + local num_mix = model.getMixesCount(sel-1 +8) + for i = 1, num_mix do + model.deleteMix(sel-1 +8, 0); + end + + --create new mixer + local source_max = getFieldInfo("cyc1") + + local val = { name = channels[sel][pos+1], + source = source_max.id - 1, -- MAX=100 on TX16S + weight = channels[sel][pos+2], + offset = 0, + switch = 0, + multiplex = 0, + curveType = 0, + curveValue = 0, + flightModes = 0, + carryTrim = false, + mixWarn = 0, + delayUp = 0, + delayDown = 0, + speedUp = 0, + speedDown = 0 } + model.insertMix(sel-1 +8, 0, val) +end + +local function LOLI_Menu(event) + if event == EVT_VIRTUAL_NEXT then + if edit == false then + -- not changing a value + if sel < 8 then + sel = sel + 1 + end + else + -- need to inc the value + LOLI_Change_Value(1) + end + elseif event == EVT_VIRTUAL_PREV then + if edit == false then + -- not changing a value + if sel > 1 then + sel = sel - 1 + end + else + -- need to dec the value + LOLI_Change_Value(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if edit == false then + edit = true + blink = BLINK_SPEED + else + edit = false + end + end +end + +-- Init +local function LOLI_Init() + local module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + loli_nok = true + end + end +end + +-- Main +local function LOLI_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + return 2 + else + LOLI_Menu(event) + LOLI_Draw_LCD(event) + return 0 + end +end + +return { init=LOLI_Init, run=LOLI_Run } diff --git a/radio/sdcard/horus/THEMES/Flexi/ghost.png b/radio/sdcard/horus/THEMES/Flexi/ghost.png new file mode 100644 index 000000000..cfec1cb35 Binary files /dev/null and b/radio/sdcard/horus/THEMES/Flexi/ghost.png differ diff --git a/radio/sdcard/horus/UTILITIES/MultiConfigExternal.lua b/radio/sdcard/horus/UTILITIES/MultiConfigExternal.lua new file mode 100755 index 000000000..a8baf3d13 --- /dev/null +++ b/radio/sdcard/horus/UTILITIES/MultiConfigExternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(1, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(1) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(1, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/horus/UTILITIES/MultiConfigInternal.lua b/radio/sdcard/horus/UTILITIES/MultiConfigInternal.lua new file mode 100755 index 000000000..29ced006c --- /dev/null +++ b/radio/sdcard/horus/UTILITIES/MultiConfigInternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(0, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(0) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(0, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/horus/SxR Calibrate/SxR_Calibrate.lua b/radio/sdcard/horus/UTILITIES/SxR Calibrate/SxR_Calibrate.lua similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/SxR_Calibrate.lua rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/SxR_Calibrate.lua diff --git a/radio/sdcard/horus/SxR Calibrate/img/back.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/back.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/back.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/back.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/delta_b.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/delta_b.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/delta_b.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/delta_b.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/done.bmp b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/done.bmp similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/done.bmp rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/done.bmp diff --git a/radio/sdcard/horus/SxR Calibrate/img/down.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/down.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/down.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/down.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/forward.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/forward.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/forward.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/forward.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/left.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/left.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/left.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/left.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/plane_b.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/plane_b.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/plane_b.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/plane_b.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/planev_b.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_b.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/planev_b.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_b.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/planev_s.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_s.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/planev_s.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_s.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/right.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/right.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/right.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/right.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/up.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/up.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/up.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/up.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/vert-r.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert-r.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/vert-r.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert-r.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/vert.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/vert.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert.png diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt index b03b2c860..305451422 100644 --- a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt @@ -6,6 +6,7 @@ 14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video 41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED 42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED 42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol @@ -34,9 +35,9 @@ 6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH 45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH -45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH 16,0,ESKY,Std,0,Gyro,Pitch 16,1,ESKY,ET4,0,Gyro,Pitch 35,0,ESKY150,4CH,0 @@ -54,6 +55,7 @@ 28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 53,0,Height,5ch,0,Gear 53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 @@ -79,6 +81,8 @@ 65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 55,1,FrSkyRX,CloneTX,0 +55,2,FrSkyRX,EraseTX,0 +55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 58,0,FX816,P38,1 20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib 20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib @@ -99,13 +103,14 @@ 26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib -57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess 2,1,Hubsan,H301,0,RTH,Light,Stab,Video 2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes 22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -71,0,JJRC345,Std,1,Flip,HLess,RTH +71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 49,0,KF606,Std,1,Trim 9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim 9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim @@ -122,6 +127,8 @@ 17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess +17,5,MT99XX,A180,0,3D_6G +17,6,MT99XX,Dragon,0,Mode,RTH 44,0,NCC1701,Std,1,Warp 77,0,OMP,M2,0,THold,IdleUp,6G_3D 60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 @@ -138,10 +145,11 @@ 72,0,Q90C,Std,0,FMode,VTX+ 74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 76,0,Realacc,R11,1,Flip,Light,Calib,HLess,RTH,UNK 50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 +21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8 19,0,Shenqi,Cycle,1 68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch @@ -174,3 +182,11 @@ 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess 52,0,ZSX,280,1,Light +78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10 +80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH +81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE +82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe +83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR +84,0,JOYSWAY,Std,0 +85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiLOLI.lua b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiLOLI.lua new file mode 100644 index 000000000..4d309e37e --- /dev/null +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiLOLI.lua @@ -0,0 +1,221 @@ + +local toolName = "TNS|Multi LOLI RX config|TNE" + +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local loli_nok = false +local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4 + { 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7 + { -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8 + +local sel = 1 +local edit = false + +local blink = 0 +local BLINK_SPEED = 15 + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +local function LOLI_Draw_LCD(event) + local line = 0 + + lcd.clear() + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + drawScreenTitle("Multi - LOLI RX configuration tool") + x_pos = 152 + x_inc = 90 + y_pos = 40 + y_inc = 20 + else + x_pos = 5 + x_inc = 30 + y_pos = 1 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if loli_nok then + if LCD_W == 480 then + lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE) + end + return + end + + --Display current config + if LCD_W == 480 then + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt) + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt) + lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188) + lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0) + lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0) + line = line + 1 + end + + local out + for i = 1, 8 do + out = getValue("ch"..(i+8)) + lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt) + for j = 1, #channels[i], 3 do + if out > channels[i][j] then + if sel == i then + invert = INVERS + if edit == true then + blink = blink + 1 + if blink > BLINK_SPEED then + invert = 0 + if blink > BLINK_SPEED * 2 then + blink = 0 + end + end + end + else + invert = 0 + end + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert) + break + end + end + line = line + 1 + end +end + +local function LOLI_Change_Value(dir) + local pos = 0 + local out + --look for the current position + out = getValue("ch"..(sel+8)) + for j = 1, #channels[sel], 3 do + if out > channels[sel][j] then + pos = j + break + end + end + + --decrement or increment + if dir < 0 and pos > 1 then + pos = pos - 3 + elseif dir > 0 and pos + 3 < #channels[sel] then + pos = pos + 3 + else + return + end + + --delete all mixers for the selected channel + local num_mix = model.getMixesCount(sel-1 +8) + for i = 1, num_mix do + model.deleteMix(sel-1 +8, 0); + end + + --create new mixer + local source_max = getFieldInfo("cyc1") + + local val = { name = channels[sel][pos+1], + source = source_max.id - 1, -- MAX=100 on TX16S + weight = channels[sel][pos+2], + offset = 0, + switch = 0, + multiplex = 0, + curveType = 0, + curveValue = 0, + flightModes = 0, + carryTrim = false, + mixWarn = 0, + delayUp = 0, + delayDown = 0, + speedUp = 0, + speedDown = 0 } + model.insertMix(sel-1 +8, 0, val) +end + +local function LOLI_Menu(event) + if event == EVT_VIRTUAL_NEXT then + if edit == false then + -- not changing a value + if sel < 8 then + sel = sel + 1 + end + else + -- need to inc the value + LOLI_Change_Value(1) + end + elseif event == EVT_VIRTUAL_PREV then + if edit == false then + -- not changing a value + if sel > 1 then + sel = sel - 1 + end + else + -- need to dec the value + LOLI_Change_Value(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if edit == false then + edit = true + blink = BLINK_SPEED + else + edit = false + end + end +end + +-- Init +local function LOLI_Init() + local module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + loli_nok = true + end + end +end + +-- Main +local function LOLI_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + return 2 + else + LOLI_Menu(event) + LOLI_Draw_LCD(event) + return 0 + end +end + +return { init=LOLI_Init, run=LOLI_Run } diff --git a/radio/sdcard/taranis-x7/UTILITIES/MultiConfigExternal.lua b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigExternal.lua new file mode 100644 index 000000000..a8baf3d13 --- /dev/null +++ b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigExternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(1, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(1) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(1, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x7/UTILITIES/MultiConfigInternal.lua b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigInternal.lua new file mode 100644 index 000000000..29ced006c --- /dev/null +++ b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigInternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(0, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(0) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(0, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x7/SxR Calibrate/SxR_Calibrate.lua b/radio/sdcard/taranis-x7/UTILITIES/SxR Calibrate/SxR_Calibrate.lua similarity index 100% rename from radio/sdcard/taranis-x7/SxR Calibrate/SxR_Calibrate.lua rename to radio/sdcard/taranis-x7/UTILITIES/SxR Calibrate/SxR_Calibrate.lua diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt index b03b2c860..305451422 100644 --- a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt @@ -6,6 +6,7 @@ 14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video 41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED 42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED 42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol @@ -34,9 +35,9 @@ 6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH 45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH -45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH 16,0,ESKY,Std,0,Gyro,Pitch 16,1,ESKY,ET4,0,Gyro,Pitch 35,0,ESKY150,4CH,0 @@ -54,6 +55,7 @@ 28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 53,0,Height,5ch,0,Gear 53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 @@ -79,6 +81,8 @@ 65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 55,1,FrSkyRX,CloneTX,0 +55,2,FrSkyRX,EraseTX,0 +55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 58,0,FX816,P38,1 20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib 20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib @@ -99,13 +103,14 @@ 26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib -57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess 2,1,Hubsan,H301,0,RTH,Light,Stab,Video 2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes 22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -71,0,JJRC345,Std,1,Flip,HLess,RTH +71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 49,0,KF606,Std,1,Trim 9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim 9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim @@ -122,6 +127,8 @@ 17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess +17,5,MT99XX,A180,0,3D_6G +17,6,MT99XX,Dragon,0,Mode,RTH 44,0,NCC1701,Std,1,Warp 77,0,OMP,M2,0,THold,IdleUp,6G_3D 60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 @@ -138,10 +145,11 @@ 72,0,Q90C,Std,0,FMode,VTX+ 74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 76,0,Realacc,R11,1,Flip,Light,Calib,HLess,RTH,UNK 50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 +21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8 19,0,Shenqi,Cycle,1 68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch @@ -174,3 +182,11 @@ 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess 52,0,ZSX,280,1,Light +78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10 +80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH +81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE +82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe +83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR +84,0,JOYSWAY,Std,0 +85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiLOLI.lua b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiLOLI.lua new file mode 100644 index 000000000..4d309e37e --- /dev/null +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiLOLI.lua @@ -0,0 +1,221 @@ + +local toolName = "TNS|Multi LOLI RX config|TNE" + +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local loli_nok = false +local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4 + { 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7 + { -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8 + +local sel = 1 +local edit = false + +local blink = 0 +local BLINK_SPEED = 15 + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +local function LOLI_Draw_LCD(event) + local line = 0 + + lcd.clear() + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + drawScreenTitle("Multi - LOLI RX configuration tool") + x_pos = 152 + x_inc = 90 + y_pos = 40 + y_inc = 20 + else + x_pos = 5 + x_inc = 30 + y_pos = 1 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if loli_nok then + if LCD_W == 480 then + lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE) + end + return + end + + --Display current config + if LCD_W == 480 then + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt) + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt) + lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188) + lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0) + lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0) + line = line + 1 + end + + local out + for i = 1, 8 do + out = getValue("ch"..(i+8)) + lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt) + for j = 1, #channels[i], 3 do + if out > channels[i][j] then + if sel == i then + invert = INVERS + if edit == true then + blink = blink + 1 + if blink > BLINK_SPEED then + invert = 0 + if blink > BLINK_SPEED * 2 then + blink = 0 + end + end + end + else + invert = 0 + end + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert) + break + end + end + line = line + 1 + end +end + +local function LOLI_Change_Value(dir) + local pos = 0 + local out + --look for the current position + out = getValue("ch"..(sel+8)) + for j = 1, #channels[sel], 3 do + if out > channels[sel][j] then + pos = j + break + end + end + + --decrement or increment + if dir < 0 and pos > 1 then + pos = pos - 3 + elseif dir > 0 and pos + 3 < #channels[sel] then + pos = pos + 3 + else + return + end + + --delete all mixers for the selected channel + local num_mix = model.getMixesCount(sel-1 +8) + for i = 1, num_mix do + model.deleteMix(sel-1 +8, 0); + end + + --create new mixer + local source_max = getFieldInfo("cyc1") + + local val = { name = channels[sel][pos+1], + source = source_max.id - 1, -- MAX=100 on TX16S + weight = channels[sel][pos+2], + offset = 0, + switch = 0, + multiplex = 0, + curveType = 0, + curveValue = 0, + flightModes = 0, + carryTrim = false, + mixWarn = 0, + delayUp = 0, + delayDown = 0, + speedUp = 0, + speedDown = 0 } + model.insertMix(sel-1 +8, 0, val) +end + +local function LOLI_Menu(event) + if event == EVT_VIRTUAL_NEXT then + if edit == false then + -- not changing a value + if sel < 8 then + sel = sel + 1 + end + else + -- need to inc the value + LOLI_Change_Value(1) + end + elseif event == EVT_VIRTUAL_PREV then + if edit == false then + -- not changing a value + if sel > 1 then + sel = sel - 1 + end + else + -- need to dec the value + LOLI_Change_Value(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if edit == false then + edit = true + blink = BLINK_SPEED + else + edit = false + end + end +end + +-- Init +local function LOLI_Init() + local module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + loli_nok = true + end + end +end + +-- Main +local function LOLI_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + return 2 + else + LOLI_Menu(event) + LOLI_Draw_LCD(event) + return 0 + end +end + +return { init=LOLI_Init, run=LOLI_Run } diff --git a/radio/sdcard/taranis-x9/UTILITIES/MultiConfigExternal.lua b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigExternal.lua new file mode 100644 index 000000000..a8baf3d13 --- /dev/null +++ b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigExternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(1, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(1) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(1, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x9/UTILITIES/MultiConfigInternal.lua b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigInternal.lua new file mode 100644 index 000000000..29ced006c --- /dev/null +++ b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigInternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(0, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(0) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(0, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/SxR_Calibrate.lua b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/SxR_Calibrate.lua similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/SxR_Calibrate.lua rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/SxR_Calibrate.lua diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/back.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/back.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/back.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/back.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/delta.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/delta.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/delta.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/delta.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/done.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/done.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/done.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/done.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/down.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/down.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/down.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/down.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/forward.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/forward.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/forward.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/forward.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz-r.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz-r.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz-r.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz-r.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/left.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/left.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/left.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/left.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/plane.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/plane.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/plane.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/plane.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/right.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/right.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/right.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/right.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/up.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/up.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/up.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/up.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert-r.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert-r.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert-r.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert-r.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/vtail.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vtail.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/vtail.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vtail.bmp diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 02fd5acb4..da1998e4b 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -528,6 +528,11 @@ void audioTask(void * pdata) setSampleRate(AUDIO_SAMPLE_RATE); +#if defined(PCBX12S) || defined(RADIO_TX16S) + // The audio amp needs ~2s to start + RTOS_WAIT_MS(1000); // 1s +#endif + if (!globalData.unexpectedShutdown) { AUDIO_HELLO(); } diff --git a/radio/src/bitmaps/480x272/mask_topmenu_gps_18.png b/radio/src/bitmaps/480x272/mask_topmenu_gps_18.png new file mode 100644 index 000000000..8e3ee9cfb Binary files /dev/null and b/radio/src/bitmaps/480x272/mask_topmenu_gps_18.png differ diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index b11fb6bcc..f7bee9f85 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -863,6 +863,7 @@ enum Functions { FUNC_LOGS, FUNC_BACKLIGHT, FUNC_SCREENSHOT, + FUNC_RACING_MODE, #if defined(DEBUG) FUNC_TEST, // should remain the last before MAX as not added in Companion #endif diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 69bb23166..93bba22e9 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -456,7 +456,8 @@ PACK(struct ModuleData { int8_t refreshRate; // definition as framelength for ppm (* 5 + 225 = time in 1/10 ms) } sbus); NOBACKUP(struct { - uint8_t receivers; // 5 bits spare + uint8_t receivers:7; // 4 bits spare + uint8_t racingMode:1; char receiverName[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME]; } pxx2); #if defined(AFHDS2) @@ -489,14 +490,21 @@ PACK(struct ModuleData { } NAME(mod) FUNC(select_mod_type); // Helper functions to set both of the rfProto protocol at the same time - NOBACKUP(inline uint8_t getMultiProtocol() { + NOBACKUP(inline uint8_t getMultiProtocol() const + { return ((uint8_t) (rfProtocol & 0x0F)) + (multi.rfProtocolExtra << 4); }) - NOBACKUP(inline void setMultiProtocol(uint8_t proto) { + NOBACKUP(inline void setMultiProtocol(uint8_t proto) + { rfProtocol = (uint8_t) (proto & 0x0F); multi.rfProtocolExtra = (proto & 0x70) >> 4; }) + + NOBACKUP(inline uint8_t getChannelsCount() const + { + return channelsCount + 8; + }) }); /* diff --git a/radio/src/functions.cpp b/radio/src/functions.cpp index 76e903d11..a3dc0882c 100644 --- a/radio/src/functions.cpp +++ b/radio/src/functions.cpp @@ -379,10 +379,18 @@ void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext case FUNC_SCREENSHOT: if (!(functionsContext.activeSwitches & switch_mask)) { - mainRequestFlags |= (1 << REQUEST_SCREENSHOT); + mainRequestFlags |= (1u << REQUEST_SCREENSHOT); } break; +#if defined(PXX2) + case FUNC_RACING_MODE: + if (isRacingModeEnabled()) { + newActiveFunctions |= (1u << FUNCTION_RACING_MODE); + } + break; +#endif + #if defined(DEBUG) case FUNC_TEST: testFunc(); diff --git a/radio/src/gui/128x64/model_select.cpp b/radio/src/gui/128x64/model_select.cpp index b202b4079..a42b22fb7 100644 --- a/radio/src/gui/128x64/model_select.cpp +++ b/radio/src/gui/128x64/model_select.cpp @@ -244,7 +244,7 @@ void menuModelSelect(event_t event) #endif #endif -#if defined(ROTARY_ENCODER) && defined(NAVIGATION_X7) +#if defined(ROTARY_ENCODER_NAVIGATION) && defined(NAVIGATION_X7) case EVT_ROTARY_LEFT: case EVT_ROTARY_RIGHT: #endif diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 0b013085e..a3a2656f4 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -95,6 +95,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS, #endif ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS, + ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE, ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM, ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS, @@ -347,6 +348,7 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) MULTIMODULE_SUBTYPE_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_SUBTYPE */ \ MULTIMODULE_STATUS_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS */ \ MODULE_CHANNELS_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS */ \ + IF_ALLOW_RACING_MODE(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE */ \ IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, MODULE_BIND_ROWS(INTERNAL_MODULE)), /* *ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE */\ IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM */ \ MODULE_OPTION_ROW(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS */ \ @@ -1217,6 +1219,16 @@ void menuModelSetup(event_t event) break; } +#if defined(HARDWARE_INTERNAL_MODULE) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE: + { + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcdDrawText(INDENT_WIDTH, y, STR_RACING_MODE); + moduleData.pxx2.racingMode = editCheckBox(moduleData.pxx2.racingMode, MODEL_SETUP_2ND_COLUMN, y, "", attr, event); + break; + } +#endif + #if defined(PCBX7) || defined(PCBX9LITE) || defined(PCBXLITE) case ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS: lcdDrawTextAlignedLeft(y, STR_PPMFRAME); @@ -1497,7 +1509,6 @@ void menuModelSetup(event_t event) if (event == EVT_KEY_LONG(KEY_ENTER)) { killEvents(event); setCustomFailsafe(moduleIdx); - storageDirty(EE_MODEL); AUDIO_WARNING1(); SEND_FAILSAFE_NOW(moduleIdx); } diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 0dd9d410d..a1ad6cd7a 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -87,6 +87,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL, ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE, ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS, + ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE, ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM, ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE, @@ -362,6 +363,7 @@ void menuModelSetup(event_t event) LABEL(InternalModule), INTERNAL_MODULE_TYPE_ROWS, MODULE_CHANNELS_ROWS(INTERNAL_MODULE), + IF_ALLOW_RACING_MODE(INTERNAL_MODULE), IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, MODULE_BIND_ROWS(INTERNAL_MODULE)), // RxNum, [Bind] and [Range] buttons IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), // RxNum for ACCESS IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), // Failsafe @@ -1098,6 +1100,15 @@ void menuModelSetup(event_t event) break; } + case ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE: + { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcdDrawText(INDENT_WIDTH, y, STR_RACING_MODE); + moduleData.pxx2.racingMode = editCheckBox(moduleData.pxx2.racingMode, MODEL_SETUP_2ND_COLUMN, y, "", attr, event); + break; + } + case ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS: lcdDrawTextAlignedLeft(y, STR_PPMFRAME); lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.trainerData.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT); @@ -1270,7 +1281,6 @@ void menuModelSetup(event_t event) if (event == EVT_KEY_LONG(KEY_ENTER)) { killEvents(event); setCustomFailsafe(moduleIdx); - storageDirty(EE_MODEL); AUDIO_WARNING1(); SEND_FAILSAFE_NOW(moduleIdx); } diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index b7000a10e..79af1169c 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -95,15 +95,9 @@ endif() if(LUA STREQUAL YES) set(SRC ${SRC} lua/widgets.cpp) - # TODO set(GUI_SRC ${GUI_SRC} model_custom_scripts.cpp) endif() -set(SRC - ${SRC} - stubs.cpp - storage/modelslist.cpp - ) - +set(SRC ${SRC} storage/modelslist.cpp) option(WINDOWS_INSPECT_BORDERS "Draw bounding boxes around windows" OFF) if(WINDOWS_INSPECT_BORDERS) diff --git a/radio/src/gui/colorlcd/bitmaps.cpp b/radio/src/gui/colorlcd/bitmaps.cpp index a66602f93..c079a4225 100644 --- a/radio/src/gui/colorlcd/bitmaps.cpp +++ b/radio/src/gui/colorlcd/bitmaps.cpp @@ -32,6 +32,10 @@ const uint8_t LBM_DOT[] = { * Main view bitmaps */ +const uint8_t LBM_TOPMENU_GPS[] = { +#include "mask_topmenu_gps_18.lbm" +}; + const uint8_t LBM_TOPMENU_USB[] = { #include "mask_topmenu_usb.lbm" }; diff --git a/radio/src/gui/colorlcd/bitmaps.h b/radio/src/gui/colorlcd/bitmaps.h index 7c163867c..3c6512153 100644 --- a/radio/src/gui/colorlcd/bitmaps.h +++ b/radio/src/gui/colorlcd/bitmaps.h @@ -25,6 +25,7 @@ extern const uint8_t LBM_DOT[]; // Main view icons +extern const uint8_t LBM_TOPMENU_GPS[]; extern const uint8_t LBM_TOPMENU_USB[]; extern const uint8_t LBM_TOPMENU_ANTENNA[]; extern const uint8_t LBM_TOPMENU_VOLUME_0[]; diff --git a/radio/src/gui/colorlcd/colors.h b/radio/src/gui/colorlcd/colors.h index 7a9c76604..8d0991676 100644 --- a/radio/src/gui/colorlcd/colors.h +++ b/radio/src/gui/colorlcd/colors.h @@ -101,7 +101,7 @@ inline void lcdSetColor(uint16_t color) lcdColorTable[CUSTOM_COLOR_INDEX] = color; } -#define COLOR(index) (lcdColorTable[ unsigned(index) & 0xFF ] << 16u) +#define COLOR(index) ((uint32_t)lcdColorTable[ unsigned(index) & 0xFF ] << 16u) #define ALARM_COLOR COLOR(ALARM_COLOR_INDEX) #define BARGRAPH1_COLOR COLOR(BARGRAPH1_COLOR_INDEX) diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index f79c9b0dd..65f07c39b 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -76,7 +76,6 @@ void drawValueOrGVar(BitmapBuffer * dc, coord_t x, coord_t y, gvar_t value, gvar void drawSleepBitmap() { - lcdNextLayer(); lcd->reset(); lcd->clear(); @@ -89,39 +88,95 @@ void drawSleepBitmap() lcdRefresh(); } +// +// TODO: move shutdown animation to Theme +// #define SHUTDOWN_CIRCLE_DIAMETER 150 void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char * message) { if (totalDuration == 0) return; - static const BitmapBuffer * shutdown = BitmapBuffer::loadBitmap(OpenTxTheme::instance()->getFilePath("shutdown.bmp")); + static const BitmapBuffer* shutdown = nullptr; + if (!shutdown) { + shutdown = BitmapBuffer::loadBitmap( + OpenTxTheme::instance()->getFilePath("shutdown.bmp")); + } - lcdNextLayer(); lcd->reset(); if (shutdown) { OpenTxTheme::instance()->drawBackground(lcd); - lcd->drawBitmap((LCD_W - shutdown->width()) / 2, (LCD_H - shutdown->height()) / 2, shutdown); + lcd->drawBitmap((LCD_W - shutdown->width()) / 2, + (LCD_H - shutdown->height()) / 2, shutdown); int quarter = duration / (totalDuration / 5); - if (quarter >= 1) lcd->drawBitmapPattern(LCD_W/2, (LCD_H-SHUTDOWN_CIRCLE_DIAMETER)/2, LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, 0, SHUTDOWN_CIRCLE_DIAMETER/2); - if (quarter >= 2) lcd->drawBitmapPattern(LCD_W/2, LCD_H/2, LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, SHUTDOWN_CIRCLE_DIAMETER/2, SHUTDOWN_CIRCLE_DIAMETER/2); - if (quarter >= 3) lcd->drawBitmapPattern((LCD_W-SHUTDOWN_CIRCLE_DIAMETER)/2, LCD_H/2, LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, SHUTDOWN_CIRCLE_DIAMETER, SHUTDOWN_CIRCLE_DIAMETER/2); - if (quarter >= 4) lcd->drawBitmapPattern((LCD_W-SHUTDOWN_CIRCLE_DIAMETER)/2, (LCD_H-SHUTDOWN_CIRCLE_DIAMETER)/2, LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, SHUTDOWN_CIRCLE_DIAMETER*3/2, SHUTDOWN_CIRCLE_DIAMETER/2); - } - else { + if (quarter >= 1) + lcd->drawBitmapPattern(LCD_W / 2, (LCD_H - SHUTDOWN_CIRCLE_DIAMETER) / 2, + LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, 0, + SHUTDOWN_CIRCLE_DIAMETER / 2); + if (quarter >= 2) + lcd->drawBitmapPattern(LCD_W / 2, LCD_H / 2, LBM_SHUTDOWN_CIRCLE, + DEFAULT_COLOR, SHUTDOWN_CIRCLE_DIAMETER / 2, + SHUTDOWN_CIRCLE_DIAMETER / 2); + if (quarter >= 3) + lcd->drawBitmapPattern((LCD_W - SHUTDOWN_CIRCLE_DIAMETER) / 2, LCD_H / 2, + LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, + SHUTDOWN_CIRCLE_DIAMETER, + SHUTDOWN_CIRCLE_DIAMETER / 2); + if (quarter >= 4) + lcd->drawBitmapPattern((LCD_W - SHUTDOWN_CIRCLE_DIAMETER) / 2, + (LCD_H - SHUTDOWN_CIRCLE_DIAMETER) / 2, + LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, + SHUTDOWN_CIRCLE_DIAMETER * 3 / 2, + SHUTDOWN_CIRCLE_DIAMETER / 2); + } else { lcd->clear(); int quarter = duration / (totalDuration / 5); for (int i = 1; i <= 4; i++) { if (quarter >= i) { - lcd->drawSolidFilledRect(LCD_W / 2 - 70 + 24 * i, LCD_H / 2 - 10, 20, 20, DEFAULT_BGCOLOR); + lcd->drawSolidFilledRect(LCD_W / 2 - 70 + 24 * i, LCD_H / 2 - 10, 20, + 20, DEFAULT_BGCOLOR); } } } + WDG_RESET(); lcdRefresh(); } +void drawFatalErrorScreen(const char * message) +{ + lcd->reset(); + lcd->clear(COLOR2FLAGS(BLACK)); + lcd->drawText(LCD_W/2, LCD_H/2-20, message, FONT(XL)|CENTERED|COLOR2FLAGS(WHITE)); + + WDG_RESET(); + lcdRefresh(); +} + +void runFatalErrorScreen(const char * message) +{ + while (true) { + backlightEnable(100); + drawFatalErrorScreen(message); + + uint8_t refresh = false; + while (true) { + uint32_t pwr_check = pwrCheck(); + if (pwr_check == e_power_off) { + boardOff(); + return; // only happens in SIMU, required for proper shutdown + } + else if (pwr_check == e_power_press) { + refresh = true; + } + else if (pwr_check == e_power_on && refresh) { + break; + } + } + } +} + void drawCurveRef(BitmapBuffer * dc, coord_t x, coord_t y, const CurveRef & curve, LcdFlags flags) { if (curve.value != 0) { @@ -431,4 +486,4 @@ void drawHexNumber(BitmapBuffer * dc, coord_t x, coord_t y, uint32_t val, LcdFla c += (c >= 10 ? 'A' - 10 : '0'); x = dc->drawSizedText(x, y, &c, 1, flags); } -} \ No newline at end of file +} diff --git a/radio/src/gui/colorlcd/fonts.cpp b/radio/src/gui/colorlcd/fonts.cpp index 8468a74aa..54cf25236 100644 --- a/radio/src/gui/colorlcd/fonts.cpp +++ b/radio/src/gui/colorlcd/fonts.cpp @@ -131,8 +131,8 @@ const unsigned char font_xxl[] = { const uint16_t fontCharactersTable[FONTS_COUNT] = { sizeof(font_std_en_specs)/2-2 }; const uint16_t * const fontspecsTable[FONTS_COUNT] = { font_std_en_specs }; const uint8_t * fontsTable[FONTS_COUNT] = { font_std_en }; +const int fontsSizeTable[FONTS_COUNT] = { sizeof(font_std_en) }; #else - // -2 for: overall length and last boundary const uint16_t fontCharactersTable[FONTS_COUNT] = { sizeof(font_std_specs)/2-2, sizeof(font_bold_specs)/2-2, sizeof(font_xxs_specs)/2-2, @@ -146,21 +146,42 @@ const uint16_t *const fontspecsTable[FONTS_COUNT] = { const uint8_t *fontsTable[FONTS_COUNT] = { font_std, font_bold, font_xxs, font_xs, font_l, font_xl, font_xxl }; +const int fontsSizeTable[FONTS_COUNT] = { + sizeof(font_std), sizeof(font_bold), sizeof(font_xxs), + sizeof(font_xs), sizeof(font_l), sizeof(font_xl), + sizeof(font_xxl) +}; #endif -uint8_t * decompressFont(const uint8_t * font) +uint8_t * decompressFont(const uint8_t * font, unsigned len) { - uint16_t width = *((uint16_t *)font); - uint16_t height = *(((uint16_t *)font)+1); + int width = 0; + int height = 0; + + uint8_t* raw_font = BitmapBuffer::loadFont(font, len, width, height); + if (!raw_font) { + return nullptr; + } size_t font_size = width * height; - uint8_t * dec_buf = (uint8_t *)malloc(font_size + 4); + uint8_t * buf = (uint8_t *)malloc(font_size + 4); - // copy width / height - memcpy(dec_buf, font, 4); + ((uint16_t*)buf)[0] = (uint16_t)width; + ((uint16_t*)buf)[1] = (uint16_t)height; - RLEBitmap::decode(dec_buf+4, font_size, font+4); - return dec_buf; +#if defined(LCD_VERTICAL_INVERT) + uint8_t* src = raw_font + font_size - 1; + for(uint8_t* dst = buf + 4; src >= raw_font; --src, ++dst) { + *dst = 0xFF - *src; + } +#else + uint8_t* src = raw_font; + for(uint8_t* dst = buf + 4; src != raw_font + font_size; ++src, ++dst) { + *dst = 0xFF - *src; + } +#endif + + return buf; } void loadFonts() @@ -170,10 +191,10 @@ void loadFonts() return; #if defined(BOOT) - fontsTable[0] = decompressFont(fontsTable[0]); + fontsTable[0] = decompressFont(fontsTable[0], fontsSizeTable[0]); #else for (int i = 0; i < FONTS_COUNT; i++) { - fontsTable[i] = decompressFont(fontsTable[i]); + fontsTable[i] = decompressFont(fontsTable[i], fontsSizeTable[i]); } #endif diff --git a/radio/src/gui/colorlcd/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/fullscreen_dialog.cpp index 6e8742086..4236c7523 100644 --- a/radio/src/gui/colorlcd/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/fullscreen_dialog.cpp @@ -23,23 +23,23 @@ #include "opentx.h" #include "libopenui.h" -FullScreenDialog::FullScreenDialog(uint8_t type, std::string title, std::string message, std::string action, const std::function & confirmHandler): - FormGroup(MainWindow::instance(), {0, 0, LCD_W, LCD_H}, OPAQUE), - type(type), - title(std::move(title)), - message(std::move(message)), - action(std::move(action)), - confirmHandler(confirmHandler) +FullScreenDialog::FullScreenDialog( + uint8_t type, std::string title, std::string message, std::string action, + const std::function& confirmHandler) : + FormGroup(MainWindow::instance(), {0, 0, LCD_W, LCD_H}, OPAQUE), + type(type), + title(std::move(title)), + message(std::move(message)), + action(std::move(action)), + confirmHandler(confirmHandler) { Layer::push(this); #if defined(HARDWARE_TOUCH) - new FabButton(this, LCD_W - 50, LCD_H - 50, ICON_NEXT, - [=]() -> uint8_t { - if (confirmHandler) - confirmHandler(); - return 0; - }); + new FabButton(this, LCD_W - 50, LCD_H - 50, ICON_NEXT, [=]() -> uint8_t { + if (confirmHandler) confirmHandler(); + return 0; + }); #endif bringToTop(); @@ -50,34 +50,58 @@ void FullScreenDialog::paint(BitmapBuffer * dc) { OpenTxTheme::instance()->drawBackground(dc); - dc->drawFilledRect(0, ALERT_FRAME_TOP, LCD_W, ALERT_FRAME_HEIGHT, SOLID, FOCUS_COLOR, OPACITY(8)); + dc->drawFilledRect(0, ALERT_FRAME_TOP, LCD_W, ALERT_FRAME_HEIGHT, SOLID, + FOCUS_COLOR, OPACITY(8)); - if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, OpenTxTheme::instance()->asterisk); - else if (type == WARNING_TYPE_INFO) - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, OpenTxTheme::instance()->busy); - else // confirmation - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, OpenTxTheme::instance()->question); + if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) { + dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->asterisk); + } + else if (type == WARNING_TYPE_INFO) { + dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->busy); + } + else { // confirmation + dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->question); + } if (type == WARNING_TYPE_ALERT) { -#if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || defined(TRANSLATIONS_CZ) - dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP, STR_WARNING, ALARM_COLOR|FONT(XL)); - dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP + ALERT_TITLE_LINE_HEIGHT, title.c_str(), ALARM_COLOR|FONT(XL)); +#if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || \ + defined(TRANSLATIONS_CZ) + dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP, STR_WARNING, + ALARM_COLOR | FONT(XL)); + dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP + ALERT_TITLE_LINE_HEIGHT, + title.c_str(), ALARM_COLOR | FONT(XL)); #else - dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP, title.c_str(), ALARM_COLOR|FONT(XL)); - dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP + ALERT_TITLE_LINE_HEIGHT, STR_WARNING, ALARM_COLOR|FONT(XL)); + dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP, title.c_str(), + ALARM_COLOR | FONT(XL)); + dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP + ALERT_TITLE_LINE_HEIGHT, + STR_WARNING, ALARM_COLOR | FONT(XL)); #endif - } - else if (!title.empty()) { - dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP, title.c_str(), ALARM_COLOR|FONT(XL)); + } else if (!title.empty()) { + dc->drawText(ALERT_TITLE_LEFT, ALERT_TITLE_TOP, title.c_str(), + ALARM_COLOR | FONT(XL)); } if (!message.empty()) { - dc->drawText(ALERT_MESSAGE_LEFT, ALERT_MESSAGE_TOP, message.c_str(), FONT(BOLD)); + dc->drawText(ALERT_MESSAGE_LEFT, ALERT_MESSAGE_TOP, message.c_str(), + FONT(BOLD)); } if (!action.empty()) { - dc->drawText(LCD_W / 2, ALERT_ACTION_TOP, action.c_str(), CENTERED | FONT(BOLD)); + dc->drawText(LCD_W / 2, ALERT_ACTION_TOP, action.c_str(), + CENTERED | FONT(BOLD)); + } else if (type == WARNING_TYPE_CONFIRM) { + + LcdFlags flags = FONT(BOLD); + int w = getTextWidth(STR_OK, flags); + + dc->drawText(ALERT_MESSAGE_LEFT, ALERT_ACTION_TOP, + STR_OK, flags); + + dc->drawText(ALERT_MESSAGE_LEFT + w + 20, ALERT_ACTION_TOP, + STR_EXIT, flags); } } @@ -145,7 +169,22 @@ void FullScreenDialog::runForever() checkBacklight(); WDG_RESET(); - RTOS_WAIT_MS(20); + RTOS_WAIT_MS(1); + MainWindow::instance()->run(false); + } + + deleteLater(); +} + +void FullScreenDialog::runForeverNoPwrCheck() +{ + running = true; + + while (running) { + checkBacklight(); + WDG_RESET(); + + RTOS_WAIT_MS(1); MainWindow::instance()->run(false); } @@ -155,6 +194,23 @@ void FullScreenDialog::runForever() void raiseAlert(const char * title, const char * msg, const char * action, uint8_t sound) { AUDIO_ERROR_MESSAGE(sound); - auto dialog = new FullScreenDialog(WARNING_TYPE_ALERT, title ? title : "", msg ? msg : "", action ? action : ""); + auto dialog = new FullScreenDialog(WARNING_TYPE_ALERT, title ? title : "", + msg ? msg : "", action ? action : ""); dialog->runForever(); } + +// POPUP_CONFIRMATION +bool confirmationDialog(const char* title, const char* msg, bool checkPwr) +{ + bool confirmed = false; + auto dialog = new FullScreenDialog(WARNING_TYPE_CONFIRM, title ? title : "", + msg ? msg : "", "", + [&confirmed]() { confirmed = true; }); + if (checkPwr) { + dialog->runForever(); + } else { + dialog->runForeverNoPwrCheck(); + } + + return confirmed; +} diff --git a/radio/src/gui/colorlcd/fullscreen_dialog.h b/radio/src/gui/colorlcd/fullscreen_dialog.h index aeb1b9140..c97fc81a7 100644 --- a/radio/src/gui/colorlcd/fullscreen_dialog.h +++ b/radio/src/gui/colorlcd/fullscreen_dialog.h @@ -34,7 +34,9 @@ enum DialogType { class FullScreenDialog : public FormGroup { public: - FullScreenDialog(uint8_t type, std::string title, std::string message = "", std::string action = "", const std::function & confirmHandler = nullptr); + FullScreenDialog(uint8_t type, std::string title, std::string message = "", + std::string action = "", + const std::function& confirmHandler = nullptr); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -69,6 +71,7 @@ class FullScreenDialog : public FormGroup } void runForever(); + void runForeverNoPwrCheck(); protected: uint8_t type; diff --git a/radio/src/gui/colorlcd/layouts/sliders.cpp b/radio/src/gui/colorlcd/layouts/sliders.cpp index 08a9fcc8c..0a31172b1 100644 --- a/radio/src/gui/colorlcd/layouts/sliders.cpp +++ b/radio/src/gui/colorlcd/layouts/sliders.cpp @@ -35,7 +35,7 @@ void MainViewHorizontalSlider::paint(BitmapBuffer * dc) } // The square - x = width() - TRIM_SQUARE_SIZE - divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); + x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); drawTrimSquare(dc, x, 0, TRIM_BGCOLOR); } @@ -58,19 +58,19 @@ void MainView6POS::paint(BitmapBuffer * dc) void MainViewVerticalSlider::paint(BitmapBuffer * dc) { - uint8_t slidersTick = height() / 5; // The ticks - int delta = (height() - TRIM_SQUARE_SIZE) / slidersTick; + int delta = 4; + int sliderTicksCount = (height() - TRIM_SQUARE_SIZE) / delta; coord_t y = TRIM_SQUARE_SIZE / 2; - for (uint8_t i = 0; i <= slidersTick; i++) { - if (i == 0 || i == slidersTick / 2 || i == slidersTick) - dc->drawSolidHorizontalLine(2, y, 13, DEFAULT_COLOR); + for (uint8_t i = 0; i <= sliderTicksCount; i++) { + if (i == 0 || i == sliderTicksCount / 2 || i == sliderTicksCount) + dc->drawSolidHorizontalLine(2, y, 13, DEFAULT_COLOR); else dc->drawSolidHorizontalLine(4, y, 9, DEFAULT_COLOR); y += delta; } // The square - y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); + y = divRoundClosest((height() - TRIM_SQUARE_SIZE) * (-value + RESX), 2 * RESX); drawTrimSquare(dc, 0, y, TRIM_BGCOLOR); } diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp index 70cbd56dc..68d9e7378 100644 --- a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp @@ -32,6 +32,9 @@ TopbarImpl::TopbarImpl(Window * parent) : unsigned int TopbarImpl::getZonesCount() const { +#if defined(INTERNAL_GPS) + return MAX_TOPBAR_ZONES-1; +#endif return MAX_TOPBAR_ZONES; } @@ -88,13 +91,27 @@ void TopbarImpl::paint(BitmapBuffer * dc) getTimerString(str, getValue(MIXSRC_TX_TIME)); dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | MENU_COLOR); +#if defined(INTERNAL_GPS) + if (gpsData.fix) { + char s[10]; + sprintf(s, "%d", gpsData.numSat); + dc->drawText(LCD_W-148, 4, s, FONT(XS) | CENTERED | MENU_COLOR); + } + dc->drawBitmapPattern(LCD_W-158, 22, LBM_TOPMENU_GPS, (gpsData.fix) ? MENU_COLOR : MENU_TITLE_DISABLE_COLOR); +#endif + // USB icon if (usbPlugged()) { - dc->drawBitmapPattern(LCD_W - 98, 8, LBM_TOPMENU_USB, MENU_COLOR); - } + LcdFlags flags = MENU_COLOR; + if (getSelectedUsbMode() == USB_UNSELECTED_MODE) { + flags = MENU_TITLE_DISABLE_COLOR; + } + + dc->drawBitmapPattern(LCD_W - 98, 8, LBM_TOPMENU_USB, flags); + } // Logs - if (isFunctionActive(FUNCTION_LOGS) && !usbPlugged() && BLINK_ON_PHASE) { + else if (isFunctionActive(FUNCTION_LOGS) && BLINK_ON_PHASE) { dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, MENU_COLOR); } diff --git a/radio/src/gui/colorlcd/lcd.h b/radio/src/gui/colorlcd/lcd.h index 18f6e23a3..090698b0b 100644 --- a/radio/src/gui/colorlcd/lcd.h +++ b/radio/src/gui/colorlcd/lcd.h @@ -39,7 +39,6 @@ #define DISPLAY_BUFFER_SIZE (DISPLAY_PIXELS_COUNT) extern coord_t lcdNextPos; -void lcdNextLayer(); inline void lcdClear() { diff --git a/radio/src/gui/colorlcd/menus.h b/radio/src/gui/colorlcd/menus.h index e539f06e2..218d1a954 100644 --- a/radio/src/gui/colorlcd/menus.h +++ b/radio/src/gui/colorlcd/menus.h @@ -23,32 +23,8 @@ #include "keys.h" -#define MIXES_2ND_COLUMN 140 - -extern int menuVerticalPosition; -extern int menuHorizontalPosition; -extern int menuVerticalOffset; extern uint8_t menuCalibrationState; -// Temporary no highlight - -typedef bool (*MenuHandlerFunc)(event_t event); - -extern MenuHandlerFunc menuHandlers[5]; -extern uint8_t menuVerticalPositions[4]; -extern uint8_t menuLevel; -extern event_t menuEvent; - -void chainMenu(MenuHandlerFunc newMenu); -void pushMenu(MenuHandlerFunc newMenu); -void popMenu(); -void abortPopMenu(); - -inline bool isModelMenuDisplayed() -{ - return menuVerticalPositions[0] == 0; -} - enum MenuIcons { ICON_OPENTX, #if defined(HARDWARE_TOUCH) @@ -100,188 +76,6 @@ enum MenuIcons { MENUS_ICONS_COUNT }; -enum EnumTabModel { - MENU_MODEL_SETUP, - CASE_HELI(MENU_MODEL_HELI) - CASE_FLIGHT_MODES(MENU_MODEL_FLIGHT_MODES) - MENU_MODEL_INPUTS, - MENU_MODEL_MIXES, - MENU_MODEL_OUTPUTS, - MENU_MODEL_CURVES, - CASE_GVARS(MENU_MODEL_GVARS) - MENU_MODEL_LOGICAL_SWITCHES, - MENU_MODEL_SPECIAL_FUNCTIONS, -#if defined(LUA_MODEL_SCRIPTS) - MENU_MODEL_CUSTOM_SCRIPTS, -#endif - MENU_MODEL_TELEMETRY, - MENU_MODEL_PAGES_COUNT -}; - -const uint8_t MODEL_ICONS[MENU_MODEL_PAGES_COUNT + 1] = { - ICON_MODEL, - ICON_MODEL_SETUP, - CASE_HELI(ICON_MODEL_HELI) - CASE_FLIGHT_MODES(ICON_MODEL_FLIGHT_MODES) - ICON_MODEL_INPUTS, - ICON_MODEL_MIXER, - ICON_MODEL_OUTPUTS, - ICON_MODEL_CURVES, - CASE_GVARS(ICON_MODEL_GVARS) - ICON_MODEL_LOGICAL_SWITCHES, - ICON_MODEL_SPECIAL_FUNCTIONS, -#if defined(LUA_MODEL_SCRIPTS) - ICON_MODEL_LUA_SCRIPTS, -#endif - ICON_MODEL_TELEMETRY -}; - -const uint8_t STATS_ICONS[] = { - ICON_STATS, - ICON_STATS_THROTTLE_GRAPH, - ICON_STATS_DEBUG, - ICON_STATS_ANALOGS, -#if defined(DEBUG_TRACE_BUFFER) - ICON_STATS_TIMERS -#endif -}; - -const uint8_t MONITOR_ICONS[] = { - ICON_MONITOR, - ICON_MONITOR_CHANNELS1, - ICON_MONITOR_CHANNELS2, - ICON_MONITOR_CHANNELS3, - ICON_MONITOR_CHANNELS4, - ICON_MONITOR_LOGICAL_SWITCHES -}; - -bool menuModelSetup(event_t event); -bool menuModelFailsafe(event_t event); -bool menuModelHeli(event_t event); -bool menuModelFlightModesAll(event_t event); -bool menuModelExposAll(event_t event); -bool menuModelMixAll(event_t event); -bool menuModelLimits(event_t event); -bool menuModelCurvesAll(event_t event); -bool menuModelCurveOne(event_t event); -bool menuModelGVars(event_t event); -bool menuModelLogicalSwitches(event_t event); -bool menuModelSpecialFunctions(event_t event); -bool menuModelCustomScripts(event_t event); -bool menuModelTelemetryFrsky(event_t event); -bool menuModelSensor(event_t event); -bool menuModelExpoOne(event_t event); - -extern const MenuHandlerFunc menuTabModel[MENU_MODEL_PAGES_COUNT]; - -enum EnumTabRadio { - MENU_RADIO_SETUP, - MENU_RADIO_SD_MANAGER, - MENU_RADIO_TOOLS, - MENU_RADIO_SPECIAL_FUNCTIONS, - MENU_RADIO_TRAINER, - MENU_RADIO_HARDWARE, - MENU_RADIO_VERSION, - MENU_RADIO_PAGES_COUNT -}; - -const uint8_t RADIO_ICONS[MENU_RADIO_PAGES_COUNT + 1] = { - ICON_RADIO, - ICON_RADIO_SETUP, - ICON_RADIO_SD_MANAGER, - ICON_RADIO_TOOLS, - ICON_RADIO_GLOBAL_FUNCTIONS, - ICON_RADIO_TRAINER, - ICON_RADIO_HARDWARE, - ICON_RADIO_VERSION -}; - -bool menuRadioSetup(event_t event); -bool menuRadioSdManager(event_t event); -bool menuRadioTools(event_t event); -bool menuRadioSpecialFunctions(event_t event); -bool menuRadioTrainer(event_t event); -bool menuRadioVersion(event_t event); -bool menuRadioHardware(event_t event); -bool menuRadioCalibration(event_t event); - -extern const MenuHandlerFunc menuTabGeneral[MENU_RADIO_PAGES_COUNT]; - -enum MenuRadioIndexes -{ - e_StatsGraph, - e_StatsDebug, - e_StatsAnalogs, -#if defined(DEBUG_TRACE_BUFFER) - e_StatsTraces, -#endif -}; - -bool menuStatsGraph(event_t event); -bool menuStatsDebug(event_t event); -bool menuStatsAnalogs(event_t event); -bool menuStatsTraces(event_t event); - -enum EnumTabMonitors -{ - e_MonChannelsFirst, - e_MonChannels1 = e_MonChannelsFirst, - e_MonChannels2, - e_MonChannels3, - e_MonChannels4, - e_MonLogicalSwitches, - e_MonTabChannelsPagesCount -}; - -extern const MenuHandlerFunc menuTabMonitors[e_MonTabChannelsPagesCount]; -extern uint8_t lastMonitorPage; -extern void drawSingleMixerBar(coord_t, coord_t, coord_t, coord_t, uint8_t); -extern void drawSingleOutputBar(coord_t, coord_t, coord_t, coord_t, uint8_t); - -extern const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] ; - -bool menuFirstCalib(event_t event); -bool menuMainView(event_t event); -bool menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomFunctionsContext & functionsContext); -bool menuModelSelect(event_t event); -bool menuAboutView(event_t event); -bool menuMainViewChannelsMonitor(event_t event); -bool menuTextView(event_t event); -bool menuScreensTheme(event_t event); -bool menuRadioGhostModuleConfig(event_t event); -bool menuRadioPowerMeter(event_t event); - -extern int8_t checkIncDec_Ret; // global helper vars - -#define EDIT_SELECT_FIELD 0 -#define EDIT_MODIFY_FIELD 1 -#define EDIT_MODIFY_STRING 2 -extern int8_t s_editMode; // global editmode - -// checkIncDec flags -#define EE_GENERAL 0x01 -#define EE_MODEL 0x02 -#define NO_INCDEC_MARKS 0x04 -#define INCDEC_SWITCH 0x08 -#define INCDEC_SOURCE 0x10 -#define INCDEC_REP10 0x40 -#define NO_DBLKEYS 0x80 - -// mawrow special values -//#define READONLY_ROW ((uint8_t)-1) -//#define TITLE_ROW READONLY_ROW -//#define ORPHAN_ROW ((uint8_t)-2) -//#define HIDDEN_ROW ((uint8_t)-3) -//#define NAVIGATION_LINE_BY_LINE 0x40 -//#define CURSOR_ON_LINE() (menuHorizontalPosition<0) - -extern uint8_t s_currIdxSubMenu; - -extern const char * warningText; -extern const char * warningInfoText; -extern uint8_t warningInfoLength; -extern uint8_t warningResult; -extern uint8_t warningType; #define COPY_MODE 1 #define MOVE_MODE 2 @@ -302,48 +96,7 @@ void deleteMix(uint8_t idx); void insertMix(uint8_t idx); void copyMix(uint8_t source, uint8_t dest, int8_t ch); -#define MENU_X 80 -#define MENU_W LCD_W-(2*MENU_X) -#define POPUP_X 120 -#define POPUP_Y 70 -#define WARNING_LINE_LEN 32 -#define WARNING_LINE_X (POPUP_X+66) -#define WARNING_LINE_Y (POPUP_Y+9) - -void copySelection(char * dst, const char * src, uint8_t size); - -void showMessageBox(const char * title); -void runPopupWarning(event_t event); - -extern void (* popupFunc)(event_t event); -extern uint8_t warningInfoFlags; - -#define DISPLAY_WARNING(evt) (*popupFunc)(evt) -#define POPUP_INPUT(s, func) (warningText = s, popupFunc = func) -#define POPUP_MENU_MAX_LINES 12 -#define MENU_MAX_DISPLAY_LINES 9 -#define MENU_LINE_LENGTH (LEN_MODEL_NAME+12) - -extern const char * popupMenuItems[POPUP_MENU_MAX_LINES]; -extern uint16_t popupMenuItemsCount; -extern uint16_t popupMenuOffset; -enum { - MENU_OFFSET_INTERNAL, - MENU_OFFSET_EXTERNAL -}; -extern uint8_t popupMenuOffsetType; -extern uint8_t popupMenuSelectedItem; -const char * runPopupMenu(event_t event); -extern void (*popupMenuHandler)(const char * result); -void pushMenuTextView(const char * filename); -void pushModelNotes(); -void readModelNotes(); - typedef int (*FnFuncP) (int x); void drawFunction(FnFuncP fn, int x, int y, int width); -void onSourceLongEnterPress(const char *result); - -uint8_t switchToMix(uint8_t source); - #endif // _MENUS_H_ diff --git a/radio/src/gui/colorlcd/message_dialog.cpp b/radio/src/gui/colorlcd/message_dialog.cpp index 3b26161f0..2c1cfe992 100644 --- a/radio/src/gui/colorlcd/message_dialog.cpp +++ b/radio/src/gui/colorlcd/message_dialog.cpp @@ -20,11 +20,20 @@ #include "message_dialog.h" #include "static.h" -MessageDialog::MessageDialog(Window * parent, const char * title, const char * message, const char * info): - Dialog(parent, title, {50, 73, LCD_W - 100, LCD_H - 146}) +MessageDialog::MessageDialog(Window* parent, const char* title, + const char* message, const char* info) : + Dialog(parent, title, {50, 73, LCD_W - 100, LCD_H - 146}) { - messageWidget = new StaticText(this, {0, coord_t(height() - PAGE_LINE_HEIGHT) / 2, width(), PAGE_LINE_HEIGHT}, message, 0, CENTERED); - infoWidget = new StaticText(this, {0, 30 + coord_t(height() - PAGE_LINE_HEIGHT) / 2, width(), PAGE_LINE_HEIGHT}, info, 0, CENTERED); + messageWidget = new StaticText( + this, + {0, coord_t(height() - PAGE_LINE_HEIGHT) / 2, width(), PAGE_LINE_HEIGHT}, + message, 0, CENTERED); + + infoWidget = new StaticText(this, + {0, 30 + coord_t(height() - PAGE_LINE_HEIGHT) / 2, + width(), PAGE_LINE_HEIGHT}, + info, 0, CENTERED); + setCloseWhenClickOutside(true); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/gui/colorlcd/model_custom_scripts.cpp b/radio/src/gui/colorlcd/model_custom_scripts.cpp deleted file mode 100644 index 5385ddb6e..000000000 --- a/radio/src/gui/colorlcd/model_custom_scripts.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 "opentx.h" - -void onModelCustomScriptMenu(const char *result) -{ - ScriptData &sd = g_model.scriptsData[s_currIdx]; - - if (result == STR_UPDATE_LIST) { - if (!sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), nullptr)) { - POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); - } - } - else { - // The user choosed a lua file in the list - copySelection(sd.file, result, sizeof(sd.file)); - memset(sd.inputs, 0, sizeof(sd.inputs)); - storageDirty(EE_MODEL); - LUA_LOAD_MODEL_SCRIPT(s_currIdx); - } -} - -enum menuModelCustomScriptItems { - ITEM_MODEL_CUSTOMSCRIPT_FILE, - ITEM_MODEL_CUSTOMSCRIPT_NAME, - ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL, -}; - -#define SCRIPT_ONE_2ND_COLUMN_POS (120) -#define SCRIPT_ONE_3RD_COLUMN_POS (240) - -bool menuModelCustomScriptOne(event_t event) -{ - ScriptData &sd = g_model.scriptsData[s_currIdx]; - - // drawStringWithIndex(lcdLastRightPos+FW, 0, "LUA", s_currIdx+1, 0); - - SUBMENU(STR_MENUCUSTOMSCRIPTS, ICON_MODEL_LUA_SCRIPTS, 3+scriptInputsOutputs[s_currIdx].inputsCount, { 0, 0, LABEL(inputs), 0/*repeated*/ }); - - int8_t sub = menuVerticalPosition; - - for (int k=0; k0 ? BLINK|INVERS : INVERS) : 0); - - if (i == ITEM_MODEL_CUSTOMSCRIPT_FILE) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SCRIPT); - if (ZEXIST(sd.file)) - lcdDrawSizedText(SCRIPT_ONE_2ND_COLUMN_POS, y, sd.file, sizeof(sd.file), attr); - else - lcdDrawTextAtIndex(SCRIPT_ONE_2ND_COLUMN_POS, y, STR_VCSWFUNC, 0, attr); - if (attr) s_editMode = 0; - if (attr && event==EVT_KEY_FIRST(KEY_ENTER) && !READ_ONLY()) { - killEvents(KEY_ENTER); - if (sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), sd.file, LIST_NONE_SD_FILE)) { - POPUP_MENU_START(onModelCustomScriptMenu); - } - else { - POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); - } - } - } - else if (i == ITEM_MODEL_CUSTOMSCRIPT_NAME) { - lcdDrawText(MENUS_MARGIN_LEFT, y, TR_NAME); - editName(SCRIPT_ONE_2ND_COLUMN_POS, y, sd.name, sizeof(sd.name), event, attr); - } - else if (i == ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_INPUTS); - } - else if (i <= ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL+scriptInputsOutputs[s_currIdx].inputsCount) { - int inputIdx = i-ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL-1; - lcdDrawSizedText(INDENT_WIDTH, y, scriptInputsOutputs[s_currIdx].inputs[inputIdx].name, 10, 0); - if (scriptInputsOutputs[s_currIdx].inputs[inputIdx].type == INPUT_TYPE_VALUE) { - lcdDrawNumber(SCRIPT_ONE_2ND_COLUMN_POS, y, g_model.scriptsData[s_currIdx].inputs[inputIdx].value+ \ - scriptInputsOutputs[s_currIdx].inputs[inputIdx].def, attr|LEFT); - if (attr) { - CHECK_INCDEC_MODELVAR(event, g_model.scriptsData[s_currIdx].inputs[inputIdx].value, \ - scriptInputsOutputs[s_currIdx].inputs[inputIdx].min-scriptInputsOutputs[s_currIdx].inputs[inputIdx].def, \ - scriptInputsOutputs[s_currIdx].inputs[inputIdx].max-scriptInputsOutputs[s_currIdx].inputs[inputIdx].def); - } - } - else { - drawSource(SCRIPT_ONE_2ND_COLUMN_POS, y, g_model.scriptsData[s_currIdx].inputs[inputIdx].source, attr); - if (attr) { - CHECK_INCDEC_MODELSOURCE(event, g_model.scriptsData[s_currIdx].inputs[inputIdx].source, 0, MIXSRC_LAST_TELEM); - } - } - } - } - - if (scriptInputsOutputs[s_currIdx].outputsCount > 0) { - lcdDrawSolidVerticalLine(SCRIPT_ONE_3RD_COLUMN_POS-4, DEFAULT_SCROLLBAR_Y, DEFAULT_SCROLLBAR_H+5, DEFAULT_COLOR); - // lcdDrawText(SCRIPT_ONE_3RD_COLUMN_POS, FH+1, STR_OUTPUTS); - - for (int i=0; i= 0) { - s_currIdx = sub; - pushMenu(menuModelCustomScriptOne); - } - - for (int i=0, scriptIndex=0; idrawBitmap(x+1, y+1, model->getBuffer()); - if (current) { - lcd->drawBitmapPattern(x+66, y+43, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); - } - if (selected) { - lcdDrawSolidRect(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2, 1, TITLE_BGCOLOR); - drawShadow(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2); - if (selectMode == MODE_MOVE_MODEL && modelselModelMoveBackground) { - lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->width(), y, modelselModelMoveBackground, TITLE_BGCOLOR); - lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->width()+12, y+5, modelselModelMoveIcon, DEFAULT_BGCOLOR); - } - } -} - -uint16_t categoriesVerticalOffset = 0; -uint16_t categoriesVerticalPosition = 0; -#define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition) - -void setCurrentModel(unsigned int index) -{ - std::list::iterator it = currentCategory->begin(); - std::advance(it, index); - currentModel = *it; - menuVerticalPosition = index / 2; - menuHorizontalPosition = index & 1; - menuVerticalOffset = limit(menuVerticalPosition-2, menuVerticalOffset, min(menuVerticalPosition, max(0, (currentCategory->size()-7)/2))); -} - -void setCurrentCategory(unsigned int index) -{ - currentCategoryIndex = index; - const std::list& cats = modelslist.getCategories(); - std::list::const_iterator it = cats.begin(); - std::advance(it, index); - currentCategory = *it; - categoriesVerticalPosition = index; - categoriesVerticalOffset = limit(categoriesVerticalPosition-4, categoriesVerticalOffset, min(categoriesVerticalPosition, max(0, cats.size()-5))); - if (currentCategory->size() > 0) - setCurrentModel(0); - else - currentModel = nullptr; -} - -#if defined(LUA) -#define MAX_WIZARD_NAME_LEN (sizeof(WIZARD_PATH)+20) -#define WIZARD_SPACING 40 -#define WIZARD_LEFT_SPACING 30 -#define WIZARD_ICON_X 80 -#define WIZARD_ICON_Y 110 -#define WIZARD_TEXT_Y 195 - -uint8_t getWizardCount() -{ - uint8_t wizCnt=0; - DIR dir; - static FILINFO fno; - - FRESULT res = f_opendir(&dir, WIZARD_PATH); - if (res == FR_OK) { - for (;;) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) { - break; - } - if (fno.fattrib & AM_DIR) { - wizCnt++; - } - } - } - f_closedir(&dir); - return wizCnt; -} - -bool menuModelWizard(event_t event) -{ - static uint8_t wizardSelected = 0; - static uint8_t wizardCnt = getWizardCount(); - bool executeMe = false; - uint8_t first = 0; - DIR dir; - static FILINFO fno; - char wizpath[MAX_WIZARD_NAME_LEN]; - - if (wizardCnt == 0) { - chainMenu(menuModelSelect); - return false; - } - - switch(event) { - case 0: - // no need to refresh the screen - return false; - - case EVT_KEY_FIRST(KEY_EXIT): - chainMenu(menuModelSelect); - return false; - - case EVT_KEY_BREAK(KEY_ENTER): - executeMe = true; - break; - - case EVT_ROTARY_RIGHT: - if (wizardSelected < wizardCnt-1) { - wizardSelected++; - } - if (wizardSelected > 3) { - first = wizardSelected - 3; - } - break; - - case EVT_ROTARY_LEFT: - if (wizardSelected != 0) { - wizardSelected--; - } - if(wizardSelected < first) - { - first = wizardSelected; - } - break; - } - strcpy(wizpath, WIZARD_PATH); - strcpy(&wizpath[sizeof(WIZARD_PATH)-1], "/"); - lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); - lcd->drawBitmap(0, 0, modelselWizardBackground); - FRESULT res = f_opendir(&dir, WIZARD_PATH); - if (res == FR_OK) { - for (uint8_t wizidx=0;;wizidx++) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) { - break; - } - if (fno.fattrib & AM_DIR) { - if((wizidx >= first) && (wizidx < (first+4))) { - uint16_t x = WIZARD_LEFT_SPACING + (wizidx - first) * (WIZARD_SPACING + WIZARD_ICON_X); - strcpy(&wizpath[sizeof(WIZARD_PATH)], fno.fname); - strcpy(&wizpath[sizeof(WIZARD_PATH) + strlen(fno.fname)], "/icon.png"); - BitmapBuffer * background = BitmapBuffer::load(wizpath); - lcd->drawBitmap(x, WIZARD_ICON_Y, background); - if(wizidx == wizardSelected ) { - if (wizardSelected < 5) { - lcdDrawRect(x, WIZARD_ICON_Y, 85, 130, 2, SOLID, MAINVIEW_GRAPHICS_COLOR_INDEX); - lcdDrawRect(x+5, WIZARD_TEXT_Y, 75, 4, 2, SOLID, MAINVIEW_GRAPHICS_COLOR_INDEX); - } - if (executeMe) { - strcpy(&wizpath[sizeof(WIZARD_PATH)+strlen(fno.fname)], "/wizard.lua"); - if (isFileAvailable(wizpath)) { - wizpath[sizeof(WIZARD_PATH) + strlen(fno.fname)] = 0; - f_chdir(wizpath); - luaExec(WIZARD_NAME); - } - } - } - delete background; - } - } - } - f_closedir(&dir); - } - return true; -} -#endif - -void onDeleteModelConfirm(const char * result) -{ - if (result == STR_OK) { - if (deleteMode == MODE_DELETE_CATEGORY) { - modelslist.removeCategory(currentCategory); - modelslist.save(); - setCurrentCategory(currentCategoryIndex > 0 ? currentCategoryIndex-1 : currentCategoryIndex); - } - else if (deleteMode == MODE_DELETE_MODEL){ - int modelIndex = MODEL_INDEX(); - modelslist.removeModel(currentCategory, currentModel); - s_copyMode = 0; - pushEvent(EVT_REFRESH); - if (modelIndex > 0) { - modelIndex--; - } - setCurrentModel(modelIndex); - } - } -} - -void onModelSelectMenu(const char * result) -{ - if (result == STR_SELECT_MODEL) { - if (!g_eeGeneral.disableRssiPoweroffAlarm) { - if (!confirmModelChange()) - return; - } - - // we store the latest changes if any - storageFlushCurrentModel(); - storageCheck(true); - memcpy(g_eeGeneral.currModelFilename, currentModel->modelFilename, LEN_MODEL_FILENAME); - modelslist.setCurrentModel(currentModel); - modelslist.setCurrentCategory(currentCategory); - loadModel(g_eeGeneral.currModelFilename, true); - storageDirty(EE_GENERAL); - storageCheck(true); - chainMenu(menuMainView); - } - else if (result == STR_DELETE_MODEL) { - POPUP_CONFIRMATION(STR_DELETEMODEL, onDeleteModelConfirm); - SET_WARNING_INFO(currentModel->modelName, LEN_MODEL_NAME, 0); - deleteMode = MODE_DELETE_MODEL; - } - else if (result == STR_CREATE_MODEL) { - if (!g_eeGeneral.disableRssiPoweroffAlarm) { - if (!confirmModelChange()) - return; - } - storageCheck(true); - modelslist.addModel(currentCategory, createModel()); - selectMode = MODE_SELECT_MODEL; - setCurrentModel(currentCategory->size() - 1); - modelslist.setCurrentModel(currentModel); - modelslist.setCurrentCategory(currentCategory); - modelslist.onNewModelCreated(currentModel, &g_model); -#if defined(LUA) - chainMenu(menuModelWizard); -#endif - } - else if (result == STR_DUPLICATE_MODEL) { - char duplicatedFilename[LEN_MODEL_FILENAME+1]; - memcpy(duplicatedFilename, currentModel->modelFilename, sizeof(duplicatedFilename)); - if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, MODELS_PATH)) { - sdCopyFile(currentModel->modelFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH); - ModelCell * dup_model = modelslist.addModel(currentCategory, duplicatedFilename); - dup_model->fetchRfData(); - setCurrentModel(currentCategory->size() - 1); - } - else { - POPUP_WARNING(STR_INVALID_FILE); - } - } - else if (result == STR_MOVE_MODEL) { - selectMode = MODE_MOVE_MODEL; - } - else if (result == STR_CREATE_CATEGORY) { - currentCategory = modelslist.createCategory(); - setCurrentCategory(modelslist.getCategories().size() - 1); - } - else if (result == STR_RENAME_CATEGORY) { - selectMode = MODE_RENAME_CATEGORY; - s_editMode = EDIT_MODIFY_STRING; - editNameCursorPos = 0; - } - else if (result == STR_DELETE_CATEGORY) { - if (currentCategory->size() > 0){ - POPUP_WARNING(STR_DELETE_ERROR); - SET_WARNING_INFO(STR_CAT_NOT_EMPTY, sizeof(TR_CAT_NOT_EMPTY), 0); - } - else { - POPUP_CONFIRMATION(STR_DELETEMODEL, onDeleteModelConfirm); - SET_WARNING_INFO(currentCategory->name, LEN_MODEL_FILENAME, 0); - deleteMode = MODE_DELETE_CATEGORY; - } - } -} - -void initModelsList() -{ - modelslist.load(); - - categoriesVerticalOffset = 0; - bool found = false; - int index = 0; - const std::list& cats = modelslist.getCategories(); - for (std::list::const_iterator it = cats.begin(); it != cats.end(); ++it, ++index) { - if (*it == modelslist.getCurrentCategory()) { - setCurrentCategory(index); - found = true; - break; - } - } - if (!found) { - setCurrentCategory(0); - } - - menuVerticalOffset = 0; - found = false; - index = 0; - for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { - if (*it == modelslist.getCurrentModel()) { - setCurrentModel(index); - found = true; - break; - } - } - if (!found) { - setCurrentModel(0); - } -} - -bool menuModelSelect(event_t event) -{ - const std::list& cats = modelslist.getCategories(); - switch (event) { - case 0: - // no need to refresh the screen - return false; - - case EVT_ENTRY: - selectMode = MODE_SELECT_MODEL; - initModelsList(); - break; - - case EVT_KEY_BREAK(KEY_ENTER): - if (selectMode == MODE_MOVE_MODEL) - selectMode = MODE_SELECT_MODEL; - break; - - case EVT_KEY_FIRST(KEY_EXIT): - switch (selectMode) { - case MODE_MOVE_MODEL: - selectMode = MODE_SELECT_MODEL; - break; - case MODE_SELECT_MODEL: - chainMenu(menuMainView); - return false; - } - break; - - case EVT_KEY_FIRST(KEY_PGUP): -#if defined(PCBX10) - case EVT_KEY_LONG(KEY_PGDN): -#endif - if (selectMode == MODE_SELECT_MODEL) { - if (categoriesVerticalPosition == 0) - categoriesVerticalPosition = cats.size() - 1; - else - categoriesVerticalPosition -= 1; - setCurrentCategory(categoriesVerticalPosition); - } - else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition > 0) { - ModelsCategory * previous_category = currentCategory; - ModelCell * model = currentModel; - categoriesVerticalPosition -= 1; - setCurrentCategory(categoriesVerticalPosition); - modelslist.moveModel(model, previous_category, currentCategory); - setCurrentModel(currentCategory->size() - 1); - } - killEvents(event); - break; - -#if defined(PCBX12S) - case EVT_KEY_FIRST(KEY_PGDN): -#elif defined(PCBX10) - case EVT_KEY_BREAK(KEY_PGDN): -#endif - if (selectMode == MODE_SELECT_MODEL) { - categoriesVerticalPosition += 1; - if (categoriesVerticalPosition >= cats.size()) - categoriesVerticalPosition = 0; - setCurrentCategory(categoriesVerticalPosition); - } - else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition < cats.size()-1) { - ModelsCategory * previous_category = currentCategory; - ModelCell * model = currentModel; - categoriesVerticalPosition += 1; - setCurrentCategory(categoriesVerticalPosition); - modelslist.moveModel(model, previous_category, currentCategory); - setCurrentModel(currentCategory->size()-1); - } - break; - - case EVT_KEY_LONG(KEY_ENTER): - if (selectMode == MODE_SELECT_MODEL) { - killEvents(event); - if (currentModel && currentModel != modelslist.getCurrentModel()) { - POPUP_MENU_ADD_ITEM(STR_SELECT_MODEL); - } - POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL); - if (currentModel) { - POPUP_MENU_ADD_ITEM(STR_DUPLICATE_MODEL); - POPUP_MENU_ADD_ITEM(STR_MOVE_MODEL); - } - // POPUP_MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); - if (currentModel && currentModel != modelslist.getCurrentModel()) { - POPUP_MENU_ADD_ITEM(STR_DELETE_MODEL); - } - // POPUP_MENU_ADD_ITEM(STR_RESTORE_MODEL); - POPUP_MENU_ADD_ITEM(STR_CREATE_CATEGORY); - POPUP_MENU_ADD_ITEM(STR_RENAME_CATEGORY); - if (cats.size() > 1) { - POPUP_MENU_ADD_ITEM(STR_DELETE_CATEGORY); - } - POPUP_MENU_START(onModelSelectMenu); - } - break; - - } - - lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); - lcd->drawBitmap(0, 0, modelselIconBitmap); - - // Categories - int index = 0; - coord_t y = 97; - drawVerticalScrollbar(CATEGORIES_WIDTH-1, y-1, 5*(FH+7)-5, categoriesVerticalOffset, cats.size(), 5); - for (std::list::const_iterator it = cats.begin(); it != cats.end(); ++it, ++index) { - if (index >= categoriesVerticalOffset && index < categoriesVerticalOffset+5) { - if (index != categoriesVerticalOffset) { - lcdDrawSolidHorizontalLine(1, y-4, CATEGORIES_WIDTH-10, LINE_COLOR); - } - if (selectMode == MODE_RENAME_CATEGORY && currentCategory == *it) { - lcdDrawSolidFilledRect(0, y-INVERT_VERT_MARGIN+1, CATEGORIES_WIDTH-2, INVERT_LINE_HEIGHT, DEFAULT_BGCOLOR); - editName(MENUS_MARGIN_LEFT, y, currentCategory->name, LEN_MODEL_FILENAME, event, 1, 0); - if (s_editMode == 0 || event == EVT_KEY_BREAK(KEY_EXIT)) { - modelslist.save(); - selectMode = MODE_SELECT_MODEL; - pushEvent(EVT_REFRESH); - } - } - else { - drawCategory(y, (*it)->name, currentCategory==*it); - } - y += FH+7; - } - } - - // Models - index = 0; - y = 5; - for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) { - if (index >= menuVerticalOffset*2 && index < (menuVerticalOffset+4)*2) { - bool selected = ((selectMode==MODE_SELECT_MODEL || selectMode==MODE_MOVE_MODEL) && index==menuVerticalPosition*2+menuHorizontalPosition); - bool current = !strncmp((*it)->modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME); - if (index & 1) { - drawModel(MODELS_LEFT + MODELS_COLUMN_WIDTH, y, *it, current, selected); - y += 66; - } - else { - drawModel(MODELS_LEFT, y, *it, current, selected); - } - if (selected) { - lcd->drawBitmap(5, LCD_H-FH, modelselModelNameBitmap); - lcdDrawText(22, LCD_H-FH-1, (*it)->modelFilename, FONT(XS)|DEFAULT_COLOR); - } - } - } - - // Navigation - if (currentModel) { - if (selectMode == MODE_SELECT_MODEL) { - if (navigate(event, index, 4, 2) != 0) { - setCurrentModel(MODEL_INDEX()); - pushEvent(EVT_REFRESH); - } - } - else if (selectMode == MODE_MOVE_MODEL) { - int8_t direction = navigate(event, index, 4, 2, false); - if (direction) { - modelslist.moveModel(currentCategory, currentModel, direction); - setCurrentModel(MODEL_INDEX()); - pushEvent(EVT_REFRESH); - } - } - } - drawVerticalScrollbar(DEFAULT_SCROLLBAR_X + 4, 7, LCD_H - 15, menuVerticalOffset, (index + 1) / 2, 4); - - // Footer - lcd->drawBitmap(5, LCD_H-FH-20, modelselSdFreeBitmap); - uint32_t size = sdGetSize() / 100; - lcdDrawNumber(22, LCD_H-FH-21, size, PREC1|FONT(XS), 0, nullptr, "GB"); - lcd->drawBitmap(70, LCD_H-FH-20, modelselModelQtyBitmap); - lcdDrawNumber(92, LCD_H-FH-21, modelslist.getModelsCount(),FONT(XS)); - - return true; -} diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index 45e34a752..ca24c6c99 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -387,8 +387,7 @@ void ModelTelemetryPage::editSensor(FormWindow * window, uint8_t index) void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) { FormGridLayout grid; - grid.spacer(8); - grid.setLabelWidth(170); + grid.spacer(PAGE_PADDING); this->window = window; @@ -470,7 +469,7 @@ void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) } // Autodiscover button - auto discover = new TextButton(window, grid.getFieldSlot(2, 0), STR_DISCOVER_SENSORS); + auto discover = new TextButton(window, grid.getFieldSlot(2, 0), (allowNewSensors) ? STR_STOP_DISCOVER_SENSORS : STR_DISCOVER_SENSORS); discover->setPressHandler([=]() { allowNewSensors = !allowNewSensors; if (allowNewSensors) { @@ -509,14 +508,15 @@ void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) grid.nextLine(); } + // restore aligned layout + grid.setLabelWidth(PAGE_LABEL_WIDTH); + // Ignore instance button - grid.setLabelWidth(170); new StaticText(window, grid.getLabelSlot(true), STR_IGNORE_INSTANCE); new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.ignoreSensorIds)); grid.nextLine(); // Vario - grid.setLabelWidth(100); new Subtitle(window, grid.getLineSlot(), STR_VARIO); grid.nextLine(); new StaticText(window, grid.getLabelSlot(true), STR_SOURCE); diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp index 368c0c195..951cba9e7 100644 --- a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp @@ -128,7 +128,6 @@ void RadioGhostModuleConfig::onEvent(event_t event) break; case EVT_KEY_LONG(KEY_EXIT): - menuVerticalOffset = 0; memclear(&reusableBuffer.ghostMenu, sizeof(reusableBuffer.ghostMenu)); reusableBuffer.ghostMenu.buttonAction = GHST_BTN_NONE; reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_CLOSE; diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index f2932f9a3..d050eba19 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -152,11 +152,11 @@ void RadioHardwarePage::build(FormWindow * window) { FormGridLayout grid; #if LCD_W > LCD_H - grid.setLabelWidth(130); + grid.setLabelWidth(180); #else - grid.setLabelWidth(100); + grid.setLabelWidth(130); #endif - grid.spacer(6); + grid.spacer(PAGE_PADDING); // Calibration new StaticText(window, grid.getLabelSlot(), STR_INPUTS, 0, FONT(BOLD)); @@ -233,15 +233,10 @@ void RadioHardwarePage::build(FormWindow * window) }); grid.nextLine(); } -#if LCD_W > LCD_H - grid.setLabelWidth(180); -#else - grid.setLabelWidth(130); -#endif // Bat calibration new StaticText(window, grid.getLabelSlot(), STR_BATT_CALIB); - auto batCal = new NumberEdit(window, grid.getFieldSlot(), -127, 127, GET_SET_DEFAULT(g_eeGeneral.txVoltageCalibration)); + auto batCal = new NumberEdit(window, grid.getFieldSlot(1,0), -127, 127, GET_SET_DEFAULT(g_eeGeneral.txVoltageCalibration)); batCal->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getBatteryVoltage(), flags | PREC2, 0, nullptr, "V"); }); @@ -250,14 +245,14 @@ void RadioHardwarePage::build(FormWindow * window) // RTC Batt display new StaticText(window, grid.getLabelSlot(), STR_RTC_BATT); - new DynamicNumber(window, grid.getFieldSlot(), [] { + new DynamicNumber(window, grid.getFieldSlot(1,0), [] { return getRTCBatteryVoltage(); }, PREC2, nullptr, "V"); grid.nextLine(); // RTC Batt check enable new StaticText(window, grid.getLabelSlot(), STR_RTC_CHECK); - new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.disableRtcWarning )); + new CheckBox(window, grid.getFieldSlot(1,0), GET_SET_INVERTED(g_eeGeneral.disableRtcWarning )); grid.nextLine(); #if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000 @@ -301,7 +296,7 @@ void RadioHardwarePage::build(FormWindow * window) // ADC filter new StaticText(window, grid.getLabelSlot(), STR_JITTER_FILTER); - new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.jitterFilter)); + new CheckBox(window, grid.getFieldSlot(1,0), GET_SET_INVERTED(g_eeGeneral.jitterFilter)); grid.nextLine(); // Debugs diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 063805cc7..f2870a528 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -376,6 +376,7 @@ void RadioSetupPage::build(FormWindow * window) grid.nextLine(); #endif +#if defined(INTERNAL_GPS) // GPS { new Subtitle(window, grid.getLabelSlot(), STR_GPS); @@ -383,7 +384,7 @@ void RadioSetupPage::build(FormWindow * window) // Timezone new StaticText(window, grid.getLabelSlot(true), STR_TIMEZONE); - new NumberEdit(window, grid.getFieldSlot(2, 0), -12, 12, GET_SET_DEFAULT(g_eeGeneral.timezone)); + new NumberEdit(window, grid.getFieldSlot(), -12, 12, GET_SET_DEFAULT(g_eeGeneral.timezone)); grid.nextLine(); // Adjust RTC (from telemetry) @@ -396,6 +397,7 @@ void RadioSetupPage::build(FormWindow * window) new Choice(window, grid.getFieldSlot(), STR_GPSFORMAT, 0, 1, GET_SET_DEFAULT(g_eeGeneral.gpsFormat)); grid.nextLine(); } +#endif #if defined(PXX2) // Owner ID diff --git a/radio/src/gui/colorlcd/screen_setup.cpp b/radio/src/gui/colorlcd/screen_setup.cpp index 856eaffa0..693e3628e 100644 --- a/radio/src/gui/colorlcd/screen_setup.cpp +++ b/radio/src/gui/colorlcd/screen_setup.cpp @@ -206,11 +206,19 @@ class SetupWidgetsPage: public FormWindow } #if defined(HARDWARE_TOUCH) - bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, + coord_t slideX, coord_t slideY) override { // prevent screen sliding when setting up widgets return true; } + + bool onTouchEnd(coord_t x, coord_t y) override + { + if (FormWindow::onTouchEnd(x, y)) return true; + + return true; + } #endif #if defined(HARDWARE_KEYS) diff --git a/radio/src/gui/colorlcd/select_fab_carousel.cpp b/radio/src/gui/colorlcd/select_fab_carousel.cpp index 215efe8d7..e188caab7 100644 --- a/radio/src/gui/colorlcd/select_fab_carousel.cpp +++ b/radio/src/gui/colorlcd/select_fab_carousel.cpp @@ -21,7 +21,7 @@ #include "select_fab_carousel.h" SelectFabCarousel::SelectFabCarousel(Window* parent): - FormGroup(parent, {}, OPAQUE | FORM_FORWARD_FOCUS | NO_SCROLLBAR) + FormGroup(parent, {}, FORM_FORWARD_FOCUS | NO_SCROLLBAR) { setPageWidth(FAB_BUTTON_SIZE + SELECT_BUTTON_BORDER); setHeight(FAB_BUTTON_SIZE + 2 * PAGE_LINE_HEIGHT + SELECT_BUTTON_BORDER / 2); @@ -36,14 +36,10 @@ void SelectFabCarousel::setMaxButtons(uint8_t max) void SelectFabCarousel::addButton(uint8_t icon, const char* title, std::function pressHandler) { - coord_t y_pos = 0;//SELECT_BUTTON_BORDER; - coord_t x_pos = pageWidth * buttons;// + SELECT_BUTTON_BORDER / 2; + coord_t y_pos = 0; + coord_t x_pos = pageWidth * buttons; buttons++; - auto button = new SelectFabButton(this, x_pos, y_pos, icon, title, pressHandler); + new SelectFabButton(this, x_pos, y_pos, icon, title, pressHandler); setInnerWidth(pageWidth * buttons); - - // y_pos += button->width() + SELECT_BUTTON_BORDER; - // setHeight(y_pos); - // setInnerHeight(y_pos); } diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index 240ff1ba5..3484d1187 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -169,12 +169,17 @@ void TabsGroup::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); +#if defined(KEYS_GPIO_REG_PGUP) + if (event == EVT_KEY_FIRST(KEY_PGDN)) { +#else if (event == EVT_KEY_BREAK(KEY_PGDN)) { +#endif + killEvents(event); uint8_t current = header.carousel.getCurrentIndex() + 1; setCurrentTab(current >= tabs.size() ? 0 : current); } -#if defined(KEYS_GPIO_REG_UP) - else if (event == EVT_KEY_BREAK(KEY_PGUP)) { +#if defined(KEYS_GPIO_REG_PGUP) + else if (event == EVT_KEY_FIRST(KEY_PGUP)) { #else else if (event == EVT_KEY_LONG(KEY_PGDN)) { #endif @@ -182,7 +187,7 @@ void TabsGroup::onEvent(event_t event) uint8_t current = header.carousel.getCurrentIndex(); setCurrentTab(current == 0 ? tabs.size() - 1 : current - 1); } - else if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + else if (event == EVT_KEY_FIRST(KEY_EXIT)) { killEvents(event); ViewMain::instance()->setFocus(SET_FOCUS_DEFAULT); deleteLater(); diff --git a/radio/src/gui/colorlcd/topbar.cpp b/radio/src/gui/colorlcd/topbar.cpp index 172f4f9cf..fe18dd59f 100644 --- a/radio/src/gui/colorlcd/topbar.cpp +++ b/radio/src/gui/colorlcd/topbar.cpp @@ -83,3 +83,20 @@ void SetupTopBarWidgetsPage::onEvent(event_t event) } #endif +#if defined(HARDWARE_TOUCH) +bool SetupTopBarWidgetsPage::onTouchSlide(coord_t x, coord_t y, + coord_t startX, coord_t startY, + coord_t slideX, coord_t slideY) +{ + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; +} + +bool SetupTopBarWidgetsPage::onTouchEnd(coord_t x, coord_t y) +{ + if (FormWindow::onTouchEnd(x, y)) + return true; + + return true; +} +#endif diff --git a/radio/src/gui/colorlcd/topbar.h b/radio/src/gui/colorlcd/topbar.h index d7b519b43..b20d2520f 100644 --- a/radio/src/gui/colorlcd/topbar.h +++ b/radio/src/gui/colorlcd/topbar.h @@ -59,6 +59,12 @@ class SetupTopBarWidgetsPage: public FormWindow void onEvent(event_t event) override; #endif +#if defined(HARDWARE_TOUCH) + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, + coord_t slideX, coord_t slideY) override; + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + protected: ScreenMenu* menu; unsigned savedView = 0; diff --git a/radio/src/gui/colorlcd/view_main.cpp b/radio/src/gui/colorlcd/view_main.cpp index 6706775c7..17fd8f729 100644 --- a/radio/src/gui/colorlcd/view_main.cpp +++ b/radio/src/gui/colorlcd/view_main.cpp @@ -257,35 +257,39 @@ bool ViewMain::onTouchEnd(coord_t x, coord_t y) void ViewMain::onEvent(event_t event) { switch (event) { - case EVT_KEY_BREAK(KEY_MODEL): + case EVT_KEY_FIRST(KEY_MODEL): killEvents(event); new ModelMenu(); break; - case EVT_KEY_BREAK(KEY_RADIO): + case EVT_KEY_FIRST(KEY_RADIO): killEvents(event); new RadioMenu(); break; - case EVT_KEY_LONG(KEY_TELEM): + case EVT_KEY_FIRST(KEY_TELEM): killEvents(event); new ScreenMenu(); break; - case EVT_KEY_BREAK(KEY_ENTER): + case EVT_KEY_FIRST(KEY_ENTER): killEvents(event); openMenu(); break; +#if defined(KEYS_GPIO_REG_PGUP) + case EVT_KEY_FIRST(KEY_PGDN): +#else case EVT_KEY_BREAK(KEY_PGDN): +#endif killEvents(event); nextMainView(); break; //TODO: these need to go away! // -> board code should map the keys as required -#if defined(KEYS_GPIO_REG_UP) - case EVT_KEY_BREAK(KEY_PGUP): +#if defined(KEYS_GPIO_REG_PGUP) + case EVT_KEY_FIRST(KEY_PGUP): #else case EVT_KEY_LONG(KEY_PGDN): #endif diff --git a/radio/src/gui/colorlcd/view_main_decoration.cpp b/radio/src/gui/colorlcd/view_main_decoration.cpp index 19684d928..16bc8cdfd 100644 --- a/radio/src/gui/colorlcd/view_main_decoration.cpp +++ b/radio/src/gui/colorlcd/view_main_decoration.cpp @@ -300,8 +300,8 @@ void ViewMainDecoration::createTrims() VERTICAL_SLIDERS_HEIGHT }; - trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 2); - trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 1); + trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 1); + trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 2); } void ViewMainDecoration::createFlightMode() diff --git a/radio/src/gui/colorlcd/view_main_menu.h b/radio/src/gui/colorlcd/view_main_menu.h index b9045dc9c..5fc8c1c20 100644 --- a/radio/src/gui/colorlcd/view_main_menu.h +++ b/radio/src/gui/colorlcd/view_main_menu.h @@ -55,7 +55,6 @@ public: parent->deleteLater(); return; } - Window::onEvent(event); } #endif diff --git a/radio/src/gui/colorlcd/view_text.cpp b/radio/src/gui/colorlcd/view_text.cpp deleted file mode 100644 index e09f62699..000000000 --- a/radio/src/gui/colorlcd/view_text.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 "opentx.h" - -bool menuTextView(event_t event) -{ - static int lines_count; - - drawMenuTemplate(STR_TEXT_VIEWER, ICON_OPENTX, nullptr, OPTION_MENU_NO_SCROLLBAR); - - switch (event) { - case EVT_ENTRY: - menuVerticalOffset = 0; - lines_count = 0; - sdReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, lines_count); - break; - - case EVT_ROTARY_LEFT: - if (menuVerticalOffset == 0) - break; - else - menuVerticalOffset--; - sdReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, lines_count); - break; - - case EVT_ROTARY_RIGHT: - if (menuVerticalOffset + TEXT_VIEWER_LINES >= lines_count) - break; - else - ++menuVerticalOffset; - sdReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, lines_count); - break; - - case EVT_KEY_FIRST(KEY_EXIT): - menuVerticalOffset = 0; - popMenu(); - break; - } - - for (int i = 0; i < TEXT_VIEWER_LINES; i++) { - lcd->drawTextMaxWidth(MENUS_MARGIN_LEFT, MENU_HEADER_HEIGHT + i * FH, reusableBuffer.viewText.lines[i], 0, LCD_W - 2 * MENUS_MARGIN_LEFT); - } - - drawVerticalScrollbar(LCD_W-5, 50, 195, menuVerticalOffset, lines_count, TEXT_VIEWER_LINES); - - return true; -} - -void pushMenuTextView(const char *filename) -{ - if (strlen(filename) < TEXT_FILENAME_MAXLEN) { - strcpy(reusableBuffer.viewText.filename, filename); - pushMenu(menuTextView); - } -} diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 9e2f26935..9b005dbb8 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -191,6 +191,34 @@ inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx) } } +#if defined(PXX2) +inline bool isRacingModeAllowed() +{ + return isModulePXX2(INTERNAL_MODULE) && g_model.moduleData[INTERNAL_MODULE].getChannelsCount() == 8; +} + +inline bool isRacingModeEnabled() +{ + return isRacingModeAllowed() && g_model.moduleData[INTERNAL_MODULE].pxx2.racingMode; +} + +inline uint8_t IF_ALLOW_RACING_MODE(int moduleIdx) +{ + if (!IS_MODULE_ENABLED(moduleIdx)) { + return HIDDEN_ROW; + } + else if (isRacingModeAllowed()) { + return 0; + } + return HIDDEN_ROW; +} +#else +inline uint8_t IF_ALLOW_RACING_MODE(int) +{ + return HIDDEN_ROW; +} +#endif + #if defined(MULTIMODULE) inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx) { diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index 0d8fda028..d81c94ace 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -124,7 +124,7 @@ class MultiExternalUpdateDriver: public MultiFirmwareUpdateDriver if (inverted) telemetryPortInvertedInit(57600); else - telemetryPortInit(57600, TELEMETRY_SERIAL_DEFAULT); + telemetryPortInit(57600, TELEMETRY_SERIAL_WITHOUT_DMA); } bool getByte(uint8_t & byte) const override @@ -170,7 +170,7 @@ class MultiExtSportUpdateDriver: public MultiFirmwareUpdateDriver void init(bool inverted) const override { - telemetryPortInit(57600, TELEMETRY_SERIAL_DEFAULT); + telemetryPortInit(57600, TELEMETRY_SERIAL_WITHOUT_DMA); } bool getByte(uint8_t & byte) const override diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 7167fc7e8..e1981d297 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1227,6 +1227,7 @@ Run function (key pressed) @status current Introduced in 2.2.0 */ +#if 0 static int luaPopupWarning(lua_State * L) { event_t event = luaL_checkinteger(L, 2); @@ -1242,6 +1243,7 @@ static int luaPopupWarning(lua_State * L) } return 1; } +#endif /*luadoc @function popupConfirmation(title, event) deprecated, please replace by @@ -1262,6 +1264,7 @@ Run function (key pressed) @status current Introduced in 2.2.0, changed to (title, message, event) in 2.3.8 */ +#if 0 static int luaPopupConfirmation(lua_State * L) { warningType = WARNING_TYPE_CONFIRM; @@ -1288,6 +1291,7 @@ static int luaPopupConfirmation(lua_State * L) } return 1; } +#endif /*luadoc @function defaultStick(channel) @@ -1537,6 +1541,19 @@ static int luaGetUsage(lua_State * L) return 1; } +/*luadoc +@function getAvailableMemory() + +Get available memory remaining in the Heap for Lua. + +@retval usage (number) a value returned in b +*/ +static int luaGetAvailableMemory(lua_State * L) +{ + lua_pushunsigned(L, availableMemory()); + return 1; +} + /*luadoc @function resetGlobalTimer([type]) @@ -1755,8 +1772,8 @@ const luaL_Reg opentxLib[] = { { "playTone", luaPlayTone }, { "playHaptic", luaPlayHaptic }, // { "popupInput", luaPopupInput }, - { "popupWarning", luaPopupWarning }, - { "popupConfirmation", luaPopupConfirmation }, + // { "popupWarning", luaPopupWarning }, + // { "popupConfirmation", luaPopupConfirmation }, { "defaultStick", luaDefaultStick }, { "defaultChannel", luaDefaultChannel }, { "getRSSI", luaGetRSSI }, @@ -1764,6 +1781,7 @@ const luaL_Reg opentxLib[] = { { "chdir", luaChdir }, { "loadScript", luaLoadScript }, { "getUsage", luaGetUsage }, + { "getAvailableMemory", luaGetAvailableMemory }, { "resetGlobalTimer", luaResetGlobalTimer }, #if LCD_DEPTH > 1 && !defined(COLORLCD) { "GREY", luaGrey }, diff --git a/radio/src/lua/api_model.cpp b/radio/src/lua/api_model.cpp index 76800d842..5e8a4bb74 100644 --- a/radio/src/lua/api_model.cpp +++ b/radio/src/lua/api_model.cpp @@ -121,11 +121,11 @@ Get RF module parameters * `channelsCount` (number) number of channels sent to module * `Type` (number) module type * if the module type is Multi additional information are available - * `protocol` (number) protocol number - * `subProtocol` (number) sub-protocol number - * `channelsOrder` (number) first 4 channels expected order + * `protocol` (number) protocol number (Multi only) + * `subProtocol` (number) sub-protocol number (Multi only) + * `channelsOrder` (number) first 4 channels expected order (Multi only) -@status current Introduced in TODO +@status current Introduced in 2.2.0 */ static int luaModelGetModule(lua_State *L) { @@ -136,7 +136,7 @@ static int luaModelGetModule(lua_State *L) lua_pushtableinteger(L, "subType", module.subType); lua_pushtableinteger(L, "modelId", g_model.header.modelId[idx]); lua_pushtableinteger(L, "firstChannel", module.channelsStart); - lua_pushtableinteger(L, "channelsCount", module.channelsCount + 8); + lua_pushtableinteger(L, "channelsCount", module.getChannelsCount()); lua_pushtableinteger(L, "Type", module.type); #if defined(MULTIMODULE) if (module.type == MODULE_TYPE_MULTIMODULE) { @@ -175,13 +175,16 @@ Set RF module parameters @notice If a parameter is missing from the value, then that parameter remains unchanged. -@status current Introduced in TODO +@status current Introduced in 2.2.0, modified in 2.3.12 (proto/subproto) */ static int luaModelSetModule(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < NUM_MODULES) { + int protocol = -1; + int subprotocol = -1; + ModuleData & module = g_model.moduleData[idx]; luaL_checktype(L, -1, LUA_TTABLE); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { @@ -202,7 +205,22 @@ static int luaModelSetModule(lua_State *L) else if (!strcmp(key, "channelsCount")) { module.channelsCount = luaL_checkinteger(L, -1) - 8; } +#if defined(MULTIMODULE) + if (!strcmp(key, "protocol")) { + protocol = luaL_checkinteger(L, -1); + } + if (!strcmp(key, "subProtocol")) { + subprotocol = luaL_checkinteger(L, -1); + } +#endif } +#if defined(MULTIMODULE) + if (protocol > 0 && subprotocol >= 0) { // Both are needed to compute otx protocol + convertMultiProtocolToOtx(&protocol, &subprotocol); + g_model.moduleData[idx].setMultiProtocol(protocol - 1); + g_model.moduleData[idx].subType = subprotocol; + } +#endif storageDirty(EE_MODEL); } return 0; diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index f30f1ba60..1046364f4 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -773,6 +773,7 @@ void displayLuaError(const char * title) } } +#if !defined(COLORLCD) void displayAcknowledgeLuaError(event_t event) { warningResult = false; @@ -781,6 +782,7 @@ void displayAcknowledgeLuaError(event_t event) warningText = nullptr; } } +#endif void luaError(lua_State * L, uint8_t error, bool acknowledge) { @@ -819,9 +821,11 @@ void luaError(lua_State * L, uint8_t error, bool acknowledge) } if (acknowledge) { +#if !defined(COLORLCD) warningText = errorTitle; warningType = WARNING_TYPE_INFO; popupFunc = displayAcknowledgeLuaError; +#endif } else { displayLuaError(errorTitle); diff --git a/radio/src/main.cpp b/radio/src/main.cpp index dfc742c5a..09f2004a0 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -30,30 +30,56 @@ uint8_t currentBacklightBright = 0; uint8_t requiredBacklightBright = 0; uint8_t mainRequestFlags = 0; +static bool _usbDisabled = false; + #if defined(LIBOPENUI) +static Menu* _usbMenu = nullptr; + +void closeUsbMenu() +{ + if (_usbMenu && !usbPlugged()) { + + // USB has been unplugged meanwhile + _usbMenu->deleteLater(); + } +} + void openUsbMenu() { - static Menu * menu = nullptr; - if (menu) - return; - menu = new Menu(MainWindow::instance()); - menu->setCloseHandler([]() { - menu = nullptr; + if (_usbMenu || _usbDisabled) return; + + _usbMenu = new Menu(MainWindow::instance()); + + _usbMenu->setCloseHandler([]() { + _usbMenu = nullptr; }); - menu->setTitle("USB"); - menu->addLine(STR_USB_JOYSTICK, [] { + + _usbMenu->setCancelHandler([]() { + if (usbPlugged() && (getSelectedUsbMode() == USB_UNSELECTED_MODE)) { + TRACE("disable USB"); + _usbDisabled = true; + } + }); + + _usbMenu->setTitle("USB"); + _usbMenu->addLine(STR_USB_JOYSTICK, [] { + TRACE("USB set joystick"); setSelectedUsbMode(USB_JOYSTICK_MODE); }); - menu->addLine(STR_USB_MASS_STORAGE, [] { + _usbMenu->addLine(STR_USB_MASS_STORAGE, [] { + TRACE("USB mass storage"); setSelectedUsbMode(USB_MASS_STORAGE_MODE); }); #if defined(DEBUG) - menu->addLine(STR_USB_SERIAL, [] { + _usbMenu->addLine(STR_USB_SERIAL, [] { + TRACE("USB serial"); setSelectedUsbMode(USB_SERIAL_MODE); }); #endif } + #elif defined(STM32) + void onUSBConnectMenu(const char *result) { if (result == STR_USB_MASS_STORAGE) { @@ -65,6 +91,9 @@ void onUSBConnectMenu(const char *result) else if (result == STR_USB_SERIAL) { setSelectedUsbMode(USB_SERIAL_MODE); } + else if (result == STR_EXIT) { + _usbDisabled = true; + } } void openUsbMenu() @@ -77,35 +106,63 @@ void openUsbMenu() POPUP_MENU_TITLE(STR_SELECT_MODE); POPUP_MENU_START(onUSBConnectMenu); } + +void closeUsbMenu() +{ +} + #endif void handleUsbConnection() { #if defined(STM32) && !defined(SIMU) - if (!usbStarted() && usbPlugged()) { + + static bool _pluggedUsb = false; + + if (_pluggedUsb && !usbPlugged()) { + TRACE("USB unplugged"); + closeUsbMenu(); + _pluggedUsb = false; + } + else if (!_pluggedUsb && usbPlugged()) { + TRACE("USB plugged"); + _pluggedUsb = true; + _usbDisabled = false; + } + + if (!_usbDisabled && !usbStarted() && usbPlugged()) { + if (getSelectedUsbMode() == USB_UNSELECTED_MODE) { - if (g_eeGeneral.USBMode == USB_UNSELECTED_MODE && popupMenuItemsCount == 0) { + if (g_eeGeneral.USBMode == USB_UNSELECTED_MODE) { openUsbMenu(); } else { setSelectedUsbMode(g_eeGeneral.USBMode); } } - else { + + // Mode might have been selected in previous block + // so re-evaluate the condition + if (getSelectedUsbMode() != USB_UNSELECTED_MODE) { + if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE) { opentxClose(false); usbPluggedIn(); } + usbStart(); + TRACE("USB started"); } } if (usbStarted() && !usbPlugged()) { usbStop(); + TRACE("USB stopped"); if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE) { opentxResume(); pushEvent(EVT_ENTRY); } + TRACE("reset selected USB mode"); setSelectedUsbMode(USB_UNSELECTED_MODE); } #endif // defined(STM32) && !defined(SIMU) @@ -423,7 +480,7 @@ void perMain() checkSpeakerVolume(); - if (!usbPlugged()) { + if (!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) { checkEeprom(); logsWrite(); } @@ -458,14 +515,17 @@ void perMain() #endif #if defined(STM32) - if (!usbPlugged() && SD_CARD_PRESENT() && !sdMounted()) { + if ((!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) + && SD_CARD_PRESENT() && !sdMounted()) { sdMount(); } #endif #if !defined(EEPROM) // In case the SD card is removed during the session - if (!usbPlugged() && !SD_CARD_PRESENT() && !globalData.unexpectedShutdown) { + if ((!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) + && !SD_CARD_PRESENT() && !globalData.unexpectedShutdown) { + drawFatalErrorScreen(STR_NO_SDCARD); return; } @@ -475,7 +535,6 @@ void perMain() if (usbPlugged() && getSelectedUsbMode() == USB_MASS_STORAGE_MODE) { #if defined(LIBOPENUI) // draw some image showing USB - lcdNextLayer(); lcd->reset(); OpenTxTheme::instance()->drawUsbPluggedScreen(lcd); lcdRefresh(); diff --git a/radio/src/mixer_scheduler.cpp b/radio/src/mixer_scheduler.cpp index dcf95383b..fef9034c0 100644 --- a/radio/src/mixer_scheduler.cpp +++ b/radio/src/mixer_scheduler.cpp @@ -68,9 +68,13 @@ void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs) mixerSchedules[moduleIdx].period = periodUs; } -bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs) +void mixerSchedulerClearTrigger() { RTOS_CLEAR_FLAG(mixerFlag); +} + +bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs) +{ return RTOS_WAIT_FLAG(mixerFlag, timeoutMs); } diff --git a/radio/src/mixer_scheduler.h b/radio/src/mixer_scheduler.h index 601f3fcd7..75a6ad1aa 100644 --- a/radio/src/mixer_scheduler.h +++ b/radio/src/mixer_scheduler.h @@ -43,6 +43,9 @@ void mixerSchedulerResetTimer(); // Set the scheduling period for a given module void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs); +// Clear the flag before waiting +void mixerSchedulerClearTrigger(); + // Wait for the scheduler timer to trigger // returns true if timeout, false otherwise bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs); @@ -66,6 +69,7 @@ void mixerSchedulerISRTrigger(); #define mixerSchedulerStop() #define mixerSchedulerResetTimer() #define mixerSchedulerSetPeriod(m,p) +#define mixerSchedulerClearTrigger() static inline bool mixerSchedulerWaitForTrigger(uint8_t timeout) { diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 0dd4f9597..710ac0a9b 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -780,6 +780,9 @@ void checkAll() checkMultiLowPower(); #endif +#if defined(COLORLCD) + #warning "KEYSTUCK Message Not Yet Implemented" +#else if (!waitKeysReleased()) { showMessageBox(STR_KEYSTUCK); tmr10ms_t tgtime = get_tmr10ms() + 500; @@ -788,6 +791,7 @@ void checkAll() WDG_RESET(); } } +#endif #if defined(EXTERNAL_ANTENNA) && defined(INTERNAL_MODULE_PXX1) checkExternalAntenna(); @@ -1844,7 +1848,7 @@ void opentxInit() if (globalData.unexpectedShutdown) { // SDCARD not available, try to restore last model from RAM TRACE("rambackupRestore"); -// rambackupRestore(); + rambackupRestore(); } else { storageReadAll(); @@ -2049,14 +2053,18 @@ uint32_t pwrCheck() #if defined(SHUTDOWN_CONFIRMATION) while (1) #else - while ((TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm)) + while ((usbPlugged() && getSelectedUsbMode() != USB_UNSELECTED_MODE) || + (TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm)) #endif { #if !defined(COLORLCD) + lcdRefreshWait(); lcdClear(); + POPUP_CONFIRMATION(STR_MODEL_SHUTDOWN, nullptr); + #if defined(SHUTDOWN_CONFIRMATION) if (TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm) { SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0); @@ -2064,9 +2072,12 @@ uint32_t pwrCheck() #else SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0); #endif + event_t evt = getEvent(false); DISPLAY_WARNING(evt); LED_ERROR_BEGIN(); + + WDG_RESET(); lcdRefresh(); if (warningResult) { @@ -2079,8 +2090,30 @@ uint32_t pwrCheck() LED_ERROR_END(); return e_power_on; } -#endif +#else // COLORLCD + + const char* message = nullptr; + if (usbPlugged()) { + message = STR_USB_STILL_CONNECTED; + } + else { + message = STR_MODEL_STILL_POWERED; + } + + // TODO: abort dialog condition (here, RSSI lost / USB connected) + if (confirmationDialog(STR_MODEL_SHUTDOWN, message, false)) { + pwr_check_state = PWR_CHECK_OFF; + return e_power_off; + } else { + // shutdown has been cancelled + pwr_check_state = PWR_CHECK_PAUSED; + LED_ERROR_END(); + return e_power_on; + } + +#endif // COLORLCD } + haptic.play(15, 3, PLAY_NOW); pwr_check_state = PWR_CHECK_OFF; return e_power_off; diff --git a/radio/src/opentx.h b/radio/src/opentx.h index f60c4ddeb..44880f5bb 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -460,29 +460,41 @@ uint16_t evalChkSum(); void alert(const char * title, const char * msg, uint8_t sound); #if !defined(GUI) + #define RAISE_ALERT(...) #define ALERT(...) -#elif defined(COLORLCD) - void raiseAlert(const char * title, const char * msg, const char * info, uint8_t sound); - inline void RAISE_ALERT(const char * title, const char * msg, const char * info, uint8_t sound) - { - raiseAlert(title, msg, info, sound); - } - inline void ALERT(const char * title, const char * msg, uint8_t sound) - { - raiseAlert(title, msg, "", sound); - } -#else - inline void RAISE_ALERT(const char * title, const char * msg, const char * info, uint8_t sound) - { - showAlertBox(title, msg, info, sound); - } - inline void ALERT(const char * title, const char * msg, uint8_t sound) - { - alert(title, msg, sound); - } -#endif +#elif defined(COLORLCD) + +bool confirmationDialog(const char *title, const char *msg, bool checkPwr = true); + +void raiseAlert(const char *title, const char *msg, const char *info, + uint8_t sound); + +inline void RAISE_ALERT(const char *title, const char *msg, const char *info, + uint8_t sound) +{ + raiseAlert(title, msg, info, sound); +} +inline void ALERT(const char *title, const char *msg, uint8_t sound) +{ + raiseAlert(title, msg, "", sound); +} + +#else // !COLORLCD && GUI + +inline void RAISE_ALERT(const char *title, const char *msg, const char *info, + uint8_t sound) +{ + showAlertBox(title, msg, info, sound); +} + +inline void ALERT(const char *title, const char *msg, uint8_t sound) +{ + alert(title, msg, sound); +} + +#endif // !COLORLCD && GUI enum PerOutMode { e_perout_mode_normal = 0, @@ -833,6 +845,7 @@ enum FunctionsActive { FUNCTION_BACKGND_MUSIC, FUNCTION_BACKGND_MUSIC_PAUSE, FUNCTION_BACKLIGHT, + FUNCTION_RACING_MODE, }; #define VARIO_FREQUENCY_ZERO 700/*Hz*/ @@ -1007,10 +1020,10 @@ union ReusableBuffer #if defined(EEPROM_RLC) && LCD_W < 212 uint16_t eepromfree; #endif -#if defined(SDCARD) +#if defined(SDCARD) && !defined(COLORLCD) char menu_bss[POPUP_MENU_MAX_LINES][MENU_LINE_LENGTH]; char mainname[45]; // because reused for SD backup / restore, max backup filename 44 chars: "/MODELS/MODEL0134353-2014-06-19-04-51-27.bin" -#else +#elif !defined(COLORLCD) char mainname[LEN_MODEL_NAME]; #endif } modelsel; diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index 6fbec157e..b6b7a6abc 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -177,12 +177,17 @@ enum ModuleSubtypeMulti { MODULE_SUBTYPE_MULTI_ELRS, MODULE_SUBTYPE_MULTI_REALACC, MODULE_SUBTYPE_MULTI_OMP, - MODULE_SUBTYPE_MULTI_MLINK, //75 + MODULE_SUBTYPE_MULTI_MLINK, //75 MODULE_SUBTYPE_MULTI_WFLY2, MODULE_SUBTYPE_MULTI_E016HV2, - MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_E016HV2 + MODULE_SUBTYPE_MULTI_E010R5, + MODULE_SUBTYPE_MULTI_LOLI, + MODULE_SUBTYPE_MULTI_E129, //80 + MODULE_SUBTYPE_MULTI_JOYSWAY, + MODULE_SUBTYPE_MULTI_E016H, + MODULE_SUBTYPE_MULTI_CONFIG, + MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_CONFIG }; -#define MODULE_SUBTYPE_MULTI_XN297DP 63-3 enum MMDSM2Subtypes { MM_RF_DSM2_SUBTYPE_DSM2_22, diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index f483d9b6b..227aa974d 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -706,7 +706,7 @@ inline void getMultiOptionValues(int8_t multi_proto, int8_t & min, int8_t & max) min = 0; max = 70; break; - case MODULE_SUBTYPE_MULTI_XN297DP: + case MODULE_SUBTYPE_MULTI_XN297DUMP: min = -1; max = 84; break; diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index 37364199a..c6e56126c 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -42,6 +42,7 @@ static void sendD16BindOption(uint8_t moduleIdx); #if defined(LUA) static void sendSport(uint8_t moduleIdx); static void sendHott(uint8_t moduleIdx); +static void sendConfig(uint8_t moduleIdx); static void sendDSM(uint8_t moduleIdx); #endif @@ -190,11 +191,14 @@ void setupPulsesMulti(uint8_t moduleIdx) #if defined(LUA) // SPort send if (IS_D16_MULTI(moduleIdx) && outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT && outputTelemetryBuffer.size) { - sendSport(moduleIdx); //8 bytes of additional data + sendSport(moduleIdx); //8 bytes of additional data } else if (IS_HOTT_MULTI(moduleIdx)) { sendHott(moduleIdx); //1 byte of additional data } + else if (IS_CONFIG_MULTI(moduleIdx)) { + sendConfig(moduleIdx); //7 bytes of additional data + } else if (IS_DSM_MULTI(moduleIdx)) { sendDSM(moduleIdx); //7 bytes of additional data } @@ -251,10 +255,54 @@ void sendChannels(uint8_t moduleIdx) } } +void convertMultiProtocolToOtx(int *protocol, int *subprotocol) +{ + if (*protocol == 3 && *subprotocol == 0) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + *subprotocol = MM_RF_FRSKY_SUBTYPE_D8; + return; + } + + if (*protocol == 3 && *subprotocol == 1) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + *subprotocol = MM_RF_FRSKY_SUBTYPE_D8_CLONED; + return; + } + + if (*protocol == 25) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + *subprotocol = MM_RF_FRSKY_SUBTYPE_V8; + return; + } + + if (*protocol == 15) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + + if (*subprotocol == 0) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16; + else if (*subprotocol == 1) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_8CH; + else if (*subprotocol == 2) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_LBT; + else if (*subprotocol == 3) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH; + else if (*subprotocol == 4) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_CLONED; + + return; + } + + if (*protocol >= 25) + *protocol -= 1; + + if (*protocol >= 16) + *protocol -= 1; +} + void convertOtxProtocolToMulti(int *protocol, int *subprotocol) { // Special treatment for the FrSky entry... - if (*protocol == MODULE_SUBTYPE_MULTI_FRSKY +1) { + if (*protocol == MODULE_SUBTYPE_MULTI_FRSKY + 1) { if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D8) { //D8 *protocol = 3; @@ -288,10 +336,10 @@ void convertOtxProtocolToMulti(int *protocol, int *subprotocol) // 15 for Multimodule is FrskyX or D16 which we map as a protocol of 3 (FrSky) // all protos > frskyx are therefore also off by one if (*protocol >= 15) - *protocol = *protocol + 1; + *protocol += 1; // 25 is again a FrSky *protocol (FrskyV) so shift again if (*protocol >= 25) - *protocol = *protocol + 1; + *protocol += 1; } } @@ -404,6 +452,23 @@ void sendHott(uint8_t moduleIdx) } } +void sendConfig(uint8_t moduleIdx) +{ + // Multi_Buffer[0..3]=="Conf" -> Lua script is running + // Multi_Buffer[4]==0x01 -> TX to Module data ready to be sent + // Multi_Buffer[4]==0xFF -> Clear buffer data + // Multi_Buffer[5..11]=7 bytes of TX to Module data + // Multi_Buffer[12] -> Current page + // Multi_Buffer[13..172]=8*20=160 bytes of Module to TX data + if (Multi_Buffer && memcmp(Multi_Buffer, "Conf", 4) == 0 && Multi_Buffer[4] == 0x01) { + // Config Lua script is running and sending + for (uint8_t i = 0; i < 7; i++) { + sendMulti(moduleIdx, Multi_Buffer[5 + i]); + } + Multi_Buffer[4] = 0x00; // Send data only once + } +} + void sendDSM(uint8_t moduleIdx) { // Multi_Buffer[0..2]=="DSM" -> Lua script is running diff --git a/radio/src/pulses/multi.h b/radio/src/pulses/multi.h index 29e335d3a..e3771d7eb 100644 --- a/radio/src/pulses/multi.h +++ b/radio/src/pulses/multi.h @@ -25,6 +25,7 @@ void multiPatchCustom(uint8_t moduleIdx); void convertOtxProtocolToMulti(int *protocol, int *subprotocol); +void convertMultiProtocolToOtx(int *protocol, int *subprotocol); class UartMultiPulses: public DataBuffer { diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 7c034a06f..8ba672aa0 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -547,7 +547,7 @@ bool setupPulsesInternalModule(uint8_t protocol) else { #if defined(INTMODULE_HEARTBEAT) mixerSchedulerResetTimer(); - mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 1000 /* backup */); + mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 2000 /* backup */); #else mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD); #endif @@ -641,10 +641,11 @@ void setCustomFailsafe(uint8_t moduleIndex) g_model.failsafeChannels[ch] = channelOutputs[ch]; } } + storageDirty(EE_MODEL); } } -int32_t getChannelValue(uint8_t channel) { +int32_t getChannelValue(uint8_t channel) +{ return channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER; } - diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index 5f53a446e..e72caaa66 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -48,14 +48,20 @@ void Pxx2Pulses::addFlag1(uint8_t module) else { subType = g_model.moduleData[module].subType; } - Pxx2Transport::addByte(subType << 4); + + uint8_t flag1 = subType << 4u; + if (isRacingModeEnabled() && isFunctionActive(FUNCTION_RACING_MODE)) { + flag1 |= PXX2_CHANNELS_FLAG1_RACING_MODE; + } + + Pxx2Transport::addByte(flag1); } void Pxx2Pulses::addPulsesValues(uint16_t low, uint16_t high) { Pxx2Transport::addByte(low); // Low byte of channel - Pxx2Transport::addByte(((low >> 8) & 0x0F) | (high << 4)); // 4 bits each from 2 channels - Pxx2Transport::addByte(high >> 4); // High byte of channel + Pxx2Transport::addByte(((low >> 8u) & 0x0Fu) | (high << 4u)); // 4 bits each from 2 channels + Pxx2Transport::addByte(high >> 4u); // High byte of channel } void Pxx2Pulses::addChannels(uint8_t module) diff --git a/radio/src/pulses/pxx2.h b/radio/src/pulses/pxx2.h index 9fe910c5c..e92a8a350 100644 --- a/radio/src/pulses/pxx2.h +++ b/radio/src/pulses/pxx2.h @@ -47,6 +47,7 @@ #define PXX2_CHANNELS_FLAG0_FAILSAFE (1 << 6) #define PXX2_CHANNELS_FLAG0_RANGECHECK (1 << 7) +#define PXX2_CHANNELS_FLAG1_RACING_MODE (1 << 3) #define PXX2_RX_SETTINGS_FLAG0_WRITE (1 << 6) @@ -269,6 +270,7 @@ enum ReceiverCapabilities { RECEIVER_CAPABILITY_TELEMETRY_25MW, RECEIVER_CAPABILITY_ENABLE_PWM_CH5_CH6, RECEIVER_CAPABILITY_FPORT2, + RECEIVER_CAPABILITY_RACING_MODE, RECEIVER_CAPABILITY_COUNT }; diff --git a/radio/src/sdcard.cpp b/radio/src/sdcard.cpp index be6370a08..398fa477e 100644 --- a/radio/src/sdcard.cpp +++ b/radio/src/sdcard.cpp @@ -210,6 +210,7 @@ const char * getBasename(const char * path) return path; } +#if !defined(LIBOPENUI) bool sdListFiles(const char * path, const char * extension, const uint8_t maxlen, const char * selection, uint8_t flags) { static uint16_t lastpopupMenuOffset = 0; @@ -427,6 +428,7 @@ void sdReadTextFile(const char * filename, char lines[TEXT_VIEWER_LINES][LCD_COL lines_count = current_line; } } +#endif // !LIBOPENUI #if defined(SDCARD) const char * sdCopyFile(const char * srcPath, const char * destPath) diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index adbe4788a..2ba72bef6 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -86,7 +86,7 @@ FXIMPLEMENT(OpenTxSim, FXMainWindow, OpenTxSimMap, ARRAYNUMBER(OpenTxSimMap)) OpenTxSim::OpenTxSim(FXApp* a): FXMainWindow(a, "OpenTX Simu", nullptr, nullptr, DECOR_ALL, 20, 90, 0, 0) { - memset(displayBuf, 0, DISPLAY_BUFFER_SIZE * sizeof(pixel_t)); + lcdInit(); bmp = new FXPPMImage(getApp(), nullptr, IMAGE_OWNED|IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP, W2, H2); #if defined(SIMU_AUDIO) @@ -500,8 +500,17 @@ void OpenTxSim::setPixel(int x, int y, FXColor color) void OpenTxSim::refreshDisplay() { - if (simuLcdRefresh) { + static bool lightEnabled = (bool)isBacklightEnabled(); + + if ((bool(isBacklightEnabled()) != lightEnabled) || simuLcdRefresh) { + simuLcdRefresh = false; + + if (bool(isBacklightEnabled()) != lightEnabled) { + lightEnabled = (bool)isBacklightEnabled(); + TRACE("backlight %s", lightEnabled ? "ON" : "OFF"); + } + FXColor offColor = isBacklightEnabled() ? BL_COLOR : FXRGB(200, 200, 200); #if LCD_DEPTH == 1 FXColor onColor = FXRGB(0, 0, 0); diff --git a/radio/src/stamp.cpp b/radio/src/stamp.cpp index 5ed727836..ad8e716ab 100644 --- a/radio/src/stamp.cpp +++ b/radio/src/stamp.cpp @@ -85,7 +85,8 @@ const char * getFirmwareVersion(const char * buffer) } for (int i = 0; i < 1024; i++) { - if (memcmp(buffer + i, "edgetx-", 7) == 0) { + if ((memcmp(buffer + i, "edgetx-", 7) == 0) + || memcmp(buffer + i, "opentx-", 7) == 0) { return buffer + i; } } diff --git a/radio/src/stubs.cpp b/radio/src/stubs.cpp deleted file mode 100644 index a4583d329..000000000 --- a/radio/src/stubs.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "opentx.h" - -const char *warningText = NULL; -const char *warningInfoText; -uint8_t warningInfoLength; -uint8_t warningType; -uint8_t warningResult = 0; -uint8_t warningInfoFlags; -void (*popupFunc)(uint16_t event) = NULL; -const char *popupMenuItems[POPUP_MENU_MAX_LINES]; -uint16_t popupMenuItemsCount = 0; -uint16_t popupMenuOffset = 0; -uint8_t popupMenuOffsetType = 0; -void (*popupMenuHandler)(const char * result); -uint8_t noHighlightCounter; - -void runPopupWarning(uint16_t event) -{ -} - -void drawFatalErrorScreen(const char * message) -{ -} - -void showMessageBox(const char * title) -{ - -} - -bool menuFirstCalib(event_t event) -{ - return false; -} - -void drawAlertBox(const char * title, const char * text, const char * action) -{ - -} - -uint8_t menuLevel; -int menuVerticalOffset; - -void runFatalErrorScreen(char const*) -{ - -} diff --git a/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp b/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp index 49e27e33a..4498fd55d 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp @@ -121,15 +121,19 @@ void extractFirmwareVersion(VersionTag* tag) if (!vers || (vers[0] == 'n' && vers[1] == 'o')) { // "no version found" memcpy(tag->flavour, "unknown", sizeof("unknown")); tag->version = "unknown"; + return; } - // skip "opentx-" - vers += sizeof("opentx-") - 1; + tag->fork = vers; + + // skip 'edgetx-' / 'opentx-' + vers += sizeof("edgetx-") - 1; char* fl = tag->flavour; while(*vers != '-') *(fl++) = *(vers++); + // skip '-' tag->version = ++vers; } diff --git a/radio/src/targets/common/arm/stm32/bootloader/bin_files.h b/radio/src/targets/common/arm/stm32/bootloader/bin_files.h index dc9e01e2b..408cab4f9 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/bin_files.h +++ b/radio/src/targets/common/arm/stm32/bootloader/bin_files.h @@ -72,6 +72,7 @@ struct VersionTag { char flavour[8]; const char* version; + const char* fork; }; // Can be called right after openBinFile() to extract the version information diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index c75efe9fe..417d32cf2 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -435,7 +435,6 @@ int main() state = ST_FLASHING; } } - else if (state == ST_FLASHING) { // commit to flashing if (!unlocked && (memoryType == MEM_FLASH)) { diff --git a/radio/src/targets/horus/bootloader/boot_menu.cpp b/radio/src/targets/horus/bootloader/boot_menu.cpp index a4ae513d1..a74604aba 100644 --- a/radio/src/targets/horus/bootloader/boot_menu.cpp +++ b/radio/src/targets/horus/bootloader/boot_menu.cpp @@ -95,12 +95,6 @@ static void bootloaderDrawBackground() void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) { - // shows current layer on LCD - lcdRefresh(); - - // swap back & front buffers - lcdNextLayer(); - bootloaderDrawBackground(); if (st == ST_START) { @@ -141,7 +135,7 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) opt = 100; // Completed > 100% } - lcd->drawRect(70, 120, 340, 31, 2); + lcd->drawRect(70, 120, 340, 31, 2, SOLID, BL_SELECTED); lcd->drawSolidFilledRect(74, 124, (332 * opt) / 100, 23, color); } else if (st == ST_DIR_CHECK) { @@ -156,7 +150,6 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) lcd->drawBitmapPattern(356, 158, LBM_ERROR, BL_RED); } else if (st == ST_FLASH_CHECK) { - bootloaderDrawFilename(str, 0, true); if (opt == FC_ERROR) { @@ -165,8 +158,12 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) } else if (opt == FC_OK) { VersionTag tag; + memset(&tag, 0, sizeof(tag)); extractFirmwareVersion(&tag); + lcd->drawText(168, 138, "Fork:", RIGHT | BL_FOREGROUND); + lcd->drawSizedText(174, 138, tag.fork, 6, BL_FOREGROUND); + lcd->drawText(168, 158, "Version:", RIGHT | BL_FOREGROUND); lcd->drawText(174, 158, tag.version, BL_FOREGROUND); diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index e73d5c3d3..878e3ffea 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -131,9 +131,8 @@ void extmodulePxx1PulsesStart() #endif EXTMODULE_TIMER->ARR = 45000; - EXTMODULE_TIMER->CCR2 = 40000; // The first frame will be sent in 20ms EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_UDE | TIM_DIER_CC2IE; // Enable DMA on update + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); @@ -183,8 +182,6 @@ void extmoduleSerialStart() NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); NVIC_SetPriority(EXTMODULE_TIMER_DMA_STREAM_IRQn, 7); - NVIC_EnableIRQ(EXTMODULE_TIMER_CC_IRQn); - NVIC_SetPriority(EXTMODULE_TIMER_CC_IRQn, 7); } #if defined(EXTMODULE_USART) @@ -292,13 +289,20 @@ void extmoduleSendNextFrame() #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: - EXTMODULE_TIMER->CCR2 = extmodulePulsesData.pxx.getLast() - 4000; // 2mS in advance - EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA + if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) + return; + + // disable timer + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | EXTMODULE_TIMER_DMA_SIZE | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.pxx.getData()); EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.pxx.getSize(); EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = 1; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; break; #endif @@ -432,7 +436,6 @@ extern "C" void EXTMODULE_TIMER_DMA_IRQHandler() DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); switch (moduleState[EXTERNAL_MODULE].protocol) { - case PROTOCOL_CHANNELS_PXX1_PULSES: case PROTOCOL_CHANNELS_PPM: EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 6275ca903..4d7399a18 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -51,7 +51,9 @@ uint8_t currentLayer = LCD_FIRST_LAYER; BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); -BitmapBuffer * lcd = &lcdBuffer1; + +BitmapBuffer * lcdFront = &lcdBuffer1; +BitmapBuffer * lcd = &lcdBuffer2; inline void LCD_NRST_LOW() { @@ -63,11 +65,6 @@ inline void LCD_NRST_HIGH() LCD_GPIO_NRST->BSRRL = LCD_GPIO_PIN_NRST; } -inline bool coordsWithinScreen(coord_t x, coord_t y, coord_t w, coord_t h) -{ - return x >= 0 && y >= 0 && x + w <= LCD_W && y + h <= LCD_H; -} - static void LCD_AF_GPIOConfig() { GPIO_InitTypeDef GPIO_InitStructure; @@ -225,15 +222,15 @@ void LCD_Init_LTDC() // init ltdc LTDC_Init(<DC_InitStruct); -#if 0 - LTDC_ITConfig(LTDC_IER_LIE, ENABLE); + LTDC_ITConfig(LTDC_IER_RRIE, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = LTDC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = LTDC_IRQ_PRIO; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pr e-emption priority. */; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); +#if 0 DMA2D_ITConfig(DMA2D_CR_TCIE, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DMA2D_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = DMA_SCREEN_IRQ_PRIO; @@ -262,8 +259,10 @@ void LCD_LayerInit() /* Pixel Format configuration*/ LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565; + /* Alpha constant (255 totally opaque) */ LTDC_Layer_InitStruct.LTDC_ConstantAlpha = 255; + /* Default Color configuration (configure A,R,G,B component values) */ LTDC_Layer_InitStruct.LTDC_DefaultColorBlue = 0; LTDC_Layer_InitStruct.LTDC_DefaultColorGreen = 0; @@ -294,26 +293,12 @@ void LCD_LayerInit() /* Initialize LTDC layer 1 */ LTDC_LayerInit(LTDC_Layer1, <DC_Layer_InitStruct); - /* Configure Layer 2 */ - LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA; - LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA; - - /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ - LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t)LCD_SECOND_FRAME_BUFFER; - - /* Initialize LTDC layer 2 */ - LTDC_LayerInit(LTDC_Layer2, <DC_Layer_InitStruct); - /* LTDC configuration reload */ LTDC_ReloadConfig(LTDC_IMReload); + // Enable layer and reload LTDC_LayerCmd(LTDC_Layer1, ENABLE); - LTDC_LayerCmd(LTDC_Layer2, ENABLE); - LTDC_ReloadConfig(LTDC_IMReload); - - /* dithering activation */ - LTDC_DitherCmd(ENABLE); } void LCD_Init(void) @@ -325,55 +310,38 @@ void LCD_Init(void) /* Configure the LCD Control pins */ LCD_AF_GPIOConfig(); + // Enable LTDC IRQ in NVIC + NVIC_EnableIRQ(LTDC_IRQn); + NVIC_SetPriority(LTDC_IRQn, 7); + LCD_Init_LTDC(); } void LCD_SetLayer(uint32_t layer) { if (layer == LCD_FIRST_LAYER) { - lcd = &lcdBuffer1; + lcdFront = &lcdBuffer1; + lcd = &lcdBuffer2; } else { - lcd = &lcdBuffer2; + lcdFront = &lcdBuffer2; + lcd = &lcdBuffer1; } currentLayer = layer; } -/** - * @brief Configure the transparency. - * @param transparency: specifies the transparency, - * This parameter must range from 0x00 to 0xFF. - * @retval None - */ -void LCD_SetTransparency(uint8_t transparency) -{ - if (currentLayer == LCD_FIRST_LAYER) { - LTDC_LayerAlpha(LTDC_Layer1, transparency); - } - else { - LTDC_LayerAlpha(LTDC_Layer2, transparency); - } - LTDC_ReloadConfig(LTDC_IMReload); -} - void lcdInit() { - /* Initialize the LCD */ + // Clear buffers first + memset(LCD_FIRST_FRAME_BUFFER, 0, sizeof(LCD_FIRST_FRAME_BUFFER)); + memset(LCD_SECOND_FRAME_BUFFER, 0, sizeof(LCD_SECOND_FRAME_BUFFER)); + + // Initialize the LCD LCD_Init(); LCD_LayerInit(); - /* Enable LCD display */ + // Enable LCD display LTDC_Cmd(ENABLE); - - /* Set Background layer */ - memset(LCD_FIRST_FRAME_BUFFER, 0, sizeof(LCD_FIRST_FRAME_BUFFER)); - LCD_SetLayer(LCD_FIRST_LAYER); - LCD_SetTransparency(0); - - /* Set Foreground layer */ - memset(LCD_SECOND_FRAME_BUFFER, 0, sizeof(LCD_SECOND_FRAME_BUFFER)); - LCD_SetLayer(LCD_SECOND_LAYER); - LCD_SetTransparency(255); } void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) @@ -454,7 +422,6 @@ void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_ srcy = srch - (srcy + h); #endif - TRACE("------"); DMA2D_DeInit(); DMA2D_InitTypeDef DMA2D_InitStruct; @@ -505,7 +472,6 @@ void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t srcy = srch - (srcy + h); #endif - TRACE("++++++"); DMA2D_DeInit(); DMA2D_InitTypeDef DMA2D_InitStruct; @@ -637,25 +603,36 @@ uint16_t* lcdGetScratchBuffer() return (uint16_t*)LCD_SCRATCH_FRAME_BUFFER; } -void lcdRefresh() +static volatile uint8_t _frameBufferAddressReloaded = 0; + +extern "C" void LTDC_IRQHandler(void) { - if (currentLayer == LCD_FIRST_LAYER) { - LTDC_LayerAlpha(LTDC_Layer1, 255); - LTDC_LayerAlpha(LTDC_Layer2, 0); - } - else { - LTDC_LayerAlpha(LTDC_Layer1, 0); - LTDC_LayerAlpha(LTDC_Layer2, 255); - } - LTDC_ReloadConfig(LTDC_IMReload); + // clear interrupt flag + LTDC->ICR = LTDC_ICR_CRRIF; + _frameBufferAddressReloaded = 1; } -void lcdNextLayer() +static void lcdSwitchLayers() { if (currentLayer == LCD_FIRST_LAYER) { + LTDC_Layer1->CFBAR = (uint32_t)LCD_SECOND_FRAME_BUFFER; LCD_SetLayer(LCD_SECOND_LAYER); } else { + LTDC_Layer1->CFBAR = (uint32_t)LCD_FIRST_FRAME_BUFFER; LCD_SetLayer(LCD_FIRST_LAYER); } + + // reload shadow registers on vertical blank + _frameBufferAddressReloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + + // wait for reload + // TODO: replace through some smarter mechanism without busy wait + while(_frameBufferAddressReloaded == 0); +} + +void lcdRefresh() +{ + lcdSwitchLayers(); } diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index 2b96aa06d..a6317dc61 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -89,24 +89,6 @@ Adc Adc0; FATFS g_FATFS_Obj; -void lcdInit() -{ -} - -void lcdCopy(void * dest, void * src) -{ - -} - -void lcdNextLayer() -{ - -} - -void toplcdOff() -{ -} - uint64_t simuTimerMicros(void) { #if SIMPGMSPC_USE_QT @@ -204,7 +186,9 @@ void simuStart(bool tests, const char * sdPath, const char * settingsPath) return; stopPulses(); +#if !defined(COLORLCD) menuLevel = 0; +#endif simu_start_mode = (tests ? 0 : OPENTX_START_NO_SPLASH | OPENTX_START_NO_CALIBRATION | OPENTX_START_NO_CHECKS); simu_shutdown = false; @@ -428,40 +412,12 @@ void stopAudioThread() } #endif // #if defined(SIMU_AUDIO) -bool simuLcdRefresh = true; -pixel_t simuLcdBuf[DISPLAY_BUFFER_SIZE]; - #if !defined(COLORLCD) void lcdSetRefVolt(uint8_t val) { } #endif -#if defined(PCBTARANIS) -void lcdOff() -{ -} -#endif - -void lcdRefresh() -{ - static bool lightEnabled = (bool)isBacklightEnabled(); - - if (bool(isBacklightEnabled()) != lightEnabled || memcmp(simuLcdBuf, displayBuf, DISPLAY_BUFFER_SIZE * sizeof(pixel_t))) { -#if defined(LCD_VERTICAL_INVERT) - auto src = (pixel_t*)displayBuf + DISPLAY_BUFFER_SIZE - 1; - auto dst = simuLcdBuf; - auto end = dst + DISPLAY_BUFFER_SIZE; - - while (dst != end) { *(dst++) = *(src--); } -#else - memcpy(simuLcdBuf, displayBuf, DISPLAY_BUFFER_SIZE * sizeof(pixel_t)); -#endif - lightEnabled = (bool)isBacklightEnabled(); - simuLcdRefresh = true; - } -} - void telemetryPortInit(uint8_t baudrate) { } @@ -527,23 +483,6 @@ void boardInit() { } -pixel_t simuLcdBackupBuf[DISPLAY_BUFFER_SIZE]; -void lcdStoreBackupBuffer() -{ - memcpy(simuLcdBackupBuf, displayBuf, sizeof(simuLcdBackupBuf)); -} - -int lcdRestoreBackupBuffer() -{ - memcpy(displayBuf, simuLcdBackupBuf, sizeof(displayBuf)); - return 1; -} - -uint16_t* lcdGetBackupBuffer() -{ - return (uint16_t*)simuLcdBackupBuf; -} - uint32_t pwrCheck() { // TODO: ability to simulate shutdown warning for a "soft" simulator restart diff --git a/radio/src/targets/simu/simulcd.cpp b/radio/src/targets/simu/simulcd.cpp index c92ace74d..0620565b9 100644 --- a/radio/src/targets/simu/simulcd.cpp +++ b/radio/src/targets/simu/simulcd.cpp @@ -20,35 +20,124 @@ #include "lcd.h" #include "simulcd.h" +#include +#include -#if defined(COLORLCD) +pixel_t simuLcdBuf[DISPLAY_BUFFER_SIZE]; +pixel_t simuLcdBackupBuf[DISPLAY_BUFFER_SIZE]; -pixel_t displayBuf[DISPLAY_BUFFER_SIZE]; -pixel_t scratchBuf[DISPLAY_BUFFER_SIZE]; +bool simuLcdRefresh = true; -BitmapBuffer _lcd(BMP_RGB565, LCD_W, LCD_H, displayBuf); -BitmapBuffer * lcd = &_lcd; +void toplcdOff() {} -uint16_t * lcdGetScratchBuffer() +#if !defined(lcdOff) +void lcdOff() {} +#endif + +void lcdCopy(void *dest, void *src) { - return static_cast(scratchBuf); + memcpy(dest, src, DISPLAY_BUFFER_SIZE * sizeof(pixel_t)); } -void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) +uint16_t *lcdGetBackupBuffer() { return (uint16_t *)simuLcdBackupBuf; } + +#if !defined(COLORLCD) + +void lcdInit() {} + +void lcdRefresh() +{ + // Mark screen dirty for async refresh + simuLcdRefresh = true; + +#if defined(LCD_VERTICAL_INVERT) + auto src = displayBuf + DISPLAY_BUFFER_SIZE - 1; + auto dst = simuLcdBuf; + auto end = dst + DISPLAY_BUFFER_SIZE; + + while (dst != end) { + *(dst++) = *(src--); + } +#else + memcpy(simuLcdBuf, displayBuf, DISPLAY_BUFFER_SIZE * sizeof(pixel_t)); +#endif +} + +#else + +pixel_t displayBuf1[DISPLAY_BUFFER_SIZE]; +pixel_t displayBuf2[DISPLAY_BUFFER_SIZE]; +pixel_t scratchBuf[DISPLAY_BUFFER_SIZE]; + +BitmapBuffer _lcd1(BMP_RGB565, LCD_W, LCD_H, displayBuf1); +BitmapBuffer _lcd2(BMP_RGB565, LCD_W, LCD_H, displayBuf2); + +BitmapBuffer * lcd = &_lcd1; +BitmapBuffer * lcdFront = &_lcd2; + +uint16_t *lcdGetScratchBuffer() { return static_cast(scratchBuf); } + +void lcdStoreBackupBuffer() +{ + memcpy(simuLcdBackupBuf, lcd->getData(), + DISPLAY_BUFFER_SIZE * sizeof(pixel_t)); +} + +int lcdRestoreBackupBuffer() +{ + memcpy(lcd->getData(), simuLcdBackupBuf, + DISPLAY_BUFFER_SIZE * sizeof(pixel_t)); + return 1; +} + +void lcdRefresh() +{ + // Mark screen dirty for async refresh + simuLcdRefresh = true; + + pixel_t* lcdData = lcd->getData(); + +#if defined(LCD_VERTICAL_INVERT) + auto src = lcdData + DISPLAY_BUFFER_SIZE - 1; + auto dst = simuLcdBuf; + auto end = dst + DISPLAY_BUFFER_SIZE; + + while (dst != end) { + *(dst++) = *(src--); + } +#else + memcpy(simuLcdBuf, lcdData, DISPLAY_BUFFER_SIZE * sizeof(pixel_t)); +#endif + + // Swap back & front buffers + std::swap(lcd, lcdFront); +} + +void lcdInit() +{ + _lcd1.clear(); + _lcd2.clear(); +} + +void DMAFillRect(uint16_t *dest, uint16_t destw, uint16_t desth, uint16_t x, + uint16_t y, uint16_t w, uint16_t h, uint16_t color) { #if defined(LCD_VERTICAL_INVERT) x = destw - (x + w); y = desth - (y + h); #endif - for (int i=0; i> 12; - uint8_t red = ((((*q >> 8) & 0x0f) << 1) * alpha + (*p >> 11) * (0x0f-alpha)) / 0x0f; - uint8_t green = ((((*q >> 4) & 0x0f) << 2) * alpha + ((*p >> 5) & 0x3f) * (0x0f-alpha)) / 0x0f; - uint8_t blue = ((((*q >> 0) & 0x0f) << 1) * alpha + ((*p >> 0) & 0x1f) * (0x0f-alpha)) / 0x0f; + uint8_t red = + ((((*q >> 8) & 0x0f) << 1) * alpha + (*p >> 11) * (0x0f - alpha)) / + 0x0f; + uint8_t green = ((((*q >> 4) & 0x0f) << 2) * alpha + + ((*p >> 5) & 0x3f) * (0x0f - alpha)) / + 0x0f; + uint8_t blue = ((((*q >> 0) & 0x0f) << 1) * alpha + + ((*p >> 0) & 0x1f) * (0x0f - alpha)) / + 0x0f; *p = (red << 11) + (green << 5) + (blue << 0); - p++; q++; + p++; + q++; } } } // 'src' has A8/L8? // 'dest' has RGB565 -void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t fg_color) +void DMACopyAlphaMask(uint16_t *dest, uint16_t destw, uint16_t desth, + uint16_t x, uint16_t y, const uint8_t *src, uint16_t srcw, + uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, + uint16_t h, uint16_t fg_color) { #if defined(LCD_VERTICAL_INVERT) x = destw - (x + w); @@ -99,25 +201,26 @@ void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t #endif RGB_SPLIT(fg_color, red, green, blue); - - for (coord_t line=0; line> 4;// convert to 4 bits (stored in 8bit for DMA) + for (coord_t line = 0; line < h; line++) { + uint16_t *p = dest + (y + line) * destw + x; + const uint8_t *q = src + (srcy + line) * srcw + srcx; + for (coord_t col = 0; col < w; col++) { + uint16_t opacity = *q >> 4; // convert to 4 bits (stored in 8bit for DMA) uint8_t bgWeight = OPACITY_MAX - opacity; RGB_SPLIT(*p, bgRed, bgGreen, bgBlue); uint16_t r = (bgRed * bgWeight + red * opacity) / OPACITY_MAX; uint16_t g = (bgGreen * bgWeight + green * opacity) / OPACITY_MAX; uint16_t b = (bgBlue * bgWeight + blue * opacity) / OPACITY_MAX; *p = RGB_JOIN(r, g, b); - p++; q++; + p++; + q++; } } } -void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format) +void DMABitmapConvert(uint16_t *dest, const uint8_t *src, uint16_t w, + uint16_t h, uint32_t format) { if (format == DMA2D_ARGB4444) { for (int row = 0; row < h; ++row) { @@ -127,10 +230,9 @@ void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t src += 4; } } - } - else { + } else { for (int row = 0; row < h; ++row) { - for(int col = 0; col < w; ++col) { + for (int col = 0; col < w; ++col) { *dest = RGB(src[1], src[2], src[3]); ++dest; src += 4; diff --git a/radio/src/targets/simu/simulcd.h b/radio/src/targets/simu/simulcd.h index 58205311b..9fc6733fa 100644 --- a/radio/src/targets/simu/simulcd.h +++ b/radio/src/targets/simu/simulcd.h @@ -26,6 +26,7 @@ extern int g_snapshot_idx; extern bool simuLcdRefresh; + extern pixel_t simuLcdBuf[DISPLAY_BUFFER_SIZE]; extern pixel_t displayBuf[DISPLAY_BUFFER_SIZE]; diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 512f16c5d..46dd891bf 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -230,8 +230,6 @@ void extmodulePxx1PulsesStart() NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); NVIC_SetPriority(EXTMODULE_TIMER_DMA_STREAM_IRQn, 7); - NVIC_EnableIRQ(EXTMODULE_TIMER_CC_IRQn); - NVIC_SetPriority(EXTMODULE_TIMER_CC_IRQn, 7); } #endif @@ -259,14 +257,12 @@ void extmoduleSendNextFrame() #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: - if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) return; // disable timer EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; - EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.pxx.getData()); diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index ec046ddf5..27d68d9c0 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2041,7 +2041,7 @@ #define AUDIO_SPEAKER_ENABLE_GPIO_PIN GPIO_Pin_14 // PD.14 #define HEADPHONE_TRAINER_SWITCH_GPIO GPIOD #define HEADPHONE_TRAINER_SWITCH_GPIO_PIN GPIO_Pin_9 // PD.09 -#elif defined(AUDIO_MUTE_GPIO) +#elif defined(RADIO_TX12) || defined(RADIO_T8) || defined(RADIO_TLITE) #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) #define AUDIO_MUTE_GPIO GPIOE #define AUDIO_MUTE_GPIO_PIN GPIO_Pin_12 // PE.12 diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 970e152c5..c635c2ae1 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -110,6 +110,25 @@ void sendSynchronousPulses(uint8_t runMask) #endif } +constexpr uint8_t MIXER_FREQUENT_ACTIONS_PERIOD = 5 /*ms*/; +constexpr uint8_t MIXER_MAX_PERIOD = 30 /*ms*/; + +void execMixerFrequentActions() +{ +#if defined(SBUS_TRAINER) + // SBUS trainer + processSbusInput(); +#endif + +#if defined(GYRO) + gyro.wakeup(); +#endif + +#if defined(BLUETOOTH) + bluetooth.wakeup(); +#endif +} + uint32_t nextMixerTime[NUM_MODULES]; TASK_FUNCTION(mixerTask) @@ -117,26 +136,27 @@ TASK_FUNCTION(mixerTask) s_pulses_paused = true; mixerSchedulerInit(); + #if !defined(PCBSKY9X) mixerSchedulerStart(); #endif + // clear the flag before first loop + mixerSchedulerClearTrigger(); + while (true) { -#if defined(SBUS_TRAINER) - // SBUS trainer - processSbusInput(); -#endif + int timeout = 0; + for (; timeout < MIXER_MAX_PERIOD; timeout += MIXER_FREQUENT_ACTIONS_PERIOD) { -#if defined(GYRO) - gyro.wakeup(); -#endif + // run periodicals before waiting for the trigger + // to keep the delay short + execMixerFrequentActions(); -#if defined(BLUETOOTH) - bluetooth.wakeup(); -#endif - - // run mixer at least every 30ms - bool timeout = mixerSchedulerWaitForTrigger(30); + // mixer flag triggered? + if (!mixerSchedulerWaitForTrigger(MIXER_FREQUENT_ACTIONS_PERIOD)) { + break; + } + } #if defined(DEBUG_MIXER_SCHEDULER) GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); @@ -144,6 +164,9 @@ TASK_FUNCTION(mixerTask) #endif #if !defined(PCBSKY9X) + // clear the flag ASAP to avoid missing a tick + mixerSchedulerClearTrigger(); + // re-enable trigger mixerSchedulerEnableTrigger(); #endif @@ -205,10 +228,6 @@ TASK_FUNCTION(mixerTask) // TODO: // - check the cause of timeouts when switching // between protocols with multi-proto RF -#if defined(DEBUG) - if (timeout) - serialPrint("mix sched timeout!"); -#endif } } } diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index b9972e240..303cdb244 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -42,7 +42,8 @@ enum MultiPacketTypes : uint8_t FlyskyIBusTelemetryAC, MultiRxChannels, HottTelemetry, - MLinkTelemetry + MLinkTelemetry, + ConfigTelemetry }; enum MultiBufferState : uint8_t @@ -233,8 +234,11 @@ static void processMultiStatusPacket(const uint8_t * data, uint8_t module, uint8 } if (getMultiModuleStatus(module).requiresFailsafeCheck) { getMultiModuleStatus(module).requiresFailsafeCheck = false; - if (getMultiModuleStatus(module).supportsFailsafe() && g_model.moduleData[module].failsafeMode == FAILSAFE_NOT_SET) + + if (getMultiModuleStatus(module).supportsFailsafe() && + g_model.moduleData[module].failsafeMode == FAILSAFE_NOT_SET) { ALERT(STR_FAILSAFEWARN, STR_NO_FAILSAFE, AU_ERROR); + } } if (wasBinding && !status.isBinding() && getMultiBindStatus(module) == MULTI_BIND_INITIATED) @@ -291,6 +295,31 @@ static void processMultiRxChannels(const uint8_t * data, uint8_t len) } #endif +#if defined(LUA) + +static void processConfigPacket(const uint8_t * packet, uint8_t len) +{ + // Multi_Buffer[0..3]=="Conf" -> Lua script is running + // Multi_Buffer[4]==0x01 -> TX to Module data ready to be sent + // Multi_Buffer[4]==0xFF -> Clear buffer data + // Multi_Buffer[5..11]=7 bytes of TX to Module data + // Multi_Buffer[12] -> Current page + // Multi_Buffer[13..172]=8*20=160 bytes of Module to TX data + if (Multi_Buffer && memcmp(Multi_Buffer, "Conf", 4) == 0) { + // HoTT Lua script is running + if (Multi_Buffer[4] == 0xFF) { + // Init + memset(&Multi_Buffer[4], 0x00, 1 + 7 + 1 + 160); // Clear the buffer + } + if ((packet[0] >> 4) != Multi_Buffer[12]) {// page change + memset(&Multi_Buffer[13], 0x00, 160); // Clear the buffer + Multi_Buffer[12] = (packet[0] >> 4); //Save the page number + } + memcpy(&Multi_Buffer[13 + (packet[0] & 0x0F) * 20], &packet[1], 20); // Store the received page in the buffer + } +} +#endif + static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) { uint8_t type = packet[0]; @@ -363,9 +392,18 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) if (len > 6) processMLinkPacket(data); else - TRACE("[MP] Received M-Link telemetry len %d < 6", len); + TRACE("[MP] Received M-Link telemetry len %d <= 6", len); break; +#if defined(LUA) + case ConfigTelemetry: + if (len >= 21) + processConfigPacket(data, len); + else + TRACE("[MP] Received Config telemetry len %d < 20", len); + break; +#endif + case FrSkyHubTelemetry: if (len >= 4) frskyDProcessPacket(data); diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 9fdc6c749..34c06aeeb 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -400,7 +400,7 @@ ModuleSyncStatus::ModuleSyncStatus() memset(this, 0, sizeof(ModuleSyncStatus)); } -void ModuleSyncStatus::update(uint16_t newRefreshRate, uint16_t newInputLag) +void ModuleSyncStatus::update(uint16_t newRefreshRate, int16_t newInputLag) { if (!newRefreshRate) return; @@ -451,13 +451,12 @@ void ModuleSyncStatus::getRefreshString(char * statusText) char * tmp = statusText; #if defined(DEBUG) *tmp++ = 'L'; - tmp = strAppendUnsigned(tmp, inputLag, 5); - tmp = strAppend(tmp, "us R "); - tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000), 5); - tmp = strAppend(tmp, "us"); + tmp = strAppendSigned(tmp, inputLag, 5); + tmp = strAppend(tmp, "R"); + tmp = strAppendUnsigned(tmp, refreshRate, 5); #else - tmp = strAppend(tmp, "Sync at "); - tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000000)); - tmp = strAppend(tmp, " ms"); + tmp = strAppend(tmp, "Sync "); + tmp = strAppendUnsigned(tmp, refreshRate); #endif + tmp = strAppend(tmp, "us"); } diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index f2a3fc98a..e7063a0e7 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -120,6 +120,7 @@ extern uint8_t telemetryProtocol; || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKYX2)) #define IS_R9_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY_R9) #define IS_HOTT_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_HOTT) + #define IS_CONFIG_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_CONFIG) #define IS_DSM_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) #define IS_RX_MULTI(module) ((g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_AFHDS2A_RX) || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKYX_RX) \ || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_BAYANG_RX) || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM_RX)) @@ -132,6 +133,7 @@ extern uint8_t telemetryProtocol; #define IS_D16_MULTI(module) false #define IS_R9_MULTI(module) false #define IS_HOTT_MULTI(module) false + #define IS_CONFIG_MULTI(module) false #define IS_DSM_MULTI(module) false #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) #define IS_RX_MULTI(module) false @@ -308,7 +310,7 @@ struct ModuleSyncStatus } // Set feedback from RF module - void update(uint16_t newRefreshRate, uint16_t newInputLag); + void update(uint16_t newRefreshRate, int16_t newInputLag); // Get computed settings for scheduler uint16_t getAdjustedRefreshRate(); diff --git a/radio/src/tests/gtests.cpp b/radio/src/tests/gtests.cpp index 2deb948ea..53022b8b9 100644 --- a/radio/src/tests/gtests.cpp +++ b/radio/src/tests/gtests.cpp @@ -163,7 +163,9 @@ int main(int argc, char **argv) #if defined(EEPROM_SIZE) eeprom = (uint8_t *)malloc(EEPROM_SIZE); #endif +#if !defined(COLORLCD) menuLevel = 0; +#endif InitGoogleTest(&argc, argv); // use --verbose option to revert to gtest's default output format diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index bd617eab1..867828f1c 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -90,6 +90,23 @@ bool checkScreenshot_colorlcd(const BitmapBuffer* dc, const char* test) return true; } +TEST(Lcd_colorlcd, lines) +{ + BitmapBuffer dc(BMP_RGB565, LCD_W, LCD_H); + dc.clear(DEFAULT_BGCOLOR); + dc.setClippingRect(50, 400, 50, 230); + + for (int x = 10; x <= 100; x += 10) { + dc.drawLine(x, x, x + 200, x + 50, 0xF0, DEFAULT_COLOR); + } + + for (int x = 200; x >= 100; x -= 10) { + dc.drawLine(x, x, x - 50, x + 100, 0xF0, DEFAULT_COLOR); + } + + dc.clearClippingRect(); + EXPECT_TRUE(checkScreenshot_colorlcd(&dc, "lines")); +} TEST(Lcd_colorlcd, vline) { diff --git a/radio/src/tests/lines_480x272.png b/radio/src/tests/lines_480x272.png new file mode 100644 index 000000000..86c6ceb11 Binary files /dev/null and b/radio/src/tests/lines_480x272.png differ diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 1bc7de6c4..c072dca43 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 1bc7de6c42c0a9e2284b55ae551a4d1245241bdb +Subproject commit c072dca43c232ab23c95035ec7b879cf84a64810 diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 663e1c5bb..05c129587 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -188,6 +188,7 @@ const char STR_MEMORYWARNING[] = TR_MEMORYWARNING; const char STR_ALARMWARNING[] = TR_ALARMWARNING; const char STR_RSSI_SHUTDOWN_ALARM[] = TR_RSSI_SHUTDOWN_ALARM; const char STR_MODEL_STILL_POWERED[] = TR_MODEL_STILL_POWERED; +const char STR_USB_STILL_CONNECTED[] = TR_USB_STILL_CONNECTED; const char STR_MODEL_SHUTDOWN[] = TR_MODEL_SHUTDOWN; const char STR_PRESS_ENTER_TO_CONFIRM[] = TR_PRESS_ENTER_TO_CONFIRM; const char STR_THROTTLEREVERSE[] = TR_THROTTLEREVERSE; @@ -965,3 +966,4 @@ const char STR_ABOUT_PARENTS_4[] = TR_ABOUT_PARENTS_4; const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; const char STR_PROTOCOL[] = TR_PROTOCOL; +const char STR_RACING_MODE[] = TR_RACING_MODE; diff --git a/radio/src/translations.h b/radio/src/translations.h index fa6f69e5c..0de4687ae 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -309,6 +309,7 @@ extern const char STR_MEMORYWARNING[]; extern const char STR_ALARMWARNING[]; extern const char STR_RSSI_SHUTDOWN_ALARM[]; extern const char STR_MODEL_STILL_POWERED[]; +extern const char STR_USB_STILL_CONNECTED[]; extern const char STR_MODEL_SHUTDOWN[]; extern const char STR_PRESS_ENTER_TO_CONFIRM[]; extern const char STR_THROTTLEREVERSE[]; @@ -1102,6 +1103,7 @@ extern const char STR_ABOUT_PARENTS_4[]; extern const char STR_AUTH_FAILURE[]; extern const char STR_PROTOCOL[]; +extern const char STR_RACING_MODE[]; #define CHR_HOUR TR_CHR_HOUR #define CHR_INPUT TR_CHR_INPUT diff --git a/radio/src/translations/cn.h.txt b/radio/src/translations/cn.h.txt index 337592c68..ca945f4c8 100644 --- a/radio/src/translations/cn.h.txt +++ b/radio/src/translations/cn.h.txt @@ -199,9 +199,10 @@ #endif #define TR_SF_SCREENSHOT "截屏\0 " +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[保留]\0 " -#define TR_VFSWFUNC TR_SF_SAFETY "教练\0 " "摇杆值存储到微调" "复位\0 " "设置\0 " TR_ADJUST_GVAR "音量\0 " "设置失控保护\0 " "测距模式\0 " "模块对频\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "背光\0 " TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "教练\0 " "摇杆值存储到微调" "复位\0 " "设置\0 " TR_ADJUST_GVAR "音量\0 " "设置失控保护\0 " "测距模式\0 " "模块对频\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "背光\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\010") @@ -450,6 +451,7 @@ #define TR_ALARMWARNING "静音" #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi Shutdown", "关机时检查RSSI") #define TR_MODEL_STILL_POWERED "模型未断电" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "关机 ?" #define TR_PRESS_ENTER_TO_CONFIRM "按ENTER键确认" #define TR_THROTTLE_LABEL "油门" @@ -1155,6 +1157,7 @@ #define TR_JITTER_FILTER "类比输入滤波" #define TR_RTC_CHECK TR("Check RTC", "检查时间驱动电池电压") #define TR_AUTH_FAILURE "验证失败" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index c235a77b5..a866cb937 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -234,9 +234,10 @@ #endif #define TR_SF_SCREENSHOT "Snímek LCD\0" +#define TR_SF_RACING_MODE "RacingMode\0" #define TR_SF_RESERVE "[rezerva]\0 " -#define TR_VFSWFUNC TR_SF_SAFETY "Trenér \0 ""Insta-Trim\0""Reset\0 ""Změna \0 " TR_ADJUST_GVAR "Hlasitost\0 " "SetFailsafe" "RangeCheck\0" "ModuleBind\0" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podsvětlení" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trenér \0 ""Insta-Trim\0""Reset\0 ""Změna \0 " TR_ADJUST_GVAR "Hlasitost\0 " "SetFailsafe" "RangeCheck\0" "ModuleBind\0" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podsvětlení" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") @@ -535,6 +536,7 @@ #define TR_ALARMWARNING TR3("Vypnutý zvuk", "Upozornit na vypnutý zvuk", "Upozornit na vyp. zvuk") #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi při vyp.", "Hlídat Rssi při vypnutí") #define TR_MODEL_STILL_POWERED "Model stále spuštěn" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Vypnout ?" #define TR_PRESS_ENTER_TO_CONFIRM "Zmáčkni enter pro potvrzení" #define TR_THROTTLE_LABEL "Plyn" @@ -1258,6 +1260,7 @@ #define TR_JITTER_FILTER "ADC Filtr" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 901f1a110..a6bd8d297 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -237,9 +237,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[Reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Lehrer \0 ""Inst. Trim""Rücksetz.\0""Setze \0 " TR_ADJUST_GVAR "Lautstr.\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "LCD Licht\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Lehrer \0 ""Inst. Trim""Rücksetz.\0""Setze \0 " TR_ADJUST_GVAR "Lautstr.\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "LCD Licht\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -541,6 +542,7 @@ #define TR_RSSI_SHUTDOWN_ALARM TR(INDENT "RSSI-Chk aus?", INDENT "Prüfe RSSI bei Ausschalten") #define TR_FLYSKY_TELEMETRY TR("FlySky RSSI #", "Use FlySky RSSI value without rescalling") #define TR_MODEL_STILL_POWERED "Model still powered" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown ?" #define TR_PRESS_ENTER_TO_CONFIRM "Press enter to confirm" #define TR_THROTTLE_LABEL "Gas-Kontrolle" @@ -1267,6 +1269,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" // ---------------------------------------------------------------- #define ZSTR_VFR "VFR" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 084dba46c..9f02dc4e0 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -236,10 +236,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Screenshot" -#define TR_SF_RESERVE "[reserve]\0" +#define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" +#define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -539,6 +540,7 @@ #define TR_ALARMWARNING "Sound off" #define TR_RSSI_SHUTDOWN_ALARM TR("RSSI shutdown", "Check RSSI on shutdown") #define TR_MODEL_STILL_POWERED "Model still powered" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown?" #define TR_PRESS_ENTER_TO_CONFIRM "Press enter to confirm" #define TR_THROTTLE_LABEL "Throttle" @@ -1272,6 +1274,7 @@ #define TR_JITTER_FILTER "ADC filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 34065eeb4..b0d630ff0 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -234,10 +234,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Captura" +#define TR_SF_SCREENSHOT "Captura\0 " +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserv.]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Entrenador""Inst. Trim""Reset\0 ""Ajuste \0 " TR_ADJUST_GVAR "Volumen\0 " "Failsafe\0 " "CheckRango" "Enl.módulo" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Luz Fondo\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Entrenador""Inst. Trim""Reset\0 ""Ajuste \0 " TR_ADJUST_GVAR "Volumen\0 " "Failsafe\0 " "CheckRango" "Enl.módulo" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Luz Fondo\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") #define TR_FSW_RESET_TELEM TR("Telm", "Telemetría") @@ -538,6 +539,7 @@ #define TR_ALARMWARNING "Sin sonido" #define TR_RSSI_SHUTDOWN_ALARM TR("Apagado Rssi", "Check Rssi al apagar") #define TR_MODEL_STILL_POWERED "Modelo aun encendido" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Apagar ?" #define TR_PRESS_ENTER_TO_CONFIRM "Enter para confirmar" #define TR_THROTTLE_LABEL "Potencia" @@ -1266,6 +1268,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltaje") #define TR_AUTH_FAILURE "Fallo " LCDW_128_480_LINEBREAK "autentificación" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index ab2d5bafe..db6a8287d 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -238,9 +238,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -552,6 +553,7 @@ #define TR_ALARMWARNING "Sound Off" #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi Shutdown", "Check Rssi on Shutdown") #define TR_MODEL_STILL_POWERED "Model still powered" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown ?" #define TR_PRESS_ENTER_TO_CONFIRM "Press enter to confirm" #define TR_THROTTLE_LABEL "Throttle" @@ -1273,6 +1275,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 2d4626477..96eccac8f 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -240,9 +240,10 @@ #endif #define TR_SF_SCREENSHOT "Photo Ecran\0 " +#define TR_SF_RACING_MODE "Racing Mode\0 " #define TR_SF_RESERVE "[reserve]\0 " - #define TR_VFSWFUNC TR_SF_SAFETY "Ecolage\0 ""Trim instant.""Remise à 0\0 ""Déf.\0 " TR_ADJUST_GVAR "Volume\0 " "DéfFailsafe\0 " "Test Port.\0 " "Bind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Rétroécl.\0 " TR_SF_SCREENSHOT TR_SF_TEST + #define TR_VFSWFUNC TR_SF_SAFETY "Ecolage\0 ""Trim instant.""Remise à 0\0 ""Déf.\0 " TR_ADJUST_GVAR "Volume\0 " "DéfFailsafe\0 " "Test Port.\0 " "Bind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Rétroécl.\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") @@ -559,6 +560,7 @@ #define TR_RSSI_SHUTDOWN_ALARM TR("RSSI extinct.", "Vérif RSSI à l'extinction") #define TR_FLYSKY_TELEMETRY TR("FlySky RSSI #", "Use FlySky RSSI value without rescalling") #define TR_MODEL_STILL_POWERED TR("Modèle allumé", "Modèle encore allumé") +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Eteindre ?" #define TR_PRESS_ENTER_TO_CONFIRM "Presser [Enter] pour confirmer" #define TR_THROTTLE_LABEL "Gaz" @@ -1297,6 +1299,7 @@ #define TR_JITTER_FILTER "Filtre ADC" #define TR_RTC_CHECK TR("Vérif. RTC", "Vérif. pile RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c13fd5dba..f6f4adac2 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -244,9 +244,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot\0 " +#define TR_SF_RACING_MODE "Racing Mode\0 " #define TR_SF_RESERVE "[riserva] \0" -#define TR_VFSWFUNC TR_SF_SAFETY "Maestro \0 ""Trim Instant.""Azzera\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Retroillum.\0 " TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Maestro \0 ""Trim Instant.""Azzera\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Retroillum.\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -563,6 +564,7 @@ #define TR_ALARMWARNING INDENT "Spegni suono" #define TR_RSSI_SHUTDOWN_ALARM TR(INDENT "RSSI Shut.", INDENT "Control. RSSI al Shutdown") #define TR_MODEL_STILL_POWERED "Ricevente ancora connessa" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown?" #define TR_PRESS_ENTER_TO_CONFIRM "Premi [ENT] per confermare" #define TR_THROTTLE_LABEL "Motore" @@ -1286,6 +1288,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Controllo RTC", "Controllo volt. RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index aa03b5f81..fc39d9eb2 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -237,10 +237,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Schermafdruk" +#define TR_SF_SCREENSHOT "Schermafdr" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") @@ -540,6 +541,7 @@ #define TR_ALARMWARNING TR("Al Geluid uit?", "Al het geluid uit?") #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi Shutdown", "Check Rssi on Shutdown") #define TR_MODEL_STILL_POWERED "Model still powered" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown ?" #define TR_PRESS_ENTER_TO_CONFIRM "Press enter to confirm" #define TR_THROTTLE_LABEL "Gas" @@ -1273,6 +1275,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 8e86bca94..933ad87ee 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -238,9 +238,10 @@ #endif #define TR_SF_SCREENSHOT "Zrzut Ekra" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[rezerwa]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trener \0 ""Inst-Trim ""Resetuj\0 ""Ustaw\0 " TR_ADJUST_GVAR "Głośność\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podświetl\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trener \0 ""Inst-Trim ""Resetuj\0 ""Ustaw\0 " TR_ADJUST_GVAR "Głośność\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podświetl\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") /*9 decimal*/ @@ -559,6 +560,7 @@ #define TR_ALARMWARNING "Wyłącz dźwięk" #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi Shutdown", "Check Rssi on Shutdown") #define TR_MODEL_STILL_POWERED "Model still powered" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown ?" #define TR_PRESS_ENTER_TO_CONFIRM "Press enter to confirm" #define TR_THROTTLE_LABEL "Gaz " @@ -1282,6 +1284,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 0909fbd6a..abd610af5 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -234,9 +234,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0 " -#define TR_VFSWFUNC TR_SF_SAFETY "Aprendiz\0 ""Ajuste Rapido""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0 " TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Aprendiz\0 ""Ajuste Rapido""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -556,6 +557,7 @@ #define TR_ALARMWARNING "Som Off" #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi Shutdown", "Check Rssi on Shutdown") #define TR_MODEL_STILL_POWERED "Model still powered" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown ?" #define TR_PRESS_ENTER_TO_CONFIRM "Press enter to confirm" #define TR_THROTTLE_LABEL "Throttle" @@ -1282,7 +1284,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" - +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" #define ZSTR_R9PW "R9PW" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 417d4f7df..abf63efbe 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -237,10 +237,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Skärmbild" +#define TR_SF_SCREENSHOT "Skärmbild\0" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Sätt Trim\0""Nollställ\0""Sätt\0 " TR_ADJUST_GVAR "Volym\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Belysning\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Sätt Trim\0""Nollställ\0""Sätt\0 " TR_ADJUST_GVAR "Volym\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Belysning\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -561,6 +562,7 @@ #define TR_ALARMWARNING "Ljud Av" #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi Shutdown", "Check Rssi on Shutdown") #define TR_MODEL_STILL_POWERED "Model still powered" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "Shutdown ?" #define TR_PRESS_ENTER_TO_CONFIRM "Press enter to confirm" #define TR_THROTTLE_LABEL "Gas" @@ -1285,6 +1287,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/tw.h.txt b/radio/src/translations/tw.h.txt index efd6ad04e..ea30246bd 100644 --- a/radio/src/translations/tw.h.txt +++ b/radio/src/translations/tw.h.txt @@ -199,9 +199,10 @@ #endif #define TR_SF_SCREENSHOT "截屏\0 " +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[保留]\0 " -#define TR_VFSWFUNC TR_SF_SAFETY "教練\0 " "搖桿值存儲到微調" "重啟\0 " "設置\0 " TR_ADJUST_GVAR "音量\0 " "設置失控保護\0 " "測距模式\0 " "模塊對頻\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "背光\0 " TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "教練\0 " "搖桿值存儲到微調" "重啟\0 " "設置\0 " TR_ADJUST_GVAR "音量\0 " "設置失控保護\0 " "測距模式\0 " "模塊對頻\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "背光\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\010") @@ -450,6 +451,7 @@ #define TR_ALARMWARNING "靜音" #define TR_RSSI_SHUTDOWN_ALARM TR("Rssi Shutdown", "關機時檢查RSSI") #define TR_MODEL_STILL_POWERED "模型未斷電" +#define TR_USB_STILL_CONNECTED "USB still connected" #define TR_MODEL_SHUTDOWN "關機 ?" #define TR_PRESS_ENTER_TO_CONFIRM "按ENTER鍵確認" #define TR_THROTTLE_LABEL "油門" @@ -1163,6 +1165,7 @@ #define TR_JITTER_FILTER "類比輸入濾波" #define TR_RTC_CHECK TR("Check RTC", "檢查時間驅動電池電壓") #define TR_AUTH_FAILURE "驗證失敗" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" @@ -1321,4 +1324,4 @@ #define ZSTR_SERVO_STATUS "SrvS" #define ZSTR_LOSS "Loss" #define ZSTR_SPEED "Spd " -#define ZSTR_FLOW "Flow" \ No newline at end of file +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index b98d5b70e..124b1a8a1 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -155,7 +155,7 @@ #define TR_DSM_PROTOCOLS "LP45""DSM2""DSMX" #define LEN_MULTI_PROTOCOLS "\007" -#define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""Futaba\0""J6 Pro\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FlSky2A""Q2x2\0 ""Walkera""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esky150""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMini""Traxxas""NCC1701""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpine""Potensi""ZSX\0 ""Height\0""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyartc""ESky-v2""DSM RX\0""JJRC345""Q90C\0 ""Kyosho\0""RadLink""ExpLRS\0""Realacc""OMP\0 ""M-Link\0""Wfly 2\0""E016Hv2" +#define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""Futaba\0""J6 Pro\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FlSky2A""Q2x2\0 ""Walkera""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esky150""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMini""Traxxas""NCC1701""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpine""Potensi""ZSX\0 ""Height\0""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyartc""ESky-v2""DSM RX\0""JJRC345""Q90C\0 ""Kyosho\0""RadLink""ExpLRS\0""Realacc""OMP\0 ""M-Link\0""Wfly 2\0""E016Hv2""E010r5 ""LOLI\0 ""E129\0 ""JOYSWAY""E016H\0 ""Config\0" #define LEN_MULTI_POWER "\005" #define TR_MULTI_POWER "1.6mW""2.0mW""2.5mW""3.2mW""4.0mW""5.0mW""6.3mW""7.9mW""10mW\0""13mW\0""16mW\0""20mW\0""25mW\0""32mW\0""40mW\0""50mW\0" diff --git a/tools/build-companion-nightly.sh b/tools/build-companion-nightly.sh index a0406f691..8608ac6fd 100755 --- a/tools/build-companion-nightly.sh +++ b/tools/build-companion-nightly.sh @@ -4,7 +4,12 @@ set -e set -x -JOBS=3 +if [ "$(uname)" = "Darwin" ]; then + num_cpus=$(sysctl -n hw.ncpu) + : "${JOBS:=$num_cpus}" +else + JOBS=3 +fi while [ $# -gt 0 ] do @@ -32,6 +37,13 @@ fi if [ "$3" != "" ]; then COMMON_OPTIONS="${COMMON_OPTIONS} -DVERSION_SUFFIX=$3" +else + wget https://downloads.open-tx.org/2.3/nightlies/companion/companion-linux.stamp + version=$(cut -d'N' -f 3 < companion-linux.stamp) + version=$(echo $version | tr -cd '[[:digit:]]') + version=$((version+1)) + nightly=N"$version" + COMMON_OPTIONS="${COMMON_OPTIONS} -DVERSION_SUFFIX=$nightly" fi rm -rf build @@ -74,6 +86,14 @@ cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TX12 ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=T8 ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TLITE ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + cmake ${COMMON_OPTIONS} -DPCB=X9D ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt diff --git a/tools/build-companion-release.sh b/tools/build-companion-release.sh index 463645dc2..e8214977f 100755 --- a/tools/build-companion-release.sh +++ b/tools/build-companion-release.sh @@ -4,7 +4,12 @@ set -e set -x -JOBS=2 +if [ "$(uname)" = "Darwin" ]; then + num_cpus=$(sysctl -n hw.ncpu) + : "${JOBS:=$num_cpus}" +else + JOBS=3 +fi while [ $# -gt 0 ] do @@ -74,6 +79,14 @@ cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TX12 ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=T8 ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TLITE ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + cmake ${COMMON_OPTIONS} -DPCB=X9D ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt diff --git a/tools/build-font-bitmap.py b/tools/build-font-bitmap.py index d3176834e..336445dc7 100755 --- a/tools/build-font-bitmap.py +++ b/tools/build-font-bitmap.py @@ -123,6 +123,7 @@ class FontBitmap: image = image.crop((0, top, width - 1, bottom)) coords.insert(0, bottom - top + 1) + image = image.convert("L") image.save(filename + ".png") if generate_coords_file: with open(filename + ".specs", "w") as f: