diff --git a/radio/src/gui/menu_model.cpp b/radio/src/gui/menu_model.cpp index d1f9889fb..681aaa22f 100644 --- a/radio/src/gui/menu_model.cpp +++ b/radio/src/gui/menu_model.cpp @@ -5164,6 +5164,8 @@ enum menuModelTelemetryItems { ITEM_TELEMETRY_USR_PROTO, #endif ITEM_TELEMETRY_USR_BLADES, + ITEM_TELEMETRY_USR_SPUR_GEAR, // T.Foley + ITEM_TELEMETRY_USR_PINION_GEAR, // T.Foley #endif ITEM_TELEMETRY_USR_VOLTAGE_SOURCE, ITEM_TELEMETRY_USR_CURRENT_SOURCE, @@ -5200,7 +5202,8 @@ enum menuModelTelemetryItems { }; #if defined(PCBTARANIS) - #define USRDATA_LINES (uint8_t)-1, 0, + //#define USRDATA_LINES (uint8_t)-1, 0, + #define USRDATA_LINES (uint8_t)-1, 0, 0, 0, // T.Foley - Add room for two new gearing options #elif defined(FRSKY_HUB) || defined(WS_HOW_HIGH) #define USRDATA_LINES (uint8_t)-1, 0, 0, #else @@ -5403,8 +5406,24 @@ void menuModelTelemetry(uint8_t event) case ITEM_TELEMETRY_USR_BLADES: lcd_putsLeft(y, STR_BLADES); - lcd_outdezAtt(TELEM_COL2+FWNUM, y, 2+g_model.frsky.blades, attr); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.blades, 3); + //lcd_outdezAtt(TELEM_COL2+FWNUM, y, 2+g_model.frsky.blades, attr); + lcd_outdezAtt(TELEM_COL2, y, 1+g_model.frsky.blades, LEFT|attr); // T.Foley + //if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.blades, 3); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.blades, 254); // T.Foley + break; + + case ITEM_TELEMETRY_USR_SPUR_GEAR: // T.Foley + lcd_putsLeft(y, STR_SPURGEAR); + lcd_outdezAtt(TELEM_COL2, y, 1+g_model.frsky.spur_gear, LEFT|attr); + // Spur gear must be greater than or equal to pinion to prevent floating point error + if (attr) CHECK_INCDEC_MODELVAR(event, g_model.frsky.spur_gear, g_model.frsky.pinion_gear, 254); + break; + + case ITEM_TELEMETRY_USR_PINION_GEAR: // T.Foley + lcd_putsLeft(y, STR_PINIONGEAR); + lcd_outdezAtt(TELEM_COL2, y, 1+g_model.frsky.pinion_gear, LEFT|attr); + // Pinion gear ,ust be less than or equal to spur gear to prevent floating point error + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.pinion_gear, g_model.frsky.spur_gear); break; #endif diff --git a/radio/src/gui/menu_model.cpp~ b/radio/src/gui/menu_model.cpp~ new file mode 100644 index 000000000..d1f9889fb --- /dev/null +++ b/radio/src/gui/menu_model.cpp~ @@ -0,0 +1,5643 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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" +#ifdef MAVLINK +#include "gui/view_mavlink.h" +#endif + +#define WCHART 32 +#define X0 (LCD_W-WCHART-2) +#define Y0 32 + +enum EnumTabModel { + e_ModelSelect, + e_ModelSetup, + IF_HELI(e_Heli) + IF_FLIGHT_MODES(e_FlightModesAll) + e_InputsAll, + e_MixAll, + e_Limits, + IF_CURVES(e_CurvesAll) +#if LCD_W >= 212 + IF_GVARS(e_GVars) +#endif + e_CustomSwitches, + e_CustomFunctions, +#if defined(LUA_MODEL_SCRIPTS) + e_CustomScripts, +#endif + IF_FRSKY(e_Telemetry) + IF_MAVLINK(e_MavSetup) + IF_TEMPLATES(e_Templates) +}; + +void menuModelSelect(uint8_t event); +void menuModelSetup(uint8_t event); +void menuModelHeli(uint8_t event); +void menuModelFlightModesAll(uint8_t event); +void menuModelExposAll(uint8_t event); +void menuModelMixAll(uint8_t event); +void menuModelLimits(uint8_t event); +void menuModelCurvesAll(uint8_t event); +void menuModelGVars(uint8_t event); +void menuModelCustomSwitches(uint8_t event); +void menuModelCustomFunctions(uint8_t event); +void menuModelCustomScripts(uint8_t event); +void menuModelTelemetry(uint8_t event); +void menuModelTemplates(uint8_t event); +void menuModelExpoOne(uint8_t event); + +const MenuFuncP_PROGMEM menuTabModel[] PROGMEM = { + menuModelSelect, + menuModelSetup, + IF_HELI(menuModelHeli) + IF_FLIGHT_MODES(menuModelFlightModesAll) + menuModelExposAll, + menuModelMixAll, + menuModelLimits, + IF_CURVES(menuModelCurvesAll) +#if LCD_W >= 212 && defined(GVARS) && defined(FLIGHT_MODES) + IF_GVARS(menuModelGVars) +#endif + menuModelCustomSwitches, + menuModelCustomFunctions, +#if defined(LUA_MODEL_SCRIPTS) + menuModelCustomScripts, +#endif + IF_FRSKY(menuModelTelemetry) + IF_MAVLINK(menuTelemetryMavlinkSetup) + IF_TEMPLATES(menuModelTemplates) +}; + +#define COPY_MODE 1 +#define MOVE_MODE 2 +static uint8_t s_copyMode = 0; +static int8_t s_copySrcRow; +static int8_t s_copyTgtOfs; + +uint8_t eeFindEmptyModel(uint8_t id, bool down) +{ + uint8_t i = id; + for (;;) { + i = (MAX_MODELS + (down ? i+1 : i-1)) % MAX_MODELS; + if (!eeModelExists(i)) break; + if (i == id) return 0xff; // no free space in directory left + } + return i; +} + +void selectModel(uint8_t sub) +{ + displayPopup(STR_LOADINGMODEL); + saveTimers(); + eeCheck(true); // force writing of current model data before this is changed + g_eeGeneral.currModel = sub; + eeDirty(EE_GENERAL); + eeLoadModel(sub); +} + +#if defined(SDCARD) +#define LIST_NONE_SD_FILE 1 +bool listSdFiles(const char *path, const char *extension, const uint8_t maxlen, const char *selection, uint8_t flags=0) +{ + FILINFO fno; + DIR dir; + char *fn; /* This function is assuming non-Unicode cfg. */ +#if _USE_LFN + TCHAR lfn[_MAX_LFN + 1]; + fno.lfname = lfn; + fno.lfsize = sizeof(lfn); +#endif + + static uint16_t s_last_menu_offset = 0; + +#if defined(CPUARM) + static uint8_t s_last_flags; + + if (selection) { + s_last_flags = flags; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + strcpy(reusableBuffer.modelsel.menu_bss[0], path); + strcat(reusableBuffer.modelsel.menu_bss[0], "/"); + strncat(reusableBuffer.modelsel.menu_bss[0], selection, maxlen); + strcat(reusableBuffer.modelsel.menu_bss[0], extension); + if (f_stat(reusableBuffer.modelsel.menu_bss[0], &fno) != FR_OK) { + selection = NULL; + } + } + else { + flags = s_last_flags; + } +#endif + + if (s_menu_offset == 0) { + s_last_menu_offset = 0; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + } + else if (s_menu_offset == s_menu_count - MENU_MAX_LINES) { + s_last_menu_offset = 0xffff; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + } + else if (s_menu_offset == s_last_menu_offset) { + // should not happen, only there because of Murphy's law + return true; + } + else if (s_menu_offset > s_last_menu_offset) { + memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], (MENU_MAX_LINES-1)*MENU_LINE_LENGTH); + memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], 0xff, MENU_LINE_LENGTH); + } + else { + memmove(reusableBuffer.modelsel.menu_bss[1], reusableBuffer.modelsel.menu_bss[0], (MENU_MAX_LINES-1)*MENU_LINE_LENGTH); + memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); + } + + s_menu_count = 0; + s_menu_flags = BSS; + + FRESULT res = f_opendir(&dir, path); /* Open the directory */ + if (res == FR_OK) { + + if (flags & LIST_NONE_SD_FILE) { + s_menu_count++; + if (selection) { + s_last_menu_offset++; + } + else if (s_menu_offset==0 || s_menu_offset < s_last_menu_offset) { + char *line = reusableBuffer.modelsel.menu_bss[0]; + memset(line, 0, MENU_LINE_LENGTH); + strcpy(line, "---"); + s_menu[0] = line; + } + } + + for (;;) { + res = f_readdir(&dir, &fno); /* Read a directory item */ + if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + +#if _USE_LFN + fn = *fno.lfname ? fno.lfname : fno.fname; +#else + fn = fno.fname; +#endif + + uint8_t len = strlen(fn); + if (len < 5 || len > maxlen+4 || strcasecmp(fn+len-4, extension) || (fno.fattrib & AM_DIR)) continue; + + s_menu_count++; + fn[len-4] = '\0'; + + if (s_menu_offset == 0) { + if (selection && strncasecmp(fn, selection, maxlen) < 0) { + s_last_menu_offset++; + } + else { + for (uint8_t i=0; i=0; i--) { + char *line = reusableBuffer.modelsel.menu_bss[i]; + if (line[0] == '\0' || strcasecmp(fn, line) > 0) { + if (i > 0) memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], sizeof(reusableBuffer.modelsel.menu_bss[i]) * i); + memset(line, 0, MENU_LINE_LENGTH); + strcpy(line, fn); + break; + } + } + for (uint8_t i=0; i s_last_menu_offset) { + if (strcasecmp(fn, reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-2]) > 0 && strcasecmp(fn, reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1]) < 0) { + memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], 0, MENU_LINE_LENGTH); + strcpy(reusableBuffer.modelsel.menu_bss[MENU_MAX_LINES-1], fn); + } + } + else { + if (strcasecmp(fn, reusableBuffer.modelsel.menu_bss[1]) < 0 && strcasecmp(fn, reusableBuffer.modelsel.menu_bss[0]) > 0) { + memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); + strcpy(reusableBuffer.modelsel.menu_bss[0], fn); + } + } + } + } + + if (s_menu_offset > 0) + s_last_menu_offset = s_menu_offset; + else + s_menu_offset = s_last_menu_offset; + + return s_menu_count; +} +#endif + +#if defined(PCBTARANIS) + static int8_t modelselBitmapIdx; + static uint8_t modelselBitmap[MODEL_BITMAP_SIZE]; + #define MODELSEL_W 133 + #define BMP_DIRTY() modelselBitmapIdx = -1 +#else + #define MODELSEL_W LCD_W + #define BMP_DIRTY() +#endif + +#if defined(NAVIGATION_MENUS) +void onModelSelectMenu(const char *result) +{ + int8_t sub = m_posVert; + + if (result == STR_SELECT_MODEL || result == STR_CREATE_MODEL) { + selectModel(sub); + } + else if (result == STR_COPY_MODEL) { + s_copyMode = COPY_MODE; + s_copyTgtOfs = 0; + s_copySrcRow = -1; + } + else if (result == STR_MOVE_MODEL) { + s_copyMode = MOVE_MODE; + s_copyTgtOfs = 0; + s_copySrcRow = -1; + } +#if defined(SDCARD) + else if (result == STR_BACKUP_MODEL) { + eeCheck(true); // force writing of current model data before this is changed + POPUP_WARNING(eeBackupModel(sub)); + } + else if (result == STR_RESTORE_MODEL || result == STR_UPDATE_LIST) { + if (!listSdFiles(MODELS_PATH, MODELS_EXT, sizeof(g_model.header.name), NULL)) { + POPUP_WARNING(STR_NO_MODELS_ON_SD); + s_menu_flags = 0; + } + } +#endif + else if (result == STR_DELETE_MODEL) { + POPUP_CONFIRMATION(STR_DELETEMODEL); +#if defined(CPUARM) + s_warning_info = modelHeaders[sub].name; +#else + char * name = reusableBuffer.modelsel.mainname; + eeLoadModelName(sub, name); + s_warning_info = name; +#endif + s_warning_info_len = sizeof(g_model.header.name); + } +#if defined(SDCARD) + else { + // The user choosed a file on SD to restore + POPUP_WARNING(eeRestoreModel(sub, (char *)result)); + BMP_DIRTY(); + if (!s_warning && g_eeGeneral.currModel == sub) + eeLoadModel(sub); + } +#endif +} +#endif + +void menuModelSelect(uint8_t event) +{ + if (s_warning_result) { + s_warning_result = 0; + eeDeleteModel(m_posVert); // delete file + s_copyMode = 0; + event = EVT_ENTRY_UP; + BMP_DIRTY(); + } + + uint8_t _event_ = (IS_ROTARY_BREAK(event) || IS_ROTARY_LONG(event) ? 0 : event); + +#if defined(PCBTARANIS) + if ((s_copyMode && EVT_KEY_MASK(event) == KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) + _event_ -= KEY_EXIT; +#else + if (s_copyMode && EVT_KEY_MASK(event) == KEY_EXIT) + _event_ -= KEY_EXIT; +#endif + + int8_t oldSub = m_posVert; + + if (!check_submenu_simple(_event_, MAX_MODELS-1)) return; + +#if defined(NAVIGATION_POT2) + if (event==0 && p2valdiff<0) { + event = EVT_KEY_FIRST(KEY_RIGHT); + } +#endif + + if (s_editMode > 0) s_editMode = 0; + +#if !defined(CPUARM) + if (event) { + eeFlush(); // flush eeprom write + } +#endif + + int8_t sub = m_posVert; + + switch (event) + { + case EVT_ENTRY: + m_posVert = sub = g_eeGeneral.currModel; + if (sub >= LCD_LINES-1) s_pgOfs = sub-LCD_LINES+2; + s_copyMode = 0; + s_editMode = EDIT_MODE_INIT; + BMP_DIRTY(); + eeCheck(true); + break; + case EVT_KEY_LONG(KEY_EXIT): + if (s_copyMode && s_copyTgtOfs == 0 && g_eeGeneral.currModel != sub && eeModelExists(sub)) { + POPUP_CONFIRMATION(STR_DELETEMODEL); +#if defined(CPUARM) + s_warning_info = modelHeaders[sub].name; +#else + char * name = reusableBuffer.modelsel.mainname; + eeLoadModelName(sub, name); + s_warning_info = name; +#endif + s_warning_info_len = sizeof(g_model.header.name); + killEvents(event); + break; + } + // no break +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LONG: + killEvents(event); + if (s_editMode < 0) { + popMenu(); + return; + } + else if (!s_copyMode) { + m_posVert = sub = g_eeGeneral.currModel; + s_copyMode = 0; + s_editMode = EDIT_MODE_INIT; + } + // no break +#endif + case EVT_KEY_BREAK(KEY_EXIT): + if (s_copyMode) { + sub = m_posVert = (s_copyMode == MOVE_MODE || s_copySrcRow<0) ? (MAX_MODELS+sub+s_copyTgtOfs) % MAX_MODELS : s_copySrcRow; + s_copyMode = 0; + } +#if defined(PCBTARANIS) + else { + if (m_posVert != g_eeGeneral.currModel) { + m_posVert = g_eeGeneral.currModel; + s_pgOfs = 0; + } + else { + popMenu(); + return; + } + } +#endif + break; +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_BREAK: + if (s_editMode == -1) { + s_editMode = 0; + break; + } + // no break; +#endif + case EVT_KEY_LONG(KEY_ENTER): + case EVT_KEY_BREAK(KEY_ENTER): + s_editMode = 0; + if (READ_ONLY()) { + if (g_eeGeneral.currModel != sub && eeModelExists(sub)) { + selectModel(sub); + } + } + else if (s_copyMode && (s_copyTgtOfs || s_copySrcRow>=0)) { + displayPopup(s_copyMode==COPY_MODE ? STR_COPYINGMODEL : STR_MOVINGMODEL); + eeCheck(true); // force writing of current model data before this is changed + + uint8_t cur = (MAX_MODELS + sub + s_copyTgtOfs) % MAX_MODELS; + + if (s_copyMode == COPY_MODE) { + if (eeCopyModel(cur, s_copySrcRow)) + BMP_DIRTY(); + else + cur = sub; + } + + s_copySrcRow = g_eeGeneral.currModel; // to update the currModel value + while (sub != cur) { + uint8_t src = cur; + cur = (s_copyTgtOfs > 0 ? cur+MAX_MODELS-1 : cur+1) % MAX_MODELS; + eeSwapModels(src, cur); + BMP_DIRTY(); + if (src == s_copySrcRow) + s_copySrcRow = cur; + else if (cur == s_copySrcRow) + s_copySrcRow = src; + } + + if (s_copySrcRow != g_eeGeneral.currModel) { + g_eeGeneral.currModel = s_copySrcRow; + eeDirty(EE_GENERAL); + } + + s_copyMode = 0; + event = EVT_ENTRY_UP; + } + else if (event == EVT_KEY_LONG(KEY_ENTER) +#if !defined(PCBTARANIS) + || IS_ROTARY_BREAK(event) +#endif + ) { + s_copyMode = 0; + killEvents(event); +#if defined(NAVIGATION_MENUS) + if (g_eeGeneral.currModel != sub) { + if (eeModelExists(sub)) { + MENU_ADD_ITEM(STR_SELECT_MODEL); + MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); + MENU_ADD_ITEM(STR_COPY_MODEL); + MENU_ADD_ITEM(STR_MOVE_MODEL); + MENU_ADD_ITEM(STR_DELETE_MODEL); + } + else { +#if defined(SDCARD) + MENU_ADD_ITEM(STR_CREATE_MODEL); + MENU_ADD_ITEM(STR_RESTORE_MODEL); +#else + selectModel(sub); +#endif + } + } + else { + MENU_ADD_SD_ITEM(STR_BACKUP_MODEL); + MENU_ADD_ITEM(STR_COPY_MODEL); + MENU_ADD_ITEM(STR_MOVE_MODEL); + } + menuHandler = onModelSelectMenu; +#else + if (g_eeGeneral.currModel != sub) { + selectModel(sub); + } +#endif + } + else if (eeModelExists(sub)) { + s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); + s_copyTgtOfs = 0; + s_copySrcRow = -1; + } + break; + +#if defined(PCBTARANIS) + case EVT_KEY_BREAK(KEY_PAGE): + case EVT_KEY_LONG(KEY_PAGE): + chainMenu(event == EVT_KEY_BREAK(KEY_PAGE) ? menuModelSetup : menuTabModel[DIM(menuTabModel)-1]); + killEvents(event); + return; +#else +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LEFT: + case EVT_ROTARY_RIGHT: +#endif + case EVT_KEY_FIRST(KEY_LEFT): + case EVT_KEY_FIRST(KEY_RIGHT): +#if defined(ROTARY_ENCODER_NAVIGATION) + if ((!IS_ROTARY_RIGHT(event) && !IS_ROTARY_LEFT(event)) || s_editMode < 0) { +#endif + if (sub == g_eeGeneral.currModel) { + chainMenu((IS_ROTARY_RIGHT(event) || event == EVT_KEY_FIRST(KEY_RIGHT)) ? menuModelSetup : menuTabModel[DIM(menuTabModel)-1]); + return; + } + AUDIO_WARNING2(); + break; +#if defined(ROTARY_ENCODER_NAVIGATION) + } + // no break +#endif +#endif + + case EVT_KEY_FIRST(KEY_MOVE_UP): + case EVT_KEY_REPT(KEY_MOVE_UP): + case EVT_KEY_FIRST(KEY_MOVE_DOWN): + case EVT_KEY_REPT(KEY_MOVE_DOWN): + if (s_copyMode) { + int8_t next_ofs = s_copyTgtOfs + oldSub - m_posVert; + if (next_ofs == MAX_MODELS || next_ofs == -MAX_MODELS) + next_ofs = 0; + + if (s_copySrcRow < 0 && s_copyMode==COPY_MODE) { + s_copySrcRow = oldSub; + // find a hole (in the first empty slot above / below) + sub = eeFindEmptyModel(s_copySrcRow, IS_ROTARY_DOWN(event) || event==EVT_KEY_FIRST(KEY_MOVE_DOWN)); + if (sub < 0) { + // no free room for duplicating the model + AUDIO_ERROR(); + sub = oldSub; + s_copyMode = 0; + } + next_ofs = 0; + m_posVert = sub; + } + s_copyTgtOfs = next_ofs; + } + break; + } + +#if defined(PCBTARANIS) + lcd_puts(27*FW-(LEN_FREE-4)*FW, 0, STR_FREE); + if (event) reusableBuffer.modelsel.eepromfree = EeFsGetFree(); + lcd_outdezAtt(20*FW, 0, reusableBuffer.modelsel.eepromfree, 0); + lcd_puts(21*FW, 0, STR_BYTES); +#elif !defined(PCBSKY9X) + lcd_puts(9*FW-(LEN_FREE-4)*FW, 0, STR_FREE); + if (event) reusableBuffer.modelsel.eepromfree = EeFsGetFree(); + lcd_outdezAtt(17*FW, 0, reusableBuffer.modelsel.eepromfree, 0); +#endif + +#if defined(ROTARY_ENCODER_NAVIGATION) + displayScreenIndex(e_ModelSelect, DIM(menuTabModel), (sub == g_eeGeneral.currModel) ? ((IS_RE_NAVIGATION_ENABLE() && s_editMode < 0) ? INVERS|BLINK : INVERS) : 0); +#elif defined(PCBTARANIS) + displayScreenIndex(e_ModelSelect, DIM(menuTabModel), 0); + lcd_filled_rect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT); +#else + displayScreenIndex(e_ModelSelect, DIM(menuTabModel), (sub == g_eeGeneral.currModel) ? INVERS : 0); +#endif + + TITLE(STR_MENUMODELSEL); + + for (uint8_t i=0; i= 0)) { + if (k == sub) { + if (s_copyMode == COPY_MODE) { + k = s_copySrcRow; + lcd_putc(MODELSEL_W-FW, y, '+'); + } + else { + k = sub + s_copyTgtOfs; + } + } + else if (s_copyTgtOfs < 0 && ((k < sub && k >= sub+s_copyTgtOfs) || (k-MAX_MODELS < sub && k-MAX_MODELS >= sub+s_copyTgtOfs))) + k += 1; + else if (s_copyTgtOfs > 0 && ((k > sub && k <= sub+s_copyTgtOfs) || (k+MAX_MODELS > sub && k+MAX_MODELS <= sub+s_copyTgtOfs))) + k += MAX_MODELS-1; + } + + k %= MAX_MODELS; + + if (eeModelExists(k)) { +#if defined(PCBSKY9X) + putsModelName(4*FW, y, modelHeaders[k].name, k, 0); +#elif defined(CPUARM) + putsModelName(4*FW, y, modelHeaders[k].name, k, 0); + lcd_outdezAtt(20*FW, y, eeModelSize(k), 0); +#else + char * name = reusableBuffer.modelsel.listnames[i]; + if (event) eeLoadModelName(k, name); + putsModelName(4*FW, y, name, k, 0); + lcd_outdezAtt(20*FW, y, eeModelSize(k), 0); +#endif + if (k==g_eeGeneral.currModel && (s_copyMode!=COPY_MODE || s_copySrcRow<0 || i+s_pgOfs!=(vertpos_t)sub)) lcd_putc(1, y, '*'); + } + + if (s_copyMode && (vertpos_t)sub==i+s_pgOfs) { + lcd_filled_rect(9, y, MODELSEL_W-1-9, 7); + lcd_rect(8, y-1, MODELSEL_W-1-7, 9, s_copyMode == COPY_MODE ? SOLID : DOTTED); + } + } + +#if defined(PCBTARANIS) + if (modelselBitmapIdx != m_posVert) { + modelselBitmapIdx = m_posVert; + if (modelselBitmapIdx == g_eeGeneral.currModel) + memcpy(modelselBitmap, modelBitmap, MODEL_BITMAP_SIZE); + else + loadModelBitmap(modelHeaders[sub].bitmap, modelselBitmap); + } + lcd_bmp(22*FW+2, 2*FH+FH/2, modelselBitmap); +#endif +} + +#if defined(PCBTARANIS) +uint8_t g_moduleIdx; +void menuModelFailsafe(uint8_t event) +{ + static bool longNames = false; + bool newLongNames = false; + uint8_t ch; + + if (event == EVT_KEY_BREAK(KEY_ENTER) && s_editMode) { + g_model.moduleData[g_moduleIdx].failsafeChannels[m_posVert] = channelOutputs[m_posVert]; + eeDirty(EE_MODEL); + AUDIO_WARNING1(); + SEND_FAILSAFE_NOW(g_moduleIdx); + } + + SIMPLE_SUBMENU_NOTITLE(32); + + SET_SCROLLBAR_X(0); + + if (m_posVert >= 16) + ch = 16; + else + ch = 0; + + lcd_putsCenter(0*FH, FAILSAFESET); + lcd_invert_line(0); + + // Column separator + lcd_vline(LCD_W/2, FH, LCD_H-FH); + + for (uint8_t col=0; col<2; col++) { + + uint8_t x = col*LCD_W/2+1; + + // Channels + for (uint8_t line=0; line<8; line++) { + uint8_t y = 9+line*7; + int32_t val; + uint8_t ofs = (col ? 0 : 1); + + if (ch < g_model.moduleData[g_moduleIdx].channelsStart || ch >= NUM_CHANNELS(g_moduleIdx) + g_model.moduleData[g_moduleIdx].channelsStart) + val = 0; + else if (s_editMode && m_posVert == ch) + val = channelOutputs[ch]; + else + val = g_model.moduleData[g_moduleIdx].failsafeChannels[8*col+line]; + + // Channel name if present, number if not + uint8_t lenLabel = ZLEN(g_model.limitData[ch].name); + if (lenLabel > 4) { + newLongNames = longNames = true; + } + + if (lenLabel > 0) + lcd_putsnAtt(x+1-ofs, y, g_model.limitData[ch].name, sizeof(g_model.limitData[ch].name), ZCHAR | SMLSIZE); + else + putsChn(x+1-ofs, y, ch+1, SMLSIZE); + + // Value + LcdFlags flags = TINSIZE; + if (m_posVert == ch) { + flags |= INVERS; + if (s_editMode) + flags |= BLINK; + } +#if defined(PPM_UNIT_US) + uint8_t wbar = (longNames ? 54 : 64); + lcd_outdezAtt(x+LCD_W/2-4-wbar-ofs, y, PPM_CH_CENTER(ch)+val/2, flags); +#elif defined(PPM_UNIT_PERCENT_PREC1) + uint8_t wbar = (longNames ? 48 : 58); + lcd_outdezAtt(x+LCD_W/2-4-wbar-ofs, y, calcRESXto1000(val), PREC1|flags); +#else + uint8_t wbar = (longNames ? 54 : 64); + lcd_outdezAtt(x+LCD_W/2-4-wbar-ofs, y, calcRESXto1000(val)/10, flags); +#endif + + // Gauge + lcd_rect(x+LCD_W/2-3-wbar-ofs, y, wbar+1, 6); + uint16_t lim = g_model.extendedLimits ? 640*2 : 512*2; + uint8_t len = limit((uint8_t)1, uint8_t((abs(val) * wbar/2 + lim/2) / lim), uint8_t(wbar/2)); + uint8_t x0 = (val>0) ? x+LCD_W/2-ofs-3-wbar/2 : x+LCD_W/2-ofs-2-wbar/2-len; + lcd_hline(x0, y+1, len); + lcd_hline(x0, y+2, len); + lcd_hline(x0, y+3, len); + lcd_hline(x0, y+4, len); + + ch++; + } + } + + longNames = newLongNames; +} +#endif + +#if defined(CPUM64) + #define editNameCursorPos m_posHorz +#else + static uint8_t editNameCursorPos = 0; +#endif + +void editName(uint8_t x, uint8_t y, char *name, uint8_t size, uint8_t event, uint8_t active) +{ +#if defined(CPUM64) + // in order to save flash + lcd_putsLeft(y, STR_NAME); +#endif + + lcd_putsnAtt(x, y, name, size, ZCHAR | ((active && s_editMode <= 0) ? INVERS : 0)); + + if (active) { + uint8_t cur = editNameCursorPos; + if (s_editMode > 0) { + int8_t c = name[cur]; + int8_t v = c; + + if (p1valdiff || IS_ROTARY_RIGHT(event) || IS_ROTARY_LEFT(event) || event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_FIRST(KEY_UP) + || event==EVT_KEY_REPT(KEY_DOWN) || event==EVT_KEY_REPT(KEY_UP)) { + v = checkIncDec(event, abs(v), 0, LEN_STD_CHARS, 0); + if (c <= 0) v = -v; + } + + switch (event) { +#if defined(ROTARY_ENCODER_NAVIGATION) || defined(PCBTARANIS) + case EVT_ROTARY_BREAK: + if (s_editMode == EDIT_MODIFY_FIELD) { + s_editMode = EDIT_MODIFY_STRING; + cur = 0; + } + else if (cur0) cur--; + break; + case EVT_KEY_BREAK(KEY_RIGHT): + if (cur=-26 && v<=26) { + v = -v; // toggle case + if (event==EVT_KEY_LONG(KEY_LEFT)) + killEvents(KEY_LEFT); + } + break; + } + + if (c != v) { + name[cur] = v; + eeDirty(EE_MODEL); + } + lcd_putcAtt(x+editNameCursorPos*FW, y, idx2char(v), INVERS); + } + else { + cur = 0; + } + editNameCursorPos = cur; + } +} + +#if defined(CPUM64) +#define editSingleName(x, y, label, name, size, event, active) editName(x, y, name, size, event, active) +#else +void editSingleName(uint8_t x, uint8_t y, const pm_char *label, char *name, uint8_t size, uint8_t event, uint8_t active) +{ + lcd_putsLeft(y, label); + editName(x, y, name, size, event, active); +} +#endif + +enum menuModelSetupItems { + ITEM_MODEL_NAME, + CASE_PCBTARANIS(ITEM_MODEL_BITMAP) + ITEM_MODEL_TIMER1, + IF_PERSISTENT_TIMERS(ITEM_MODEL_TIMER1_PERSISTENT) + ITEM_MODEL_TIMER1_MINUTE_BEEP, + ITEM_MODEL_TIMER1_COUNTDOWN_BEEP, + ITEM_MODEL_TIMER2, + IF_PERSISTENT_TIMERS(ITEM_MODEL_TIMER2_PERSISTENT) + ITEM_MODEL_TIMER2_MINUTE_BEEP, + ITEM_MODEL_TIMER2_COUNTDOWN_BEEP, + ITEM_MODEL_EXTENDED_LIMITS, + ITEM_MODEL_EXTENDED_TRIMS, + ITEM_MODEL_TRIM_INC, + ITEM_MODEL_THROTTLE_REVERSED, + ITEM_MODEL_THROTTLE_TRACE, + ITEM_MODEL_THROTTLE_TRIM, + ITEM_MODEL_THROTTLE_WARNING, + ITEM_MODEL_SWITCHES_WARNING, + ITEM_MODEL_BEEP_CENTER, +#if defined(PCBTARANIS) + ITEM_MODEL_INTERNAL_MODULE_LABEL, + ITEM_MODEL_INTERNAL_MODULE_MODE, + ITEM_MODEL_INTERNAL_MODULE_CHANNELS, + ITEM_MODEL_INTERNAL_MODULE_BIND, + ITEM_MODEL_INTERNAL_MODULE_FAILSAFE, + ITEM_MODEL_EXTERNAL_MODULE_LABEL, + ITEM_MODEL_EXTERNAL_MODULE_MODE, + ITEM_MODEL_EXTERNAL_MODULE_CHANNELS, + ITEM_MODEL_EXTERNAL_MODULE_BIND, + ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE, + ITEM_MODEL_TRAINER_LABEL, + ITEM_MODEL_TRAINER_MODE, + ITEM_MODEL_TRAINER_CHANNELS, + ITEM_MODEL_TRAINER_SETTINGS, +#elif defined(PCBSKY9X) + ITEM_MODEL_PPM1_PROTOCOL, + ITEM_MODEL_PPM1_PARAMS, + ITEM_MODEL_PPM2_PROTOCOL, + ITEM_MODEL_PPM2_PARAMS, +#else + ITEM_MODEL_PPM1_PROTOCOL, + ITEM_MODEL_PPM1_PARAMS, +#endif + ITEM_MODEL_SETUP_MAX +}; + +#if defined(PCBSKY9X) + #define FIELD_PROTOCOL_MAX 2 +#else + #define FIELD_PROTOCOL_MAX 1 +#endif + +#if LCD_W >= 212 +#define MODEL_SETUP_2ND_COLUMN (LCD_W-17*FW-MENUS_SCROLLBAR_WIDTH) +#else +#define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW-MENUS_SCROLLBAR_WIDTH) +#endif + +#if defined(PCBTARANIS) && defined(SDCARD) +void copySelection(char *dst, const char *src, uint8_t size) +{ + if (memcmp(src, "---", 3) == 0) + memset(dst, 0, size); + else + memcpy(dst, src, size); +} + +void onModelSetupBitmapMenu(const char *result) +{ + if (result == STR_UPDATE_LIST) { + if (!listSdFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), NULL)) { + POPUP_WARNING(STR_NO_BITMAPS_ON_SD); + s_menu_flags = 0; + } + } + else { + // The user choosed a bmp file in the list + copySelection(g_model.header.bitmap, result, sizeof(g_model.header.bitmap)); + LOAD_MODEL_BITMAP(); + memcpy(modelHeaders[g_eeGeneral.currModel].bitmap, g_model.header.bitmap, sizeof(g_model.header.bitmap)); + eeDirty(EE_MODEL); + } +} +#endif + +void menuModelSetup(uint8_t event) +{ +#if defined(PCBTARANIS) + #define IF_PORT1_ON(x) (g_model.moduleData[0].rfProtocol == RF_PROTO_OFF ? HIDDEN_ROW : (uint8_t)(x)) + #define IF_PORT2_ON(x) (g_model.externalModule == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) + #define IF_TRAINER_ON(x) (g_model.trainerMode ? (uint8_t)(x) : HIDDEN_ROW) + #define IF_PORT_ON(idx, x) (idx==0 ? IS_PORT1_ON(x) : (idx==1 ? IS_PORT2_ON(x) : IS_TRAINER_ON())) + #define IF_PORT2_XJT(x) (IS_MODULE_XJT(1) ? (uint8_t)x : HIDDEN_ROW) + #define IS_D8_RX(x) (g_model.moduleData[x].rfProtocol == RF_PROTO_D8) + #define PORT1_CHANNELS_ROWS() IF_PORT1_ON(1) + #define PORT2_CHANNELS_ROWS() IF_PORT2_ON(IS_MODULE_DSM2(1) ? (uint8_t)0 : (uint8_t)1) + #define TRAINER_CHANNELS_ROWS() IF_TRAINER_ON(1) + #define PORT_CHANNELS_ROWS(x) (x==0 ? PORT1_CHANNELS_ROWS() : (x==1 ? PORT2_CHANNELS_ROWS() : TRAINER_CHANNELS_ROWS())) + #define FAILSAFE_ROWS(x) ((g_model.moduleData[x].rfProtocol==RF_PROTO_X16 || g_model.moduleData[x].rfProtocol==RF_PROTO_LR12) ? (g_model.moduleData[x].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW) + #define MODEL_SETUP_MAX_LINES (1+ITEM_MODEL_SETUP_MAX) + + bool CURSOR_ON_CELL = (m_posHorz >= 0); + MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) 2, IF_PERSISTENT_TIMERS(0) 0, 0, 2, IF_PERSISTENT_TIMERS(0) 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, NAVIGATION_LINE_BY_LINE|(NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1), LABEL(InternalModule), 0, IF_PORT1_ON(1), IF_PORT1_ON(IS_D8_RX(0) ? (uint8_t)1 : (uint8_t)2), IF_PORT1_ON(FAILSAFE_ROWS(0)), LABEL(ExternalModule), (g_model.externalModule==MODULE_TYPE_XJT || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0, PORT2_CHANNELS_ROWS(), (IS_MODULE_XJT(1) && IS_D8_RX(1)) ? (uint8_t)1 : (IS_MODULE_PPM(1) || IS_MODULE_XJT(1) || IS_MODULE_DSM2(1)) ? (uint8_t)2 : HIDDEN_ROW, IF_PORT2_XJT(FAILSAFE_ROWS(1)), LABEL(Trainer), 0, TRAINER_CHANNELS_ROWS(), IF_TRAINER_ON(2)}); +#elif defined(CPUM64) + #define CURSOR_ON_CELL (true) + #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? 1+ITEM_MODEL_SETUP_MAX : ITEM_MODEL_SETUP_MAX) + + uint8_t protocol = g_model.protocol; + MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) 2, IF_PERSISTENT_TIMERS(0) 0, 0, 2, IF_PERSISTENT_TIMERS(0) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2 }); +#else + #define CURSOR_ON_CELL (true) + #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? 1+ITEM_MODEL_SETUP_MAX : ITEM_MODEL_SETUP_MAX) + + uint8_t protocol = g_model.protocol; + MENU_TAB({ 0, 0, CASE_PCBTARANIS(0) 2, IF_PERSISTENT_TIMERS(0) 0, 0, 2, IF_PERSISTENT_TIMERS(0) 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2, IF_PCBSKY9X(1) IF_PCBSKY9X(2) }); +#endif + + if (!MENU_CHECK(menuTabModel, e_ModelSetup, MODEL_SETUP_MAX_LINES)) { +#if defined(DSM2) + dsm2Flag = 0; +#endif +#if defined(PCBTARANIS) + pxxFlag[INTERNAL_MODULE] = 0; + pxxFlag[EXTERNAL_MODULE] = 0; +#endif + return; + } + + TITLE(STR_MENUSETUP); + + uint8_t sub = m_posVert - 1; + int8_t editMode = s_editMode; + + for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS); + uint8_t attr = (sub == k ? blink : 0); + + switch(k) { + case ITEM_MODEL_NAME: + editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_MODELNAME, g_model.header.name, sizeof(g_model.header.name), event, attr); +#if defined(CPUARM) + memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name)); +#endif + break; + +#if defined(PCBTARANIS) && defined(SDCARD) + case ITEM_MODEL_BITMAP: + lcd_putsLeft(y, STR_BITMAP); + if (ZEXIST(g_model.header.bitmap)) + lcd_putsnAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.bitmap, sizeof(g_model.header.bitmap), attr); + else + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); + if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) { + s_editMode = 0; + if (listSdFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), g_model.header.bitmap, LIST_NONE_SD_FILE)) { + menuHandler = onModelSetupBitmapMenu; + } + else { + POPUP_WARNING(STR_NO_BITMAPS_ON_SD); + s_menu_flags = 0; + } + } + break; +#endif + + case ITEM_MODEL_TIMER1: + case ITEM_MODEL_TIMER2: + case ITEM_MODEL_TIMER1_MINUTE_BEEP: + case ITEM_MODEL_TIMER2_MINUTE_BEEP: + case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP: + case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP: + { + TimerData *timer = &g_model.timers[k>=ITEM_MODEL_TIMER2 ? 1 : 0]; + if (k==ITEM_MODEL_TIMER1_MINUTE_BEEP || k==ITEM_MODEL_TIMER2_MINUTE_BEEP) { + timer->minuteBeep = onoffMenuItem(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event); + } + else if (k==ITEM_MODEL_TIMER1_COUNTDOWN_BEEP || k==ITEM_MODEL_TIMER2_COUNTDOWN_BEEP) { +#if defined(CPUARM) + timer->countdownBeep = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, STR_VBEEPCOUNTDOWN, timer->countdownBeep, 0, 2, attr, event); +#else + timer->countdownBeep = onoffMenuItem(timer->countdownBeep, MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, attr, event); +#endif + } + else { + putsStrIdx(0*FW, y, STR_TIMER, k>=ITEM_MODEL_TIMER2 ? 2 : 1); + putsTmrMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, m_posHorz==0 ? attr : 0); + putsTime(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, m_posHorz==1 ? attr : 0, m_posHorz==2 ? attr : 0); +#if defined(PCBTARANIS) + if (attr && m_posHorz < 0) lcd_filled_rect(MODEL_SETUP_2ND_COLUMN, y, LCD_W-MODEL_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8); +#endif + if (attr && (editMode>0 || p1valdiff)) { + div_t qr = div(timer->start, 60); + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, timer->mode, -2*(MAX_PSWITCH+NUM_CSW), TMR_VAROFS-1+2*(MAX_PSWITCH+NUM_CSW)); + break; + case 1: + CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 59); + timer->start = qr.rem + qr.quot*60; + break; + case 2: + qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; + timer->start -= qr.rem ; + if ((int16_t)timer->start < 0) timer->start=0; + break; + } + } + } + break; + } + +#if defined(CPUARM) || defined(PCBGRUVIN9X) + case ITEM_MODEL_TIMER1_PERSISTENT: + case ITEM_MODEL_TIMER2_PERSISTENT: + { + TimerData &timer = g_model.timers[k==ITEM_MODEL_TIMER2_PERSISTENT]; + timer.persistent = onoffMenuItem(timer.persistent, MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, attr, event); + break; + } +#endif + + case ITEM_MODEL_EXTENDED_LIMITS: + g_model.extendedLimits = onoffMenuItem(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, STR_ELIMITS, attr, event); + break; + + case ITEM_MODEL_EXTENDED_TRIMS: +#if defined(CPUM64) + g_model.extendedTrims = onoffMenuItem(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, attr, event); +#else + g_model.extendedTrims = onoffMenuItem(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, m_posHorz<=0 ? attr : 0, event==EVT_KEY_BREAK(KEY_ENTER) ? event : 0); + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_RESET_BTN, m_posHorz>0 ? attr : 0); + if (attr && m_posHorz>0) { + s_editMode = 0; + if (event==EVT_KEY_LONG(KEY_ENTER)) { + for (uint8_t i=0; i MIXSRC_Thr) + idx += 1; + if (idx >= MIXSRC_FIRST_POT+NUM_POTS) + idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS; + putsMixerSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr); + break; + } + + case ITEM_MODEL_THROTTLE_TRIM: + g_model.thrTrim = onoffMenuItem(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event); + break; + + case ITEM_MODEL_THROTTLE_WARNING: + g_model.disableThrottleWarning = !onoffMenuItem(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEWARNING, attr, event); + break; + + case ITEM_MODEL_SWITCHES_WARNING: + { + lcd_putsLeft(y, STR_SWITCHWARNING); + swstate_t states = g_model.switchWarningStates; + char c = !(states & 1); + menu_lcd_onoff(MODEL_SETUP_2ND_COLUMN, y, c, attr); + if (attr) { + s_editMode = 0; + switch(event) { + case EVT_KEY_LONG(KEY_ENTER): + if (!READ_ONLY()) { + killEvents(event); + getMovedSwitch(); + g_model.switchWarningStates = 0x01 + (switches_states << 1); + } + // no break + CASE_EVT_ROTARY_BREAK + case EVT_KEY_BREAK(KEY_ENTER): +#if !defined(PCBTARANIS) + case EVT_KEY_BREAK(KEY_LEFT): + case EVT_KEY_BREAK(KEY_RIGHT): +#endif + if (!READ_ONLY()) { + g_model.switchWarningStates ^= 0x01; + eeDirty(EE_MODEL); + } + break; + } + } + if (c) { + states >>= 1; + for (uint8_t i=1; i>= 2; +#else + attr = 0; + if (IS_3POS(i-1)) { + c = '0'+(states & 0x03); + states >>= 2; + } + else { + if (states & 0x01) + attr = INVERS; + c = pgm_read_byte(STR_VSWITCHES - 2 + 6 + (3*i)); + states >>= 1; + } + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+2*FW+i*FW, y, c, attr); +#endif + } + } + break; + } + + case ITEM_MODEL_BEEP_CENTER: + lcd_putsLeft(y, STR_BEEPCTR); + for (uint8_t i=0; i0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.externalModule, MODULE_TYPE_NONE, MODULE_TYPE_COUNT-1); + if (checkIncDec_Ret) { + g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; + if (g_model.externalModule == MODULE_TYPE_PPM) + g_model.moduleData[EXTERNAL_MODULE].channelsCount = 0; + else + g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_PORT2_CHANNELS(); + } + break; + case 1: + if (IS_MODULE_DSM2(EXTERNAL_MODULE)) + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX); + else + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, RF_PROTO_X16, RF_PROTO_LAST); + if (checkIncDec_Ret) { + g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; + g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_PORT2_CHANNELS(); + } + } + } + break; + + case ITEM_MODEL_INTERNAL_MODULE_CHANNELS: + case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS: + case ITEM_MODEL_TRAINER_CHANNELS: + { + uint8_t moduleIdx = (k>=ITEM_MODEL_TRAINER_LABEL ? 2 : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? 1 : 0)); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcd_putsLeft(y, STR_CHANNELRANGE); + if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx) >= 0) { + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN, y, STR_CH, m_posHorz==0 ? attr : 0); + lcd_outdezAtt(lcdLastPos, y, moduleData.channelsStart+1, LEFT | (m_posHorz==0 ? attr : 0)); + lcd_putc(lcdLastPos, y, '-'); + lcd_outdezAtt(lcdLastPos + FW+1, y, moduleData.channelsStart+NUM_CHANNELS(moduleIdx), LEFT | (m_posHorz==1 ? attr : 0)); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, moduleData.channelsCount, -4, min(MAX_CHANNELS(moduleIdx), 32-8-moduleData.channelsStart)); + if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.externalModule == MODULE_TYPE_PPM) || (k == ITEM_MODEL_TRAINER_CHANNELS)) + SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx); + break; + } + } + } + break; + } + + case ITEM_MODEL_INTERNAL_MODULE_BIND: + case ITEM_MODEL_EXTERNAL_MODULE_BIND: + case ITEM_MODEL_TRAINER_SETTINGS: + { + uint8_t moduleIdx = (k>=ITEM_MODEL_TRAINER_LABEL ? 2 : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? 1 : 0)); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + if (IS_MODULE_PPM(moduleIdx)) { + lcd_putsLeft(y, STR_PPMFRAME); + lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); + lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (moduleData.ppmDelay*50)+300, (m_posHorz < 0 || m_posHorz==1) ? attr : 0); + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, moduleData.ppmPulsePol ? '+' : '-', (m_posHorz < 0 || m_posHorz==2) ? attr : 0); + + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, moduleData.ppmFrameLength, -20, 35); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, moduleData.ppmDelay, -4, 10); + break; + case 2: + CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppmPulsePol, 1); + break; + } + } + } + else { + horzpos_t l_posHorz = m_posHorz; + uint8_t xOffsetBind = 3*FW; + if (IS_MODULE_XJT(moduleIdx) && IS_D8_RX(moduleIdx)) { + xOffsetBind = 0; + lcd_putsLeft(y, INDENT "Receiver"); + if (attr) l_posHorz += 1; + } + else { + lcd_putsLeft(y, STR_RXNUM); + } + if (IS_MODULE_XJT(moduleIdx) || IS_MODULE_DSM2(moduleIdx)) { + if (xOffsetBind) lcd_outdezNAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId, (l_posHorz==0 ? attr : 0) | LEADING0|LEFT, 2); + if (attr && l_posHorz==0) { + if (editMode>0 || p1valdiff) { + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId, IS_MODULE_DSM2(moduleIdx) ? 20 : 63); + if (checkIncDec_Ret) { + modelHeaders[g_eeGeneral.currModel].modelId = g_model.header.modelId; + } + } + if (editMode==0 && event==EVT_KEY_BREAK(KEY_ENTER)) { + checkModelIdUnique(g_eeGeneral.currModel); + } + } + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, l_posHorz==1 ? attr : 0); + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+7*FW+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0); + if (IS_MODULE_XJT(moduleIdx)) { + uint8_t newFlag = 0; + if (attr && l_posHorz>0 && s_editMode>0) { + if (l_posHorz == 1) + newFlag = PXX_SEND_RXNUM; + else if (l_posHorz == 2) { + newFlag = PXX_SEND_RANGECHECK; + } + } + pxxFlag[moduleIdx] = newFlag; + } +#if defined(DSM2) + else { + uint8_t newFlag = 0; + if (attr && l_posHorz>0 && s_editMode>0) { + if (l_posHorz == 1) + newFlag = DSM2_BIND_FLAG; + else if (l_posHorz == 2) { + newFlag = DSM2_RANGECHECK_FLAG; + } + } + dsm2Flag = newFlag; + } +#endif + } + } + break; + } + + case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE: + case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE: + { + uint8_t moduleIdx = (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? 1 : 0); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcd_putsLeft(y, TR_FAILSAFE); + if (IS_MODULE_XJT(moduleIdx)) { + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, m_posHorz==0 ? attr : 0); + if (moduleData.failsafeMode == FAILSAFE_CUSTOM) lcd_putsAtt(MODEL_SETUP_2ND_COLUMN + 10*FW, y, STR_SET, m_posHorz==1 ? attr : 0); + if (attr) { + if (moduleData.failsafeMode != FAILSAFE_CUSTOM) + m_posHorz = 0; + if (m_posHorz==0) { + if (editMode>0 || p1valdiff) { + CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, FAILSAFE_LAST); + if (checkIncDec_Ret) SEND_FAILSAFE_NOW(moduleIdx); + } + } + else if (m_posHorz==1) { + s_editMode = 0; + if (moduleData.failsafeMode==FAILSAFE_CUSTOM && event==EVT_KEY_FIRST(KEY_ENTER)) { + g_moduleIdx = moduleIdx; + pushMenu(menuModelFailsafe); + } + } + else { + lcd_filled_rect(MODEL_SETUP_2ND_COLUMN, y, LCD_W-MODEL_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8); + } + } + } + break; + } + + case ITEM_MODEL_TRAINER_LABEL: + lcd_putsLeft(y, STR_TRAINER); + break; + + case ITEM_MODEL_TRAINER_MODE: + g_model.trainerMode = selectMenuItem(MODEL_SETUP_2ND_COLUMN, y, STR_MODE, STR_VTRAINERMODES, g_model.trainerMode, 0, 1, attr, event); + break; + +#else + + case ITEM_MODEL_PPM1_PROTOCOL: +#if defined(PCBSKY9X) + lcd_putsLeft(y, PSTR("Port1")); +#else + lcd_putsLeft(y, NO_INDENT(STR_PROTO)); +#endif + + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, protocol, m_posHorz<=0 ? attr : 0); + +#if defined(PCBSKY9X) + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW+3, y, STR_CH, m_posHorz==1 ? attr : 0); + lcd_outdezAtt(lcdLastPos, y, g_model.moduleData[0].channelsStart+1, LEFT | (m_posHorz==1 ? attr : 0)); + lcd_putc(lcdLastPos, y, '-'); + lcd_outdezAtt(lcdLastPos + FW+1, y, g_model.moduleData[0].channelsStart+NUM_PORT1_CHANNELS(), LEFT | (m_posHorz==2 ? attr : 0)); +#else + if (IS_PPM_PROTOCOL(protocol)) { + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN+7*FW, y, STR_NCHANNELS, g_model.ppmNCH+2, m_posHorz!=0 ? attr : 0); + } + else if (attr) { + MOVE_CURSOR_FROM_HERE(); + } +#endif + + if (attr && (editMode>0 || p1valdiff +#if !defined(PCBSKY9X) + || (!IS_PPM_PROTOCOL(protocol) && !IS_DSM2_PROTOCOL(protocol)) +#endif + )) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.protocol, PROTO_MAX-1); + break; + case 1: +#if defined(PCBSKY9X) + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[0].channelsStart, 32-8-g_model.moduleData[0].channelsCount); + g_model.moduleData[0].ppmFrameLength = max((int8_t)0, g_model.moduleData[0].channelsCount) * 4; + SET_DEFAULT_PPM_FRAME_LENGTH(0); +#else + CHECK_INCDEC_MODELVAR(event, g_model.ppmNCH, -2, 4); + g_model.ppmFrameLength = g_model.ppmNCH * 8; +#endif + break; +#if defined(PCBSKY9X) + case 2: + if (IS_PPM_PROTOCOL(protocol)) { + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[0].channelsCount, -4, min(8, 32-8-g_model.moduleData[0].channelsStart)); + SET_DEFAULT_PPM_FRAME_LENGTH(0); + } + else + REPEAT_LAST_CURSOR_MOVE(); + break; +#endif + } + } + break; + +#if defined(PCBSKY9X) + case ITEM_MODEL_PPM2_PROTOCOL: + lcd_putsLeft(y, PSTR("Port2")); + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, 0, 0); + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW+3, y, STR_CH, m_posHorz<=0 ? attr : 0); + lcd_outdezAtt(lcdLastPos, y, g_model.moduleData[1].channelsStart+1, LEFT | (m_posHorz<=0 ? attr : 0)); + lcd_putc(lcdLastPos, y, '-'); + lcd_outdezAtt(lcdLastPos + FW+1, y, g_model.moduleData[1].channelsStart+8+g_model.moduleData[1].channelsCount, LEFT | (m_posHorz!=0 ? attr : 0)); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].channelsStart, 32-8-g_model.moduleData[1].channelsCount); + SET_DEFAULT_PPM_FRAME_LENGTH(1); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].channelsCount, -4, min(8, 32-8-g_model.moduleData[1].channelsStart)); + SET_DEFAULT_PPM_FRAME_LENGTH(1); + break; + } + } + break; + + case ITEM_MODEL_PPM2_PARAMS: + lcd_putsLeft(y, STR_PPMFRAME); + lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.moduleData[1].ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); + lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.moduleData[1].ppmDelay*50)+300, (m_posHorz < 0 || m_posHorz==1) ? attr : 0); + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.moduleData[1].ppmPulsePol ? '+' : '-', (m_posHorz < 0 || m_posHorz==2) ? attr : 0); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmFrameLength, -20, 35); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmDelay, -4, 10); + break; + case 2: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].ppmPulsePol, 1); + break; + } + } + break; +#endif + + case ITEM_MODEL_PPM1_PARAMS: +#if defined(PCBSKY9X) + if (IS_PPM_PROTOCOL(protocol)) { + lcd_putsLeft(y, STR_PPMFRAME); + lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.moduleData[0].ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); + lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.moduleData[0].ppmDelay*50)+300, (m_posHorz < 0 || m_posHorz==1) ? attr : 0); + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.moduleData[0].ppmPulsePol ? '+' : '-', (m_posHorz < 0 || m_posHorz==2) ? attr : 0); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[0].ppmFrameLength, -20, 35); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[0].ppmDelay, -4, 10); + break; + case 2: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[0].ppmPulsePol, 1); + break; + } + } + } +#else + if (IS_PPM_PROTOCOL(protocol)) { + lcd_putsLeft(y, STR_PPMFRAME); + lcd_puts(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.ppmFrameLength*5 + 225, (m_posHorz<=0 ? attr : 0) | PREC1|LEFT); + lcd_putc(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u'); + lcd_outdezAtt(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.ppmDelay*50)+300, (m_posHorz < 0 || m_posHorz==1) ? attr : 0); + lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.pulsePol ? '+' : '-', (m_posHorz < 0 || m_posHorz==2) ? attr : 0); + if (attr && (editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.ppmFrameLength, -20, 35); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.ppmDelay, -4, 10); + break; + case 2: + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.pulsePol, 1); + break; + } + } + } +#endif +#if defined(DSM2) || defined(PXX) + else if (IS_DSM2_PROTOCOL(protocol) || IS_PXX_PROTOCOL(protocol)) { + if (attr && m_posHorz > 1) { + REPEAT_LAST_CURSOR_MOVE(); // limit 3 column row to 2 colums (Rx_Num and RANGE fields) + } + + lcd_putsLeft(y, STR_RXNUM); + lcd_outdezNAtt(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId, (m_posHorz<=0 ? attr : 0) | LEADING0|LEFT, 2); + if (attr && (m_posHorz==0 && (editMode>0 || p1valdiff))) { + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId, 99); +#if defined(CPUARM) + if (checkIncDec_Ret) + modelHeaders[g_eeGeneral.currModel].modelId = g_model.header.modelId; +#endif + } + +#if defined(PXX) + if (protocol == PROTO_PXX) { +#if defined(CPUARM) + if (attr && m_posHorz==0 && editMode==0 && event==EVT_KEY_BREAK(KEY_ENTER)) + checkModelIdUnique(g_eeGeneral.currModel); +#endif + + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_SYNCMENU, m_posHorz!=0 ? attr : 0); + if (attr && m_posHorz>0 && editMode>0) { + // send reset code + pxxFlag[0] = PXX_SEND_RXNUM; + } + } +#endif + +#if defined(DSM2) + if (IS_DSM2_PROTOCOL(protocol)) { + lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_MODULE_RANGE, m_posHorz!=0 ? attr : 0); + dsm2Flag = (attr && m_posHorz>0 && editMode>0) ? DSM2_RANGECHECK_FLAG : 0; // [MENU] key toggles range check mode + } +#endif + } +#endif + break; +#endif // defined(PCBTARANIS) + } + } + +#if defined(PCBTARANIS) + if (pxxFlag[INTERNAL_MODULE] == PXX_SEND_RANGECHECK || pxxFlag[EXTERNAL_MODULE] == PXX_SEND_RANGECHECK) { + displayPopup("RSSI: "); + lcd_outdezAtt(16+4*FW, 5*FH, frskyData.rssi[0].value, BOLD); + } +#endif +} + +static uint8_t s_currIdx; + +#if MENU_COLUMNS < 2 + #if LCD_W >= 212 + #define MIXES_2ND_COLUMN (18*FW) + #else + #define MIXES_2ND_COLUMN (12*FW) + #endif +#else + #define MIXES_2ND_COLUMN (9*FW) +#endif + +#if LCD_W >= 212 + #define EXPO_ONE_2ND_COLUMN (LCD_W-8*FW-90) + #define EXPO_ONE_FP_WIDTH (9*FW) +#else + #define EXPO_ONE_2ND_COLUMN (7*FW+3*FW+2) + #define EXPO_ONE_FP_WIDTH (5*FW) +#endif + +#if MENU_COLUMNS > 1 +uint8_t editDelay(const xcoord_t x, const uint8_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay) +{ + lcd_puts(x, y, str); + lcd_outdezAtt(x+MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX); + return delay; +} +#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(x, y, event, attr, str, delay) +#else +uint8_t editDelay(const uint8_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay) +{ + lcd_putsLeft(y, str); + lcd_outdezAtt(MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX); + return delay; +} +#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(y, event, attr, str, delay) +#endif + +#if defined(FLIGHT_MODES) + +#if defined(CPUARM) + #define FlightModesType uint16_t +#else + #define FlightModesType uint8_t +#endif + +void displayFlightModes(uint8_t x, uint8_t y, FlightModesType value) +{ + uint8_t p = MAX_PHASES; + do { + --p; + if (!(value & (1< EXPO_ONE_2ND_COLUMN-FW))) + continue; +#endif +#if defined(PCBTARANIS) + LcdFlags flags = 0; + if (attr) { + flags |= INVERS; + if (posHorz==p) flags |= BLINK; + } + if (value & (1<= 212 + +enum FlightModesItems { + ITEM_PHASES_NAME, + ITEM_PHASES_SWITCH, + ITEM_PHASES_TRIMS, + ITEM_PHASES_FADE_IN, + ITEM_PHASES_FADE_OUT, + ITEM_PHASES_COUNT, + ITEM_PHASES_LAST = ITEM_PHASES_COUNT-1 +}; + +void editPhaseTrims(uint8_t x, uint8_t y, uint8_t phase, uint8_t event, uint8_t active) +{ + static uint8_t cursorPos = 0; + + for (uint8_t t=0; t 0) { + if (p1valdiff || IS_ROTARY_RIGHT(event) || IS_ROTARY_LEFT(event) || event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_FIRST(KEY_UP) + || event==EVT_KEY_REPT(KEY_DOWN) || event==EVT_KEY_REPT(KEY_UP)) { + int16_t v = getRawTrimValue(phase, cur); + if (v < TRIM_EXTENDED_MAX) v = TRIM_EXTENDED_MAX; + v = checkIncDec(event, v, TRIM_EXTENDED_MAX, TRIM_EXTENDED_MAX+MAX_PHASES-1, EE_MODEL); + if (checkIncDec_Ret) { + if (v == TRIM_EXTENDED_MAX) v = 0; + setTrimValue(phase, cur, v); + } + } + + switch (event) { +#if defined(ROTARY_ENCODER_NAVIGATION) || defined(PCBTARANIS) + case EVT_ROTARY_BREAK: + if (s_editMode == EDIT_MODIFY_FIELD) { + s_editMode = EDIT_MODIFY_STRING; + cur = 0; + } + else if (cur0) cur--; + break; + case EVT_KEY_BREAK(KEY_RIGHT): + if (cur 0) { posHorz += 2; } + + if (sub=0) { + displayColumnHeader(STR_PHASES_HEADERS, posHorz); + } + + for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS) : 0); + uint8_t active = (attr && (s_editMode>0 || p1valdiff)) ; + switch(j) + { + case ITEM_PHASES_NAME: + editName(4*FW, y, p->name, sizeof(p->name), event, attr); + break; + + case ITEM_PHASES_SWITCH: + if (k == 0) { + lcd_puts((5+LEN_FP_NAME)*FW+FW/2, y, STR_DEFAULT); + } + else { + putsSwitches((5+LEN_FP_NAME)*FW+FW/2, y, p->swtch, attr); + if (active) CHECK_INCDEC_MODELSWITCH(event, p->swtch, -MAX_SWITCH, MAX_SWITCH); + } + break; + + case ITEM_PHASES_TRIMS: + if (k != 0) { + editPhaseTrims((10+LEN_FP_NAME)*FW+FW/2, y, k, event, attr); + } + break; + + case ITEM_PHASES_FADE_IN: + lcd_outdezAtt(29*FW, y, (10/DELAY_STEP)*p->fadeIn, attr|PREC1); + if (active) p->fadeIn = checkIncDec(event, p->fadeIn, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); + break; + + case ITEM_PHASES_FADE_OUT: + lcd_outdezAtt(34*FW, y, (10/DELAY_STEP)*p->fadeOut, attr|PREC1); + if (active) p->fadeOut = checkIncDec(event, p->fadeOut, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); + break; + + } + } + } +} + +#else // LCD_W >= 212 + +enum menuModelPhaseItems { + ITEM_MODEL_PHASE_NAME, + ITEM_MODEL_PHASE_SWITCH, + ITEM_MODEL_PHASE_TRIMS, + IF_ROTARY_ENCODERS(ITEM_MODEL_PHASE_ROTARY_ENCODERS) + ITEM_MODEL_PHASE_FADE_IN, + ITEM_MODEL_PHASE_FADE_OUT, +#if defined(GVARS) && !defined(PCBSTD) + ITEM_MODEL_PHASE_GVARS_LABEL, + ITEM_MODEL_PHASE_GV1, + ITEM_MODEL_PHASE_GV2, + ITEM_MODEL_PHASE_GV3, + ITEM_MODEL_PHASE_GV4, + ITEM_MODEL_PHASE_GV5, +#endif + ITEM_MODEL_PHASE_MAX +}; + +void menuModelPhaseOne(uint8_t event) +{ + PhaseData *phase = phaseAddress(s_currIdx); + putsFlightPhase(13*FW, 0, s_currIdx+1, (getFlightPhase()==s_currIdx ? BOLD : 0)); + +#if defined(GVARS) && !defined(PCBSTD) + static const pm_uint8_t mstate_tab_phase1[] PROGMEM = {0, 0, 0, (uint8_t)-1, 1, 1, 1, 1, 1}; + static const pm_uint8_t mstate_tab_others[] PROGMEM = {0, 0, 3, IF_ROTARY_ENCODERS(NUM_ROTARY_ENCODERS-1) 0, 0, (uint8_t)-1, 2, 2, 2, 2, 2}; + + if (!check(event, 0, NULL, 0, (s_currIdx == 0) ? mstate_tab_phase1 : mstate_tab_others, DIM(mstate_tab_others)-1, ITEM_MODEL_PHASE_MAX - 1 - (s_currIdx==0 ? (ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH) : 0))) return; + + TITLE(STR_MENUFLIGHTPHASE); + + #define PHASE_ONE_FIRST_LINE (1+1*FH) +#else + SUBMENU(STR_MENUFLIGHTPHASE, 3 + (s_currIdx==0 ? 0 : 2 + (bool)NUM_ROTARY_ENCODERS), {0, 0, 3, IF_ROTARY_ENCODERS(NUM_ROTARY_ENCODERS-1) 0/*, 0*/}); + #define PHASE_ONE_FIRST_LINE (1+1*FH) +#endif + + int8_t sub = m_posVert; + int8_t editMode = s_editMode; + +#if defined(GVARS) && !defined(PCBSTD) + if (s_currIdx == 0 && sub>=ITEM_MODEL_PHASE_SWITCH) sub += ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH; + + for (uint8_t k=0; k=ITEM_MODEL_PHASE_SWITCH) i += ITEM_MODEL_PHASE_FADE_IN-ITEM_MODEL_PHASE_SWITCH; + uint8_t attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0); +#else + for (uint8_t i=0, k=0, y=PHASE_ONE_FIRST_LINE; i0 ? BLINK|INVERS : INVERS) : 0); +#endif + switch(i) { + case ITEM_MODEL_PHASE_NAME: + editSingleName(MIXES_2ND_COLUMN, y, STR_PHASENAME, phase->name, sizeof(phase->name), event, attr); + break; + case ITEM_MODEL_PHASE_SWITCH: + phase->swtch = switchMenuItem(MIXES_2ND_COLUMN, y, phase->swtch, attr, event); + break; + case ITEM_MODEL_PHASE_TRIMS: + lcd_putsLeft(y, STR_TRIMS); + for (uint8_t t=0; t0) || p1valdiff)) { + int16_t v = getRawTrimValue(s_currIdx, t); + if (v < TRIM_EXTENDED_MAX) v = TRIM_EXTENDED_MAX; + v = checkIncDec(event, v, TRIM_EXTENDED_MAX, TRIM_EXTENDED_MAX+MAX_PHASES-1, EE_MODEL); + if (checkIncDec_Ret) { + if (v == TRIM_EXTENDED_MAX) v = 0; + setTrimValue(s_currIdx, t, v); + } + } + } + break; + +#if ROTARY_ENCODERS > 0 + case ITEM_MODEL_PHASE_ROTARY_ENCODERS: + lcd_putsLeft(y, STR_ROTARY_ENCODER); + for (uint8_t t=0; t0) || p1valdiff)) { +#if ROTARY_ENCODERS > 2 + int16_t v; + if(t < (NUM_ROTARY_ENCODERS - NUM_ROTARY_ENCODERS_EXTRA)) + v = phaseAddress(s_currIdx)->rotaryEncoders[t]; + else + v = g_model.rotaryEncodersExtra[s_currIdx][t-(NUM_ROTARY_ENCODERS - NUM_ROTARY_ENCODERS_EXTRA)]; +#else + int16_t v = phaseAddress(s_currIdx)->rotaryEncoders[t]; +#endif + if (v < ROTARY_ENCODER_MAX) v = ROTARY_ENCODER_MAX; + v = checkIncDec(event, v, ROTARY_ENCODER_MAX, ROTARY_ENCODER_MAX+MAX_PHASES-1, EE_MODEL); + if (checkIncDec_Ret) { + if (v == ROTARY_ENCODER_MAX) v = 0; +#if ROTARY_ENCODERS > 2 + if (t < (NUM_ROTARY_ENCODERS - NUM_ROTARY_ENCODERS_EXTRA)) + phaseAddress(s_currIdx)->rotaryEncoders[t] = v; + else + g_model.rotaryEncodersExtra[s_currIdx][t-(NUM_ROTARY_ENCODERS - NUM_ROTARY_ENCODERS_EXTRA)] = v; +#else + phaseAddress(s_currIdx)->rotaryEncoders[t] = v; +#endif + } + } + } + break; +#endif + + case ITEM_MODEL_PHASE_FADE_IN: + phase->fadeIn = EDIT_DELAY(0, y, event, attr, STR_FADEIN, phase->fadeIn); + break; + + case ITEM_MODEL_PHASE_FADE_OUT: + phase->fadeOut = EDIT_DELAY(0, y, event, attr, STR_FADEOUT, phase->fadeOut); + break; + +#if defined(GVARS) && !defined(PCBSTD) + case ITEM_MODEL_PHASE_GVARS_LABEL: + lcd_putsLeft(y, STR_GLOBAL_VARS); + break; + + default: + { + uint8_t idx = i-ITEM_MODEL_PHASE_GV1; + uint8_t posHorz = m_posHorz; + if (attr && posHorz > 0 && s_currIdx==0) posHorz++; + + putsStrIdx(INDENT_WIDTH, y, STR_GV, idx+1); + + editName(4*FW, y, g_model.gvars[idx].name, LEN_GVAR_NAME, event, posHorz==0 ? attr : 0); + + int16_t v = phase->gvars[idx]; + if (v > GVAR_MAX) { + uint8_t p = v - GVAR_MAX - 1; + if (p >= s_currIdx) p++; + putsFlightPhase(11*FW, y, p+1, posHorz==1 ? attr : 0); + } + else { + lcd_putsAtt(11*FW, y, STR_OWN, posHorz==1 ? attr : 0); + } + if (attr && s_currIdx>0 && posHorz==1 && (editMode>0 || p1valdiff)) { + if (v < GVAR_MAX) v = GVAR_MAX; + v = checkIncDec(event, v, GVAR_MAX, GVAR_MAX+MAX_PHASES-1, EE_MODEL); + if (checkIncDec_Ret) { + if (v == GVAR_MAX) v = 0; + phase->gvars[idx] = v; + } + } + + uint8_t p = getGVarFlightPhase(s_currIdx, idx); + lcd_outdezAtt(21*FW, y, GVAR_VALUE(idx, p), posHorz==2 ? attr : 0); + if (attr && posHorz==2 && ((editMode>0) || p1valdiff)) { + GVAR_VALUE(idx, p) = checkIncDec(event, GVAR_VALUE(idx, p), -GVAR_LIMIT, GVAR_LIMIT, EE_MODEL); + } + + break; + } +#endif + } + } +} + +#if defined(ROTARY_ENCODERS) + #if ROTARY_ENCODERS > 2 + #define NAME_OFS (-4-12) + #define SWITCH_OFS (-FW/2-2-13) + #define TRIMS_OFS (-FW/2-4-15) + #define ROTARY_ENC_OFS (0) + #else + #define NAME_OFS (-4) + #define SWITCH_OFS (-FW/2-2) + #define TRIMS_OFS (-FW/2-4) + #define ROTARY_ENC_OFS (2) + #endif +#else + #define NAME_OFS 0 + #define SWITCH_OFS (FW/2) + #define TRIMS_OFS (FW/2) +#endif + +void menuModelFlightModesAll(uint8_t event) +{ + SIMPLE_MENU(STR_MENUFLIGHTPHASES, menuTabModel, e_FlightModesAll, 1+MAX_PHASES+1); + + int8_t sub = m_posVert - 1; + + switch (event) { + CASE_EVT_ROTARY_BREAK + case EVT_KEY_FIRST(KEY_ENTER): + if (sub == MAX_PHASES) { + s_editMode = 0; + trimsCheckTimer = 200; // 2 seconds + } + // no break + case EVT_KEY_FIRST(KEY_RIGHT): + if (sub >= 0 && sub < MAX_PHASES) { + s_currIdx = sub; + pushMenu(menuModelPhaseOne); + } + break; + } + + uint8_t att; + for (uint8_t i=0; i(LCD_LINES-1)*FH+1) continue; +#else + uint8_t y = 1 + (i+1)*FH; +#endif + att = (i==sub ? INVERS : 0); + PhaseData *p = phaseAddress(i); +#if ROTARY_ENCODERS > 2 + putsFlightPhase(0, y, i+1, att|CONDENSED|(getFlightPhase()==i ? BOLD : 0)); +#else + putsFlightPhase(0, y, i+1, att|(getFlightPhase()==i ? BOLD : 0)); +#endif + + lcd_putsnAtt(4*FW+NAME_OFS, y, p->name, sizeof(p->name), ZCHAR); + if (i == 0) { + lcd_puts((5+LEN_FP_NAME)*FW+SWITCH_OFS, y, STR_DEFAULT); + } + else { + putsSwitches((5+LEN_FP_NAME)*FW+SWITCH_OFS, y, p->swtch, 0); + for (uint8_t t=0; tfadeIn || p->fadeOut) + lcd_putc(LCD_W-FW-MENUS_SCROLLBAR_WIDTH, y, (p->fadeIn && p->fadeOut) ? '*' : (p->fadeIn ? 'I' : 'O')); + + } + +#if defined(CPUARM) + if (s_pgOfs != MAX_PHASES-(LCD_LINES-2)) return; +#endif + + lcd_putsLeft((LCD_LINES-1)*FH+1, STR_CHECKTRIMS); + putsFlightPhase(OFS_CHECKTRIMS, (LCD_LINES-1)*FH+1, s_perout_flight_phase+1); + if (sub==MAX_PHASES && !trimsCheckTimer) { + lcd_status_line(); + } +} + +#endif // defined(PCBTARANIS) + +#else // defined(FLIGHT_MODES) + +#define displayFlightModes(...) + +#endif + +#if defined(HELI) + +enum menuModelHeliItems { + ITEM_HELI_SWASHTYPE, + ITEM_HELI_COLLECTIVE, + ITEM_HELI_SWASHRING, + ITEM_HELI_ELEDIRECTION, + ITEM_HELI_AILDIRECTION, + ITEM_HELI_COLDIRECTION +}; + +#if LCD_W >= 212 + #define HELI_PARAM_OFS (23*FW) +#else + #define HELI_PARAM_OFS (14*FW) +#endif + +void menuModelHeli(uint8_t event) +{ + SIMPLE_MENU(STR_MENUHELISETUP, menuTabModel, e_Heli, 7); + + uint8_t sub = m_posVert - 1; + + for (uint8_t i=0; i<6; i++) { + uint8_t y = 1 + 1*FH + i*FH; + uint8_t attr = (sub == i ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0); + switch(i) { + case ITEM_HELI_SWASHTYPE: + g_model.swashR.type = selectMenuItem(HELI_PARAM_OFS, y, STR_SWASHTYPE, STR_VSWASHTYPE, g_model.swashR.type, 0, SWASH_TYPE_NUM, attr, event); + break; + + case ITEM_HELI_COLLECTIVE: +#if defined(PCBTARANIS) + lcd_putsLeft(y, STR_COLLECTIVE); + if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH); +#else + g_model.swashR.collectiveSource = selectMenuItem(HELI_PARAM_OFS, y, STR_COLLECTIVE, NULL, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH, attr, event); +#endif + putsMixerSource(HELI_PARAM_OFS, y, g_model.swashR.collectiveSource, attr); + break; + + case ITEM_HELI_SWASHRING: + lcd_putsLeft(y, STR_SWASHRING); + lcd_outdezAtt(HELI_PARAM_OFS, y, g_model.swashR.value, LEFT|attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.swashR.value, 100); + break; + + case ITEM_HELI_ELEDIRECTION: + g_model.swashR.invertELE = selectMenuItem(HELI_PARAM_OFS, y, STR_ELEDIRECTION, STR_MMMINV, g_model.swashR.invertELE, 0, 1, attr, event); + break; + + case ITEM_HELI_AILDIRECTION: + g_model.swashR.invertAIL = selectMenuItem(HELI_PARAM_OFS, y, STR_AILDIRECTION, STR_MMMINV, g_model.swashR.invertAIL, 0, 1, attr, event); + break; + + case ITEM_HELI_COLDIRECTION: + g_model.swashR.invertCOL = selectMenuItem(HELI_PARAM_OFS, y, STR_COLDIRECTION, STR_MMMINV, g_model.swashR.invertCOL, 0, 1, attr, event); + break; + } + } +} +#endif + +typedef int16_t (*FnFuncP) (int16_t x); + +int16_t expoFn(int16_t x) +{ + ExpoData *ed = expoAddress(s_currIdx); + int16_t anas[NUM_INPUTS] = {0}; +#if defined(PCBTARANIS) + applyExpos(anas, e_perout_mode_inactive_phase, ed->srcRaw, x); +#else + anas[ed->chn] = x; + applyExpos(anas, e_perout_mode_inactive_phase); +#endif + return anas[ed->chn]; +} + +void DrawFunction(FnFuncP fn, uint8_t offset=0) +{ + lcd_vlineStip(X0-offset, 0, LCD_H, 0xee); + lcd_hlineStip(X0-WCHART-offset, Y0, WCHART*2, 0xee); + + uint8_t prev_yv = 255; + + for (int8_t xv=-WCHART; xv<=WCHART; xv++) { + uint8_t yv = (LCD_H-1) - (((uint16_t)RESX + fn(xv * (RESX/WCHART))) / 2 * (LCD_H-1) / RESX); + if (prev_yv != 255) { + if (abs((int8_t)yv-prev_yv) <= 1) { + lcd_plot(X0+xv-offset-1, prev_yv, FORCE); + } + else { + uint8_t tmp = (prev_yv < yv ? 0 : 1); + lcd_vline(X0+xv-offset-1, yv+tmp, prev_yv-yv); + } + } + prev_yv = yv; + } +} + +#if defined(CURVES) +static uint8_t s_curveChan; +int16_t curveFn(int16_t x) +{ + return applyCustomCurve(x, s_curveChan); +} + +struct point_t { + uint8_t x; + uint8_t y; +}; + +point_t getPoint(uint8_t i) +{ + point_t result = {0, 0}; +#if defined(PCBTARANIS) + CurveInfo &crv = g_model.curves[s_curveChan]; + int8_t *points = curveAddress(s_curveChan); + bool custom = (crv.type == CURVE_TYPE_CUSTOM); + uint8_t count = 5+crv.points; +#else + CurveInfo crv = curveInfo(s_curveChan); + int8_t *points = crv.crv; + bool custom = crv.custom; + uint8_t count = crv.points; +#endif + if (i < count) { +#if defined(PCBTARANIS) + result.x = X0-1-WCHART+i*WCHART*2/(count-1); +#else + result.x = X0-1-WCHART+i*WCHART/(count/2); +#endif + result.y = (LCD_H-1) - (100 + points[i]) * (LCD_H-1) / 200; + if (custom && i>0 && i g_model.points + sizeof(g_model.points)) { + AUDIO_WARNING2(); + return false; + } + + int8_t *nextCrv = curveAddress(index+1); + memmove(nextCrv+shift, nextCrv, 5*(MAX_CURVES-index-1)+curveEnd[MAX_CURVES-1]-curveEnd[index]); + if (shift < 0) memclear(&g_model.points[NUM_POINTS-1] + shift, -shift); + while (index NUM_POINTS-5*MAX_CURVES) { + AUDIO_WARNING2(); + return false; + } + + int8_t *crv = curveAddress(index); + if (shift < 0) { + for (uint8_t i=0; i0 ? INVERS|BLINK : INVERS) : 0); + lcd_putsLeft(3*FH+1, STR_TYPE); + lcd_putsiAtt(INDENT_WIDTH, 4*FH+1, "\010StandardCustom\0", crv.type, attr); + if (attr) { + uint8_t newType = checkIncDecModelZero(event, crv.type, CURVE_TYPE_LAST); + if (newType != crv.type) { + for (int i=1; i<4+crv.points; i++) + points[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + i*200/(4+crv.points)), s_curveChan)); + moveCurve(s_curveChan, checkIncDec_Ret > 0 ? 3+crv.points : -3-crv.points); + if (newType == CURVE_TYPE_CUSTOM) { + for (int i=0; i<3+crv.points; i++) + points[5+crv.points+i] = -100 + ((i+1)*200) / (4+crv.points); + } + crv.type = newType; + } + } + + attr = (m_posVert==2 ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0); + lcd_putsLeft(5*FH+1, "Count"); + lcd_outdezAtt(INDENT_WIDTH, 6*FH+1, 5+crv.points, LEFT|attr); + lcd_putsAtt(lcdLastPos, 6*FH+1, PSTR("pts"), attr); + if (attr) { + int8_t count = checkIncDecModel(event, crv.points, -3, 12); // 2pts - 17pts + if (checkIncDec_Ret) { + int8_t newPoints[MAX_POINTS]; + newPoints[0] = points[0]; + newPoints[4+count] = points[4+crv.points]; + for (int i=1; i<4+count; i++) + newPoints[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + (i*200) / (4+count)), s_curveChan)); + moveCurve(s_curveChan, checkIncDec_Ret*(crv.type==CURVE_TYPE_CUSTOM?2:1)); + for (int i=0; i<5+count; i++) { + points[i] = newPoints[i]; + if (crv.type == CURVE_TYPE_CUSTOM && i!=0 && i!=4+count) + points[5+count+i-1] = -100 + (i*200) / (4+count); + } + crv.points = count; + } + } + + lcd_putsLeft(7*FH+1, PSTR("Smooth")); + menu_lcd_onoff(7*FW, 7*FH+1, crv.smooth, m_posVert==3 ? INVERS : 0); + if (m_posVert==3) crv.smooth = checkIncDecModel(event, crv.smooth, 0, 1); + + switch(event) { + case EVT_ENTRY: + pointsOfs = 0; + SET_SCROLLBAR_X(0); + break; + case EVT_KEY_LONG(KEY_ENTER): + if (m_posVert > 1) { + killEvents(event); + MENU_ADD_ITEM(STR_CURVE_PRESET); + MENU_ADD_ITEM(STR_MIRROR); + MENU_ADD_ITEM(STR_CLEAR); + menuHandler = onCurveOneMenu; + } + break; + case EVT_KEY_LONG(KEY_MENU): + pushMenu(menuChannelsView); + killEvents(event); + return; + } + + DrawCurve(FW); + + uint8_t posY = FH+1; + attr = (s_editMode > 0 ? INVERS|BLINK : INVERS); + for (uint8_t i=0; i<5+crv.points; i++) { + point_t point = getPoint(i); + uint8_t selectionMode = 0; + if (crv.type==CURVE_TYPE_CUSTOM) { + if (m_posVert==4+2*i || (i==5+crv.points-1 && m_posVert==4+5+crv.points+5+crv.points-2-1)) + selectionMode = 2; + else if (i>0 && m_posVert==3+2*i) + selectionMode = 1; + } + else if (m_posVert == 4+i) { + selectionMode = 2; + } + + if (i>=pointsOfs && i0 && i<5+crv.points-1) x = points[5+crv.points+i-1]; + lcd_outdezAtt(6+8*FW, posY, i+1, LEFT); + lcd_outdezAtt(3+12*FW, posY, x, LEFT|(selectionMode==1?attr:0)); + lcd_outdezAtt(3+16*FW, posY, points[i], LEFT|(selectionMode==2?attr:0)); + posY += FH; + } + + if (selectionMode > 0) { + // do selection square + lcd_filled_rect(point.x-FW-1, point.y-2, 5, 5, SOLID, FORCE); + lcd_filled_rect(point.x-FW, point.y-1, 3, 3, SOLID); + if (s_editMode > 0) { + if (selectionMode == 1) + CHECK_INCDEC_MODELVAR(event, points[5+crv.points+i-1], i==1 ? -100 : points[5+crv.points+i-2], i==5+crv.points-2 ? 100 : points[5+crv.points+i]); // edit X + else if (selectionMode == 2) + CHECK_INCDEC_MODELVAR(event, points[i], -100, 100); + } + if (i < pointsOfs) + pointsOfs = i; + else if (i > pointsOfs+6) + pointsOfs = i-6; + } + } +} +#else +void menuModelCurveOne(uint8_t event) +{ + TITLE(STR_MENUCURVE); + lcd_outdezAtt(PSIZE(TR_MENUCURVE)*FW+1, 0, s_curveChan+1, INVERS|LEFT); + DISPLAY_PROGRESS_BAR(20*FW+1); + + CurveInfo crv = curveInfo(s_curveChan); + + switch(event) { + case EVT_ENTRY: + s_editMode = 1; + break; + CASE_EVT_ROTARY_BREAK + case EVT_KEY_BREAK(KEY_ENTER): + if (s_editMode <= 0) + m_posHorz = 0; + if (s_editMode == 1 && crv.custom) + s_editMode = 2; + else + s_editMode = 1; + break; + case EVT_KEY_LONG(KEY_ENTER): + if (s_editMode <= 0) { + if (int8_t(++m_posHorz) > 4) + m_posHorz = -4; + for (uint8_t i=0; i 0) { + if (--s_editMode == 0) + m_posHorz = 0; + } + else { + popMenu(); + } + break; + + /* CASE_EVT_ROTARY_LEFT */ + case EVT_KEY_REPT(KEY_LEFT): + case EVT_KEY_FIRST(KEY_LEFT): + if (s_editMode==1 && m_posHorz>0) m_posHorz--; + if (s_editMode <= 0) { + if (crv.custom) { + moveCurve(s_curveChan, -crv.points+2); + } + else if (crv.points > MIN_POINTS) { + moveCurve(s_curveChan, -1, (crv.points+1)/2); + } + else { + AUDIO_WARNING2(); + } + return; + } + break; + + /* CASE_EVT_ROTARY_RIGHT */ + case EVT_KEY_REPT(KEY_RIGHT): + case EVT_KEY_FIRST(KEY_RIGHT): + if (s_editMode==1 && m_posHorz<(crv.points-1)) m_posHorz++; + if (s_editMode <= 0) { + if (!crv.custom) { + moveCurve(s_curveChan, crv.points-2, crv.points); + } + else if (crv.points < MAX_POINTS) { + if (moveCurve(s_curveChan, 1)) { + for (int8_t i=crv.points+crv.points-2; i>=0; i--) { + if (i%2) + crv.crv[i] = (crv.crv[i/2] + crv.crv[1+i/2]) / 2; + else + crv.crv[i] = crv.crv[i/2]; + } + } + } + else { + AUDIO_WARNING2(); + } + } + break; + } + + lcd_putsLeft(7*FH, STR_TYPE); + uint8_t attr = (s_editMode <= 0 ? INVERS : 0); + lcd_outdezAtt(5*FW-2, 7*FH, crv.points, LEFT|attr); + lcd_putsAtt(lcdLastPos, 7*FH, crv.custom ? PSTR("pt'") : PSTR("pt"), attr); + + DrawCurve(); + + if (s_editMode>0) { + uint8_t i = m_posHorz; + point_t point = getPoint(i); + + if (s_editMode==1 || !BLINK_ON_PHASE) { + // do selection square + lcd_filled_rect(point.x-1, point.y-2, 5, 5, SOLID, FORCE); + lcd_filled_rect(point.x, point.y-1, 3, 3, SOLID); + } + + int8_t x = -100 + 200*i/(crv.points-1); + if (crv.custom && i>0 && i=0; i--) { + ch = (expo ? EXPO_VALID(expoAddress(i)) : mixAddress(i)->srcRaw); + if (ch != 0) { + count++; + } + } + return count; +} + +bool reachExpoMixCountLimit(uint8_t expo) +{ + // check mixers count limit + if (getExpoMixCount(expo) >= (expo ? MAX_EXPOS : MAX_MIXERS)) { + POPUP_WARNING(expo ? STR_NOFREEEXPO : STR_NOFREEMIXER); + return true; + } + return false; +} + +void deleteExpoMix(uint8_t expo, uint8_t idx) +{ + pauseMixerCalculations(); + if (expo) { + ExpoData *expo = expoAddress(idx); + memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + memclear(&g_model.expoData[MAX_EXPOS-1], sizeof(ExpoData)); + } + else { + MixData *mix = mixAddress(idx); + memmove(mix, mix+1, (MAX_MIXERS-(idx+1))*sizeof(MixData)); + memclear(&g_model.mixData[MAX_MIXERS-1], sizeof(MixData)); + } + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +// TODO avoid this global s_currCh on ARM boards ... +int8_t s_currCh; +void insertExpoMix(uint8_t expo, uint8_t idx) +{ + pauseMixerCalculations(); + if (expo) { + ExpoData *expo = expoAddress(idx); + memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + memclear(expo, sizeof(ExpoData)); +#if defined(PCBTARANIS) + expo->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); + expo->curve.type = CURVE_REF_EXPO; +#else + expo->mode = 3; // pos&neg +#endif + expo->chn = s_currCh - 1; + expo->weight = 100; + } + else { + MixData *mix = mixAddress(idx); + memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); + memclear(mix, sizeof(MixData)); + mix->destCh = s_currCh-1; +#if defined(PCBTARANIS) + mix->srcRaw = s_currCh; + if (!isSourceAvailable(mix->srcRaw)) + mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); +#else + mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh)); +#endif + mix->weight = 100; + } + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +void copyExpoMix(uint8_t expo, uint8_t idx) +{ + pauseMixerCalculations(); + if (expo) { + ExpoData *expo = expoAddress(idx); + memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + } + else { + MixData *mix = mixAddress(idx); + memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); + } + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +void memswap(void *a, void *b, uint8_t size) +{ + uint8_t *x = (uint8_t*)a; + uint8_t *y = (uint8_t*)b; + uint8_t temp ; + + while (size--) { + temp = *x; + *x++ = *y; + *y++ = temp; + } +} + +bool swapExpoMix(uint8_t expo, uint8_t &idx, uint8_t up) +{ + void *x, *y; + uint8_t size; + int8_t tgt_idx = (up ? idx-1 : idx+1); + + if (expo) { + x = (ExpoData *)expoAddress(idx); + + if (tgt_idx < 0) { + if (((ExpoData *)x)->chn == 0) + return false; + ((ExpoData *)x)->chn--; + return true; + } + + if (tgt_idx == MAX_EXPOS) { + if (((ExpoData *)x)->chn == NUM_STICKS-1) + return false; + ((ExpoData *)x)->chn++; + return true; + } + + y = (ExpoData *)expoAddress(tgt_idx); + if(((ExpoData *)x)->chn != ((ExpoData *)y)->chn || !EXPO_VALID((ExpoData *)y)) { + if (up) { + if (((ExpoData *)x)->chn>0) ((ExpoData *)x)->chn--; + else return false; + } + else { + if (((ExpoData *)x)->chnchn++; + else return false; + } + return true; + } + + size = sizeof(ExpoData); + } + else { + x = (MixData *)mixAddress(idx); + + if (tgt_idx < 0) { + if (((MixData *)x)->destCh == 0) + return false; + ((MixData *)x)->destCh--; + return true; + } + + if (tgt_idx == MAX_MIXERS) { + if (((MixData *)x)->destCh == NUM_CHNOUT-1) + return false; + ((MixData *)x)->destCh++; + return true; + } + + y = (MixData *)mixAddress(tgt_idx); + uint8_t destCh = ((MixData *)x)->destCh; + if(!((MixData *)y)->srcRaw || destCh != ((MixData *)y)->destCh) { + if (up) { + if (destCh>0) ((MixData *)x)->destCh--; + else return false; + } + else { + if (destChdestCh++; + else return false; + } + return true; + } + + size = sizeof(MixData); + } + + pauseMixerCalculations(); + memswap(x, y, size); + resumeMixerCalculations(); + + idx = tgt_idx; + return true; +} + +enum ExposFields { + CASE_PCBTARANIS(EXPO_FIELD_INPUT_NAME) + IF_CPUARM(EXPO_FIELD_NAME) + CASE_PCBTARANIS(EXPO_FIELD_SOURCE) + CASE_PCBTARANIS(EXPO_FIELD_SCALE) + EXPO_FIELD_WEIGHT, + CASE_PCBTARANIS(EXPO_FIELD_OFFSET) + CASE_9X(EXPO_FIELD_EXPO) + IF_CURVES(EXPO_FIELD_CURVE) + IF_FLIGHT_MODES(EXPO_FIELD_FLIGHT_PHASE) + EXPO_FIELD_SWITCH, + CASE_9X(EXPO_FIELD_SIDE) + CASE_PCBTARANIS(EXPO_FIELD_TRIM) + EXPO_FIELD_MAX +}; + +#if defined(PCBTARANIS) + #define CURVE_ROWS 1 +#else + #define CURVE_ROWS 0 +#endif + +void menuModelExpoOne(uint8_t event) +{ +#if defined(PCBTARANIS) + if (event == EVT_KEY_LONG(KEY_MENU)) { + pushMenu(menuChannelsView); + killEvents(event); + return; + } +#endif + + ExpoData *ed = expoAddress(s_currIdx); +#if defined(PCBTARANIS) + putsMixerSource(7*FW+FW/2, 0, MIXSRC_FIRST_INPUT+ed->chn, 0); +#else + putsMixerSource(7*FW+FW/2, 0, MIXSRC_Rud+ed->chn, 0); +#endif + + SUBMENU(STR_MENUINPUTS, EXPO_FIELD_MAX, {CASE_PCBTARANIS(0) IF_CPUARM(0) CASE_PCBTARANIS(0) CASE_PCBTARANIS((ed->srcRaw >= MIXSRC_FIRST_TELEM ? (uint8_t)0 : (uint8_t)HIDDEN_ROW)) 0, CASE_PCBTARANIS(0) CASE_9X(0) IF_CURVES(CURVE_ROWS) IF_FLIGHT_MODES((MAX_PHASES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/}); + + SET_SCROLLBAR_X(EXPO_ONE_2ND_COLUMN+10*FW); + + int8_t sub = m_posVert; + + uint8_t y = FH+1; + +#if defined(PCBTARANIS) + for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); + switch(i) + { +#if defined(PCBTARANIS) + case EXPO_FIELD_INPUT_NAME: + editSingleName(EXPO_ONE_2ND_COLUMN, y, "Input Name", g_model.inputNames[ed->chn], sizeof(g_model.inputNames[ed->chn]), event, attr); + break; +#endif + +#if defined(CPUARM) + case EXPO_FIELD_NAME: + editSingleName(EXPO_ONE_2ND_COLUMN-IF_9X(sizeof(ed->name)*FW), y, STR_EXPONAME, ed->name, sizeof(ed->name), event, attr); + break; +#endif + +#if defined(PCBTARANIS) + case EXPO_FIELD_SOURCE: + lcd_putsLeft(y, NO_INDENT(STR_SOURCE)); + putsMixerSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, STREXPANDED|attr); + if (attr) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isInputSourceAvailable); + break; + + case EXPO_FIELD_SCALE: + lcd_putsLeft(y, STR_SCALE); + putsTelemetryChannel(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw - MIXSRC_FIRST_TELEM, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|attr); + if (attr) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL, NULL); + break; +#endif + + case EXPO_FIELD_WEIGHT: + lcd_putsLeft(y, STR_WEIGHT); + ed->weight = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->weight, MIN_EXPO_WEIGHT, 100, IF_PCBTARANIS(LEFT)|attr, 0, event); + break; + +#if defined(PCBTARANIS) + case EXPO_FIELD_OFFSET: + lcd_putsLeft(y, NO_INDENT(STR_OFFSET)); + ed->offset = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->offset, -100, 100, LEFT|attr, 0, event); + break; +#endif + +#if !defined(PCBTARANIS) + case EXPO_FIELD_EXPO: + lcd_putsLeft(y, STR_EXPO); + if (ed->curveMode==MODE_EXPO || ed->curveParam==0) { + ed->curveMode = MODE_EXPO; + ed->curveParam = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->curveParam, -100, 100, attr, 0, event); + } + else { + lcd_putsAtt(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); + } + break; +#endif + +#if defined(CURVES) + case EXPO_FIELD_CURVE: + lcd_putsLeft(y, STR_CURVE); +#if defined(PCBTARANIS) + editCurveRef(EXPO_ONE_2ND_COLUMN, y, ed->curve, event, attr); +#else + if (ed->curveMode!=MODE_EXPO || ed->curveParam==0) { + putsCurve(EXPO_ONE_2ND_COLUMN-3*FW, y, ed->curveParam, attr); + if (attr) { + CHECK_INCDEC_MODELVAR_ZERO(event, ed->curveParam, CURVE_BASE+MAX_CURVES-1); + if (ed->curveParam) ed->curveMode = MODE_CURVE; + if (ed->curveParam>=CURVE_BASE && event==EVT_KEY_LONG(KEY_ENTER)) { + s_curveChan = ed->curveParam - CURVE_BASE; + pushMenu(menuModelCurveOne); + } + } + } + else { + lcd_putsAtt(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); + } +#endif + break; +#endif + +#if defined(FLIGHT_MODES) + case EXPO_FIELD_FLIGHT_PHASE: + ed->phases = editFlightModes(EXPO_ONE_2ND_COLUMN-IF_9X(EXPO_ONE_FP_WIDTH), y, event, ed->phases, attr); + break; +#endif + + case EXPO_FIELD_SWITCH: + ed->swtch = switchMenuItem(EXPO_ONE_2ND_COLUMN-IF_9X(3*FW), y, ed->swtch, attr, event); + break; + +#if !defined(PCBTARANIS) + case EXPO_FIELD_SIDE: + ed->mode = 4 - selectMenuItem(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_SIDE, STR_VSIDE, 4-ed->mode, 1, 3, attr, event); + break; +#endif + +#if defined(PCBTARANIS) + case EXPO_FIELD_TRIM: + uint8_t not_stick = (ed->srcRaw > MIXSRC_Ail); + int8_t carryTrim = -ed->carryTrim; + lcd_putsLeft(y, STR_TRIM); + lcd_putsiAtt(EXPO_ONE_2ND_COLUMN, y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, m_posHorz==0 ? attr : 0); + if (attr) ed->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_AIL); + break; +#endif + } + y += FH; + } + + DrawFunction(expoFn); + +#if defined(PCBTARANIS) + int x512 = getValue(ed->srcRaw); + if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { + putsTelemetryChannel(LCD_W-8, 6*FH, ed->srcRaw - MIXSRC_FIRST_TELEM, x512, 0); + if (ed->scale > 0) x512 = (x512 * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale); + x512 = limit(-1024, x512, 1024); + } + else { + lcd_outdezAtt(LCD_W-8, 6*FH, calcRESXto1000(x512), PREC1); + } + int y512 = expoFn(x512); + lcd_outdezAtt(LCD_W-8-6*FW, 1*FH, calcRESXto1000(y512), PREC1); +#else + int16_t x512 = calibratedStick[ed->chn]; + lcd_outdezAtt(LCD_W-8, 6*FH, calcRESXto100(x512), 0); + int16_t y512 = expoFn(x512); + lcd_outdezAtt(LCD_W-8-6*FW, 1*FH, calcRESXto100(y512), 0); +#endif + + x512 = X0+x512/(RESXu/WCHART); +#if defined(CPUARM) + y512 = (LCD_H-1) - ((y512+RESX)/2) * (LCD_H-1) / RESX; +#else + y512 = (LCD_H-1) - (uint16_t)((y512+RESX)/2) * (LCD_H-1) / RESX; +#endif + + lcd_vline(x512, y512-3, 3*2+1); + lcd_hline(x512-3, y512, 3*2+1); +} + +enum MixFields { + IF_CPUARM(MIX_FIELD_NAME) + MIX_FIELD_SOURCE, + MIX_FIELD_WEIGHT, + MIX_FIELD_OFFSET, + CASE_9X(MIX_FIELD_TRIM) + IF_CURVES(MIX_FIELD_CURVE) + IF_FLIGHT_MODES(MIX_FIELD_FLIGHT_PHASE) + MIX_FIELD_SWITCH, + MIX_FIELD_WARNING, + MIX_FIELD_MLTPX, + MIX_FIELD_DELAY_UP, + MIX_FIELD_DELAY_DOWN, + MIX_FIELD_SLOW_UP, + MIX_FIELD_SLOW_DOWN, + MIX_FIELD_COUNT +}; + +void gvarWeightItem(xcoord_t x, uint8_t y, MixData *md, uint8_t attr, uint8_t event) +{ + u_int8int16_t weight; + MD_WEIGHT_TO_UNION(md, weight); + weight.word = GVAR_MENU_ITEM(x, y, weight.word, -500, 500, attr, 0, event); + MD_UNION_TO_WEIGHT(weight, md); +} + +void menuModelMixOne(uint8_t event) +{ +#if defined(PCBTARANIS) + if (event == EVT_KEY_LONG(KEY_MENU)) { + pushMenu(menuChannelsView); + killEvents(event); + return; + } +#endif + + TITLE(s_currCh ? STR_INSERTMIX : STR_EDITMIX); + MixData *md2 = mixAddress(s_currIdx) ; + putsChn(lcdLastPos+1*FW, 0, md2->destCh+1,0); + +#if defined(ROTARY_ENCODERS) +#if defined(CURVES) + if ((m_posVert == MIX_FIELD_TRIM && md2->srcRaw > NUM_STICKS) || (m_posVert == MIX_FIELD_CURVE && md2->curveMode == MODE_CURVE)) +#else + if (m_posVert == MIX_FIELD_TRIM && md2->srcRaw > NUM_STICKS) +#endif + SUBMENU_NOTITLE(MIX_FIELD_COUNT, {IF_CPUARM(0) 0, 0, 0, CASE_9X(0) IF_CURVES(0) IF_FLIGHT_MODES((MAX_PHASES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}) + else + SUBMENU_NOTITLE(MIX_FIELD_COUNT, {IF_CPUARM(0) 0, 0, 0, CASE_9X(1) IF_CURVES(1) IF_FLIGHT_MODES((MAX_PHASES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}); +#else + SUBMENU_NOTITLE(MIX_FIELD_COUNT, {IF_CPUARM(0) 0, 0, 0, CASE_9X(1) IF_CURVES(1) IF_FLIGHT_MODES((MAX_PHASES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}); +#endif + +#if MENU_COLUMNS > 1 + lcd_vline(MENU_COLUMN2_X-4, FH+1, LCD_H-FH-1); +#endif + + int8_t sub = m_posVert; + int8_t editMode = s_editMode; + + for (uint8_t k=0; k 1 + uint8_t y; + uint8_t COLUMN_X; + if (k >= LCD_LINES-1) { + y = 1 + (k-LCD_LINES+2)*FH; + COLUMN_X = MENU_COLUMN2_X; + } + else { + y = 1 + (k+1)*FH; + COLUMN_X = 0; + } + int8_t i = k; +#else + uint8_t y = 1 + (k+1)*FH; + int8_t i = k + s_pgOfs; +#endif + + uint8_t attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0); + switch(i) { +#if defined(CPUARM) + case MIX_FIELD_NAME: + editSingleName(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXNAME, md2->name, sizeof(md2->name), event, attr); + break; +#endif + case MIX_FIELD_SOURCE: + lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_SOURCE)); + putsMixerSource(COLUMN_X+MIXES_2ND_COLUMN, y, md2->srcRaw, STREXPANDED|attr); + if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST); + break; + case MIX_FIELD_WEIGHT: + lcd_putsColumnLeft(COLUMN_X, y, STR_WEIGHT); + gvarWeightItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2, attr|LEFT, event); + break; + case MIX_FIELD_OFFSET: + { + lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_OFFSET)); + u_int8int16_t offset; + MD_OFFSET_TO_UNION(md2, offset); + offset.word = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, GV_RANGELARGE_NEG, GV_RANGELARGE, attr|LEFT, 0, event); + MD_UNION_TO_OFFSET(offset, md2); + break; + } + +#if !defined(PCBTARANIS) + case MIX_FIELD_TRIM: + { + uint8_t not_stick = (md2->srcRaw > NUM_STICKS); + int8_t carryTrim = -md2->carryTrim; + lcd_putsColumnLeft(COLUMN_X, y, STR_TRIM); +#if LCD_W >= 212 && defined(TRANSLATIONS_FR) + lcd_putsiAtt((not_stick ? COLUMN_X+MIXES_2ND_COLUMN : COLUMN_X+11*FW-3), y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, m_posHorz==0 ? attr : 0); +#else + lcd_putsiAtt((not_stick ? COLUMN_X+MIXES_2ND_COLUMN : COLUMN_X+6*FW-3), y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, m_posHorz==0 ? attr : 0); +#endif + if (attr && m_posHorz==0 && (not_stick || editMode>0)) md2->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_AIL); + if (!not_stick) { + lcd_puts(COLUMN_X+MIXES_2ND_COLUMN, y, STR_DREX); + menu_lcd_onoff(COLUMN_X+MIXES_2ND_COLUMN+5*FW, y, !md2->noExpo, m_posHorz==1 ? attr : 0); + if (attr && m_posHorz==1 && editMode>0) md2->noExpo = !checkIncDecModel(event, !md2->noExpo, 0, 1); + } + else if (attr) { + REPEAT_LAST_CURSOR_MOVE(); + } + break; + } +#endif + +#if defined(CURVES) + case MIX_FIELD_CURVE: + { + lcd_putsColumnLeft(COLUMN_X, y, STR_CURVE); +#if defined(PCBTARANIS) + editCurveRef(COLUMN_X+MIXES_2ND_COLUMN, y, md2->curve, event, attr); +#else + int8_t curveParam = md2->curveParam; + if (md2->curveMode == MODE_CURVE) { + putsCurve(COLUMN_X+MIXES_2ND_COLUMN, y, curveParam, attr); + if (attr) { + if (event==EVT_KEY_LONG(KEY_ENTER) && (curveParam<0 || curveParam>=CURVE_BASE)){ + s_curveChan = (curveParam<0 ? -curveParam-1 : curveParam-CURVE_BASE); + pushMenu(menuModelCurveOne); + } + else { + CHECK_INCDEC_MODELVAR(event, md2->curveParam, -MAX_CURVES, CURVE_BASE+MAX_CURVES-1); + if (md2->curveParam == 0) + md2->curveMode = MODE_DIFFERENTIAL; +#if defined(ROTARY_ENCODER_NAVIGATION) || defined(PCBTARANIS) + MOVE_CURSOR_FROM_HERE(); +#else + m_posHorz = 0; +#endif + } + } + } + else { + lcd_putsAtt(COLUMN_X+MIXES_2ND_COLUMN, y, PSTR("Diff"), m_posHorz==0 ? attr : 0); + md2->curveParam = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN+5*FW, y, curveParam, -100, 100, LEFT|(m_posHorz==1 ? attr : 0), 0, editMode>0 ? event : 0); + if (attr && editMode>0 && m_posHorz==0) { + int8_t tmp = 0; + CHECK_INCDEC_MODELVAR(event, tmp, -1, 1); + if (tmp != 0) { + md2->curveMode = MODE_CURVE; + md2->curveParam = tmp; + } + } + } +#endif + break; + } +#endif +#if defined(FLIGHT_MODES) + case MIX_FIELD_FLIGHT_PHASE: + md2->phases = editFlightModes(COLUMN_X+MIXES_2ND_COLUMN, y, event, md2->phases, attr); + break; +#endif + case MIX_FIELD_SWITCH: + md2->swtch = switchMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->swtch, attr, event); + break; + case MIX_FIELD_WARNING: + lcd_putsColumnLeft(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXWARNING); + if (md2->mixWarn) + lcd_outdezAtt(COLUMN_X+MIXES_2ND_COLUMN, y, md2->mixWarn, attr|LEFT); + else + lcd_putsAtt(COLUMN_X+MIXES_2ND_COLUMN, y, STR_OFF, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, md2->mixWarn, 3); + break; + case MIX_FIELD_MLTPX: + md2->mltpx = selectMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MULTPX, STR_VMLTPX, md2->mltpx, 0, 2, attr, event); + break; + case MIX_FIELD_DELAY_UP: + md2->delayUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYUP, md2->delayUp); + break; + case MIX_FIELD_DELAY_DOWN: + md2->delayDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYDOWN, md2->delayDown); + break; + case MIX_FIELD_SLOW_UP: + md2->speedUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWUP, md2->speedUp); + break; + case MIX_FIELD_SLOW_DOWN: + md2->speedDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWDOWN, md2->speedDown); + break; + } + } +} + +static uint8_t s_maxLines = 8; +static uint8_t s_copySrcIdx; +static uint8_t s_copySrcCh; + +#define _STR_MAX(x) PSTR("/" #x) +#define STR_MAX(x) _STR_MAX(x) + +#define MIX_LINE_SRC_POS 4*FW-1 + +#if LCD_W >= 212 + #define EXPO_LINE_WEIGHT_POS 8*FW+1 + #define EXPO_LINE_SRC_POS 9*FW-2 + #define EXPO_LINE_CURVE_POS 12*FW+4 + #define EXPO_LINE_SWITCH_POS 17*FW-1 + #define EXPO_LINE_SELECT_POS 5*FW+2 + #define EXPO_LINE_FM_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH-FW-1 + #define EXPO_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH + #define MIX_LINE_WEIGHT_POS 11*FW+5 + #define MIX_LINE_CURVE_POS 12*FW+4 + #define MIX_LINE_SWITCH_POS 16*FW+1 + #define MIX_LINE_DELAY_POS 19*FW+2 +#elif defined(CPUARM) + #define EXPO_LINE_WEIGHT_POS 7*FW-1 + #define EXPO_LINE_EXPO_POS 10*FW+5 + #define EXPO_LINE_SWITCH_POS 11*FW+2 + #define EXPO_LINE_SIDE_POS 14*FW+2 + #define EXPO_LINE_SELECT_POS 24 + #define EXPO_LINE_FM_POS + #define EXPO_LINE_NAME_POS LCD_W-sizeof(ed->name)*FW-MENUS_SCROLLBAR_WIDTH + #define MIX_LINE_WEIGHT_POS 11*FW+3 + #define MIX_LINE_CURVE_POS 12*FW+2 + #define MIX_LINE_SWITCH_POS 16*FW + #define MIX_LINE_DELAY_POS 19*FW+7 +#else + #define EXPO_LINE_WEIGHT_POS 7*FW-1 + #define EXPO_LINE_EXPO_POS 11*FW + #define EXPO_LINE_SWITCH_POS 11*FW+4 + #if MAX_PHASES == 6 + #define EXPO_LINE_SIDE_POS 15*FW + #else + #define EXPO_LINE_SIDE_POS 15*FW+2 + #endif + #define EXPO_LINE_FM_POS LCD_W-FW-MENUS_SCROLLBAR_WIDTH + #define EXPO_LINE_SELECT_POS 24 + #define MIX_LINE_WEIGHT_POS 11*FW+3 + #define MIX_LINE_CURVE_POS 12*FW+2 + #define MIX_LINE_SWITCH_POS 16*FW + #define MIX_LINE_DELAY_POS 19*FW+7 +#endif + +#if defined(NAVIGATION_MENUS) +void onExpoMixMenu(const char *result) +{ + bool expo = (g_menuStack[g_menuStackPtr] == menuModelExposAll); + uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); + + if (result == STR_EDIT) { + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + } + else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) { + if (!reachExpoMixCountLimit(expo)) { + s_currCh = chn; + if (result == STR_INSERT_AFTER) { s_currIdx++; m_posVert++; } + insertExpoMix(expo, s_currIdx); + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + } + } + else if (result == STR_COPY || result == STR_MOVE) { + s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE); + s_copySrcIdx = s_currIdx; + s_copySrcCh = chn; + s_copySrcRow = m_posVert; + } + else if (result == STR_DELETE) { + deleteExpoMix(expo, s_currIdx); + } +} +#endif + +#if LCD_W >= 212 +void displayHeaderChannelName(uint8_t ch) +{ + uint8_t len = zlen(g_model.limitData[ch-1].name, sizeof(g_model.limitData[ch-1].name)); + if (len) { + lcd_putc(17*FW, 0, ' '); + lcd_putsnAtt(18*FW, 0, g_model.limitData[ch-1].name, len, ZCHAR); + lcd_putc(18*FW+len*FW, 0, ' '); + } +} +#endif + +void menuModelExpoMix(uint8_t expo, uint8_t event) +{ + uint8_t sub = m_posVert; + + if (s_editMode > 0) + s_editMode = 0; + + uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); + + switch (event) + { + case EVT_ENTRY: + case EVT_ENTRY_UP: + s_copyMode = 0; + s_copyTgtOfs = 0; + break; + case EVT_KEY_LONG(KEY_EXIT): + if (s_copyMode && s_copyTgtOfs == 0) { + deleteExpoMix(expo, s_currIdx); + killEvents(event); + event = 0; + } + // no break +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LONG: + if (s_copyMode) { + killEvents(event); + } +#endif + case EVT_KEY_BREAK(KEY_EXIT): + if (s_copyMode) { + if (s_copyTgtOfs) { + // cancel the current copy / move operation + if (s_copyMode == COPY_MODE) { + deleteExpoMix(expo, s_currIdx); + } + else { + do { + swapExpoMix(expo, s_currIdx, s_copyTgtOfs > 0); + s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1); + } while (s_copyTgtOfs != 0); + eeDirty(EE_MODEL); + } + m_posVert = s_copySrcRow; + s_copyTgtOfs = 0; + } + s_copyMode = 0; + event = 0; + } + break; + case EVT_KEY_BREAK(KEY_ENTER): + if (sub != 0 && (!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) { + s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); + s_copySrcIdx = s_currIdx; + s_copySrcCh = chn; + s_copySrcRow = sub; + break; + } + // no break + + CASE_EVT_ROTARY_BREAK + case EVT_KEY_LONG(KEY_ENTER): + killEvents(event); + if (s_copyTgtOfs) { + s_copyMode = 0; + s_copyTgtOfs = 0; + } + else if (sub != 0) { + if (READ_ONLY()) { + if (!s_currCh) { + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + return; + } + } + else { + if (s_copyMode) s_currCh = 0; +#if defined(NAVIGATION_MENUS) + if (s_currCh) { + if (reachExpoMixCountLimit(expo)) break; + insertExpoMix(expo, s_currIdx); + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + s_copyMode = 0; + return; + } + else { + event = 0; + s_copyMode = 0; + MENU_ADD_ITEM(STR_EDIT); + MENU_ADD_ITEM(STR_INSERT_BEFORE); + MENU_ADD_ITEM(STR_INSERT_AFTER); + MENU_ADD_ITEM(STR_COPY); + MENU_ADD_ITEM(STR_MOVE); + MENU_ADD_ITEM(STR_DELETE); + menuHandler = onExpoMixMenu; + } +#else + if (s_currCh) { + if (reachExpoMixCountLimit(expo)) break; + insertExpoMix(expo, s_currIdx); + } + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + s_copyMode = 0; + return; +#endif + } + } + break; + case EVT_KEY_LONG(KEY_LEFT): + case EVT_KEY_LONG(KEY_RIGHT): + if (s_copyMode && !s_copyTgtOfs) { + if (reachExpoMixCountLimit(expo)) break; + s_currCh = chn; + if (event == EVT_KEY_LONG(KEY_RIGHT)) { s_currIdx++; m_posVert++; } + insertExpoMix(expo, s_currIdx); + pushMenu(expo ? menuModelExpoOne : menuModelMixOne); + s_copyMode = 0; + killEvents(event); + return; + } + break; +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_LEFT: + case EVT_ROTARY_RIGHT: +#endif + case EVT_KEY_FIRST(KEY_MOVE_UP): + case EVT_KEY_REPT(KEY_MOVE_UP): + case EVT_KEY_FIRST(KEY_MOVE_DOWN): + case EVT_KEY_REPT(KEY_MOVE_DOWN): + if (s_copyMode) { + uint8_t key = (event & 0x1f); + uint8_t next_ofs = ((IS_ROTARY_UP(event) || key==KEY_MOVE_UP) ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1); + + if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) { + // insert a mix on the same channel (just above / just below) + if (reachExpoMixCountLimit(expo)) break; + copyExpoMix(expo, s_currIdx); + if (IS_ROTARY_DOWN(event) || key==KEY_MOVE_DOWN) s_currIdx++; + else if (sub-s_pgOfs >= 6) s_pgOfs++; + } + else if (next_ofs==0 && s_copyMode==COPY_MODE) { + // delete the mix + deleteExpoMix(expo, s_currIdx); + if (IS_ROTARY_UP(event) || key==KEY_MOVE_UP) s_currIdx--; + } + else { + // only swap the mix with its neighbor + if (!swapExpoMix(expo, s_currIdx, IS_ROTARY_UP(event) || key==KEY_MOVE_UP)) break; + eeDirty(EE_MODEL); + } + + s_copyTgtOfs = next_ofs; + } + break; + } + + lcd_outdezAtt(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER))+FW+FW/2, 0, getExpoMixCount(expo)); + lcd_puts(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXER))+FW+FW/2, 0, expo ? STR_MAX(MAX_EXPOS) : STR_MAX(MAX_MIXERS)); + SIMPLE_MENU(expo ? STR_MENUINPUTS : STR_MIXER, menuTabModel, expo ? e_InputsAll : e_MixAll, s_maxLines); + + sub = m_posVert; + s_currCh = 0; + uint8_t cur = 1; + uint8_t i = 0; + + for (uint8_t ch=1; ch<=(expo ? NUM_INPUTS : NUM_CHNOUT); ch++) { + void *pointer = NULL; MixData * &md = (MixData * &)pointer; ExpoData * &ed = (ExpoData * &)pointer; + uint8_t y = 1+(cur-s_pgOfs)*FH; + if (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)) { + if (s_pgOfs < cur && cur-s_pgOfs < LCD_LINES) { + if (expo) { +#if defined(PCBTARANIS) + putsMixerSource(0, y, ch, 0); +#else + putsMixerSource(0, y, MIXSRC_Rud+ch-1, 0); +#endif + } + else { + putsChn(0, y, ch, 0); // show CHx + } + } + uint8_t mixCnt = 0; + do { + if (s_copyMode) { + if (s_copyMode == MOVE_MODE && s_pgOfs < cur && cur-s_pgOfs < 8 && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { + lcd_rect(expo ? 18 : 22, y-1, expo ? LCD_W-18 : LCD_W-22, 9, DOTTED); + cur++; y+=FH; + } + if (s_currIdx == i) { + sub = m_posVert = cur; + s_currCh = ch; + } + } + else if (sub == cur) { + s_currIdx = i; + } + if (s_pgOfs < cur && cur-s_pgOfs < 8) { + uint8_t attr = ((s_copyMode || sub != cur) ? 0 : INVERS); + if (expo) { + ed->weight = GVAR_MENU_ITEM(EXPO_LINE_WEIGHT_POS, y, ed->weight, MIN_EXPO_WEIGHT, 100, attr | (isExpoActive(i) ? BOLD : 0), 0, event); + +#if defined(PCBTARANIS) + putsMixerSource(EXPO_LINE_SRC_POS, y, ed->srcRaw, 0); +#endif + +#if defined(PCBTARANIS) + putsCurveRef(EXPO_LINE_CURVE_POS, y, ed->curve, 0); +#else + if (ed->curveMode == MODE_CURVE) + putsCurve(EXPO_LINE_EXPO_POS-3*FW, y, ed->curveParam); + else + displayGVar(EXPO_LINE_EXPO_POS, y, ed->curveParam, -100, 100); +#endif + + putsSwitches(EXPO_LINE_SWITCH_POS, y, ed->swtch, 0); + +#if !defined(PCBTARANIS) + if (ed->mode!=3) lcd_putc(EXPO_LINE_SIDE_POS, y, ed->mode == 2 ? 126 : 127); +#endif + +#if defined(CPUARM) && LCD_W >= 212 + displayFlightModes(EXPO_LINE_FM_POS, y, ed->phases); + if (ed->name[0]) lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR | (isExpoActive(i) ? BOLD : 0)); +#elif defined(CPUARM) + if (ed->name[0]) lcd_putsnAtt(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR | (isExpoActive(i) ? BOLD : 0)); +#else + displayFlightModes(EXPO_LINE_FM_POS, y, ed->phases); +#endif + } + else { +#if LCD_W >= 212 + if (attr) { + displayHeaderChannelName(ch); + } +#endif + + if (mixCnt > 0) lcd_putsiAtt(FW, y, STR_VMLTPX2, md->mltpx, 0); + + putsMixerSource(MIX_LINE_SRC_POS, y, md->srcRaw, isMixActive(i) ? BOLD : 0); + + gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, attr, event); + +#if LCD_W >= 212 + displayFlightModes(EXPO_LINE_FM_POS, y, md->phases); +#endif + +#if defined(CPUARM) + if (md->name[0]) { + lcd_putsnAtt(EXPO_LINE_NAME_POS, y, md->name, sizeof(md->name), ZCHAR | (isMixActive(i) ? BOLD : 0)); + } +#if LCD_W < 212 + else +#endif +#endif + { +#if defined(PCBTARANIS) +#else + if (md->curveParam) { + if (md->curveMode == MODE_CURVE) + putsCurve(MIX_LINE_CURVE_POS, y, md->curveParam); + else + displayGVar(MIX_LINE_CURVE_POS+3*FW, y, md->curveParam, -100, 100); + } +#endif + if (md->swtch) putsSwitches(MIX_LINE_SWITCH_POS, y, md->swtch); + + char cs = ' '; + if (md->speedDown || md->speedUp) + cs = 'S'; + if ((md->delayUp || md->delayDown)) + cs = (cs =='S' ? '*' : 'D'); + lcd_putc(MIX_LINE_DELAY_POS, y, cs); + } + } + if (s_copyMode) { + if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { + /* draw a border around the raw on selection mode (copy/move) */ + lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, s_copyMode == COPY_MODE ? SOLID : DOTTED); + } + if (cur == sub) { + /* invert the raw when it's the current one */ + lcd_filled_rect(expo ? EXPO_LINE_SELECT_POS+1 : 23, y, expo ? (LCD_W-EXPO_LINE_SELECT_POS-2) : (LCD_W-24), 7); + } + } + } + cur++; y+=FH; mixCnt++; i++; if (expo) ed++; else md++; + } while (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)); + if (s_copyMode == MOVE_MODE && s_pgOfs < cur && cur-s_pgOfs < LCD_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { + lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? LCD_W-EXPO_LINE_SELECT_POS : LCD_W-22, 9, DOTTED); + cur++; y+=FH; + } + } + else { + uint8_t attr = 0; + if (sub == cur) { + s_currIdx = i; + s_currCh = ch; + if (!s_copyMode) { + attr = INVERS; + } + } + if (s_pgOfs < cur && cur-s_pgOfs < LCD_LINES) { + if (expo) { +#if defined(PCBTARANIS) + putsMixerSource(0, y, ch, attr); +#else + putsMixerSource(0, y, MIXSRC_Rud+ch-1, attr); +#endif + } + else { + putsChn(0, y, ch, attr); // show CHx +#if LCD_W >= 212 + if (attr) { + displayHeaderChannelName(ch); + } +#endif + } + if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) { + lcd_rect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, DOTTED); + } + } + cur++; y+=FH; + } + } + s_maxLines = cur; + if (sub >= s_maxLines-1) m_posVert = s_maxLines-1; +} + +void menuModelExposAll(uint8_t event) +{ + return menuModelExpoMix(1, event); +} + +void menuModelMixAll(uint8_t event) +{ + return menuModelExpoMix(0, event); +} + +bool thrOutput(uint8_t ch) +{ + for (uint8_t i=0; idestCh==ch && mix->srcRaw==MIXSRC_Thr) + return true; + } + return false; +} + +enum LimitsItems { +#if defined(PCBTARANIS) + ITEM_LIMITS_CH_NAME, +#endif + ITEM_LIMITS_OFFSET, + ITEM_LIMITS_MIN, + ITEM_LIMITS_MAX, + ITEM_LIMITS_DIRECTION, +#if defined(PCBTARANIS) && defined(CURVES) + ITEM_LIMITS_CURVE, +#endif +#if defined(PPM_CENTER_ADJUSTABLE) + ITEM_LIMITS_PPM_CENTER, +#endif +#if defined(PPM_LIMITS_SYMETRICAL) + ITEM_LIMITS_SYMETRICAL, +#endif + ITEM_LIMITS_COUNT, + ITEM_LIMITS_MAXROW = ITEM_LIMITS_COUNT-1 +}; + +#if defined(PCBTARANIS) + #define LIMITS_NAME_POS 4*FW + #define LIMITS_OFFSET_POS 14*FW+4 + #define LIMITS_MIN_POS 20*FW-3 + #if defined(PPM_CENTER_ADJUSTABLE) + #define LIMITS_DIRECTION_POS 20*FW-3 + #define LIMITS_MAX_POS 24*FW+2 + #define LIMITS_REVERT_POS 25*FW-1 + #define LIMITS_CURVE_POS 27*FW-1 + #define LIMITS_PPM_CENTER_POS 34*FW + #else + #define LIMITS_DIRECTION_POS 21*FW + #define LIMITS_MAX_POS 26*FW + #define LIMITS_REVERT_POS 27*FW + #define LIMITS_CURVE_POS 32*FW + #endif +#else + #if defined(PPM_UNIT_US) + #define LIMITS_MIN_POS 12*FW+1 + #else + #define LIMITS_MIN_POS 12*FW + #endif + #define LIMITS_OFFSET_POS 8*FW + #if defined(PPM_LIMITS_SYMETRICAL) + #if defined(PPM_CENTER_ADJUSTABLE) + #define LIMITS_MAX_POS 15*FW + #define LIMITS_REVERT_POS 16*FW-3 + #define LIMITS_PPM_CENTER_POS 20*FW+1 + #else + #define LIMITS_DIRECTION_POS 12*FW+4 + #define LIMITS_MAX_POS 16*FW+4 + #define LIMITS_REVERT_POS 17*FW + #endif + #else + #if defined(PPM_CENTER_ADJUSTABLE) + #define LIMITS_MAX_POS 16*FW + #define LIMITS_REVERT_POS 17*FW-2 + #define LIMITS_PPM_CENTER_POS 21*FW+2 + #else + #define LIMITS_MAX_POS 17*FW + #define LIMITS_REVERT_POS 18*FW + #define LIMITS_DIRECTION_POS 12*FW+5 + #endif + #endif +#endif + +#if defined(CPUARM) + #define LIMITS_MIN_MAX_OFFSET 1000 + #define CONVERT_US_MIN_MAX(x) (((x)*128)/250) + #define MIN_MAX_LIMIT (10*limit) + #define MIN_MAX_ATTR attr|PREC1 +#else + #define LIMITS_MIN_MAX_OFFSET 100 + #define CONVERT_US_MIN_MAX(x) ((int16_t(x)*128)/25) + #define MIN_MAX_LIMIT (limit) + #define MIN_MAX_ATTR attr +#endif + +#if defined(PPM_UNIT_US) + #define MIN_MAX_DISPLAY(x) CONVERT_US_MIN_MAX(x) + #undef MIN_MAX_ATTR + #define MIN_MAX_ATTR attr +#else + #define MIN_MAX_DISPLAY(x) (x) +#endif + +#if defined(PCBTARANIS) +void onLimitsMenu(const char *result) +{ + uint8_t ch = m_posVert - 1; + + if (result == STR_RESET) { + LimitData *ld = limitAddress(ch); + ld->min = 0; + ld->max = 0; + ld->offset = 0; + ld->ppmCenter = 0; + ld->revert = false; + ld->curve = 0; + } + else if (result == STR_COPY_TRIMS_TO_OFFSET) { + copyTrimsToOffset(ch); + } +} +#endif + +void menuModelLimits(uint8_t event) +{ + uint8_t sub = m_posVert - 1; + + if (sub < NUM_CHNOUT) { +#if defined(PPM_CENTER_ADJUSTABLE) || defined(PPM_UNIT_US) + lcd_outdezAtt(13*FW, 0, PPM_CH_CENTER(sub)+channelOutputs[sub]/2, 0); + lcd_puts(13*FW, 0, STR_US); +#else + lcd_outdezAtt(13*FW, 0, calcRESXto1000(channelOutputs[sub]), PREC1); +#endif + } + +#if defined(CPUARM) + MENU(STR_MENULIMITS, menuTabModel, e_Limits, 1+NUM_CHNOUT+1, {0, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, 0}); +#else + MENU(STR_MENULIMITS, menuTabModel, e_Limits, 1+NUM_CHNOUT+1, {0, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, ITEM_LIMITS_MAXROW, 0}); +#endif + +#if LCD_W >= 212 + if (sub=0) { + displayColumnHeader(STR_LIMITS_HEADERS, m_posHorz); + } +#endif + + if (s_warning_result) { + s_warning_result = 0; + LimitData *ld = limitAddress(sub); + ld->revert = !ld->revert; + eeDirty(EE_MODEL); + } + + for (uint8_t i=0; i= 212 || !defined(PPM_CENTER_ADJUSTABLE) + int16_t v = (ld->revert) ? -ld->offset : ld->offset; + char swVal = '-'; // '-', '<', '>' + if((channelOutputs[k] - v) > 50) swVal = (ld->revert ? 127 : 126); // Switch to raw inputs? - remove trim! + if((channelOutputs[k] - v) < -50) swVal = (ld->revert ? 126 : 127); + putsChn(0, y, k+1, 0); + lcd_putc(LIMITS_DIRECTION_POS, y, swVal); +#endif + +#if defined(PPM_CENTER_ADJUSTABLE) + int8_t limit = ((g_model.extendedLimits && !limitAddress(k)->ppmCenter) ? 125 : 100); +#else + int8_t limit = (g_model.extendedLimits ? 125 : 100); +#endif + +#if defined(PCBTARANIS) + putsChn(0, y, k+1, (sub==k && m_posHorz < 0) ? INVERS : 0); + if (sub==k && m_posHorz < 0 && event==EVT_KEY_LONG(KEY_ENTER)) { + killEvents(event); + MENU_ADD_ITEM(STR_RESET); + MENU_ADD_ITEM(STR_COPY_TRIMS_TO_OFFSET); + menuHandler = onLimitsMenu; + } +#else + putsChn(0, y, k+1, 0); +#endif + + for (uint8_t j=0; j0) ? BLINK|INVERS : INVERS) : 0); + uint8_t active = (attr && (s_editMode>0 || p1valdiff)) ; + if (active) STICK_SCROLL_DISABLE(); + switch(j) + { +#if defined(PCBTARANIS) + case ITEM_LIMITS_CH_NAME: + editName(LIMITS_NAME_POS, y, ld->name, sizeof(ld->name), event, attr); + break; +#endif + + case ITEM_LIMITS_OFFSET: +#if defined(PCBTARANIS) + ld->offset = GVAR_MENU_ITEM(LIMITS_OFFSET_POS, y, MIN_MAX_DISPLAY(ld->offset), -1000, 1000, attr|PREC1, 0, event); +#else + #if defined(PPM_UNIT_US) + lcd_outdezAtt(LIMITS_OFFSET_POS, y, ((int32_t)ld->offset*128) / 25, attr|PREC1); + #else + lcd_outdezAtt(LIMITS_OFFSET_POS, y, ld->offset, attr|PREC1); + #endif + if (active) { + ld->offset = checkIncDec(event, ld->offset, -1000, 1000, EE_MODEL|NO_INCDEC_MARKS); + } + else if (attr && event==EVT_KEY_LONG(KEY_MENU)) { + copyTrimsToOffset(k); + s_editMode = 0; + } +#endif + break; + + case ITEM_LIMITS_MIN: +#if defined(CPUARM) + ld->min = LIMITS_MIN_MAX_OFFSET + GVAR_MENU_ITEM(LIMITS_MIN_POS, y, MIN_MAX_DISPLAY(ld->min-LIMITS_MIN_MAX_OFFSET), -MIN_MAX_LIMIT, 0, MIN_MAX_ATTR, DBLKEYS_1000, event); +#else + lcd_outdezAtt(LIMITS_MIN_POS, y, MIN_MAX_DISPLAY(ld->min-LIMITS_MIN_MAX_OFFSET), MIN_MAX_ATTR); + if (active) ld->min = LIMITS_MIN_MAX_OFFSET + checkIncDecModel(event, ld->min-LIMITS_MIN_MAX_OFFSET, -MIN_MAX_LIMIT, 0); +#endif + break; + + case ITEM_LIMITS_MAX: +#if defined(CPUARM) + ld->max = -LIMITS_MIN_MAX_OFFSET + GVAR_MENU_ITEM(LIMITS_MAX_POS, y, MIN_MAX_DISPLAY(ld->max+LIMITS_MIN_MAX_OFFSET), 0, MIN_MAX_LIMIT, MIN_MAX_ATTR, DBLKEYS_1000, event); +#else + lcd_outdezAtt(LIMITS_MAX_POS, y, MIN_MAX_DISPLAY(ld->max+LIMITS_MIN_MAX_OFFSET), MIN_MAX_ATTR); + if (active) ld->max = -LIMITS_MIN_MAX_OFFSET + checkIncDecModelZero(event, ld->max+LIMITS_MIN_MAX_OFFSET, +MIN_MAX_LIMIT); +#endif + break; + + case ITEM_LIMITS_DIRECTION: + { + uint8_t revert = ld->revert; +#if defined(PPM_CENTER_ADJUSTABLE) + lcd_putcAtt(LIMITS_REVERT_POS, y, revert ? 127 : 126, attr); +#else + lcd_putsiAtt(LIMITS_REVERT_POS, y, STR_MMMINV, revert, attr); +#endif + if (active) { + uint8_t revert_new = checkIncDecModel(event, revert, 0, 1); + if (checkIncDec_Ret && thrOutput(k)) { + POPUP_CONFIRMATION(STR_INVERT_THR); + } + else { + ld->revert = revert_new; + } + } + break; + } + +#if defined(PCBTARANIS) && defined(CURVES) + case ITEM_LIMITS_CURVE: + putsCurve(LIMITS_CURVE_POS, y, ld->curve, attr); + if (attr && event==EVT_KEY_LONG(KEY_ENTER) && ld->curve>0) { + s_curveChan = (ld->curve<0 ? -ld->curve-1 : ld->curve-1); + pushMenu(menuModelCurveOne); + } + if (active) { + CHECK_INCDEC_MODELVAR(event, ld->curve, -MAX_CURVES, +MAX_CURVES); + } + break; +#endif + +#if defined(PPM_CENTER_ADJUSTABLE) + case ITEM_LIMITS_PPM_CENTER: + lcd_outdezAtt(LIMITS_PPM_CENTER_POS, y, PPM_CENTER+ld->ppmCenter, attr); +#pragma message("could be less restrictive!") + if (active && ld->max <= 0 && ld->min >= 0) { + CHECK_INCDEC_MODELVAR(event, ld->ppmCenter, -125, +125); + } + break; +#endif + +#if defined(PPM_LIMITS_SYMETRICAL) + case ITEM_LIMITS_SYMETRICAL: + lcd_putcAtt(LCD_W-FW-MENUS_SCROLLBAR_WIDTH, y, ld->symetrical ? '=' : '^', attr); + if (active) { + CHECK_INCDEC_MODELVAR_ZERO(event, ld->symetrical, 1); + } + break; +#endif + } + } + } +} + +#if defined(CURVES) + +#if defined(GVARS) + #define CURVE_SELECTED() (sub >= 0 && sub < MAX_CURVES) + #define GVAR_SELECTED() (sub >= MAX_CURVES) +#else + #define CURVE_SELECTED() (sub >= 0) +#endif + +void menuModelCurvesAll(uint8_t event) +{ +#if defined(GVARS) && defined(PCBSTD) + SIMPLE_MENU(STR_MENUCURVES, menuTabModel, e_CurvesAll, 1+MAX_CURVES+MAX_GVARS); +#else + SIMPLE_MENU(STR_MENUCURVES, menuTabModel, e_CurvesAll, 1+MAX_CURVES); +#endif + + int8_t sub = m_posVert - 1; + + switch (event) { +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_BREAK: +#endif +#if defined(PCBTARANIS) + case EVT_KEY_BREAK(KEY_ENTER): +#else + case EVT_KEY_FIRST(KEY_RIGHT): + case EVT_KEY_FIRST(KEY_ENTER): +#endif + if (CURVE_SELECTED() && !READ_ONLY()) { + s_curveChan = sub; + pushMenu(menuModelCurveOne); + } + break; + } + + for (uint8_t i=0; i= MAX_CURVES) { + putsStrIdx(0, y, STR_GV, k-MAX_CURVES+1); + if (GVAR_SELECTED()) { + if (attr && s_editMode>0) attr |= BLINK; + lcd_outdezAtt(10*FW, y, GVAR_VALUE(k-MAX_CURVES, -1), attr); + if (attr) g_model.gvars[k-MAX_CURVES] = checkIncDec(event, g_model.gvars[k-MAX_CURVES], -1000, 1000, EE_MODEL); + } + } + else +#endif + { + putsStrIdx(0, y, STR_CV, k+1, attr); +#if defined(PCBTARANIS) + editName(4*FW, y, g_model.curveNames[k], sizeof(g_model.curveNames[k]), 0, 0); + CurveInfo & crv = g_model.curves[k]; + lcd_outdezAtt(11*FW, y, 5+crv.points, LEFT); + lcd_putsAtt(lcdLastPos, y, PSTR("pts"), 0); +#endif + } + } + + if (CURVE_SELECTED()) { + s_curveChan = sub; + DrawCurve(23); + } +} +#endif + +#if LCD_W >= 212 && defined(GVARS) && defined(FLIGHT_MODES) + +void onGVARSMenu(const char *result) +{ + int8_t sub = m_posVert - 1; + + if (result == STR_ENABLE_POPUP) { + g_model.gvars[sub].popup = true; + eeDirty(EE_MODEL); + } + else if (result == STR_DISABLE_POPUP) { + g_model.gvars[sub].popup = false; + eeDirty(EE_MODEL); + } + else if (result == STR_CLEAR) { + for (int i=0; i 6 + for (uint8_t l=0; l0) ? BLINK|INVERS : INVERS) : 0); + xcoord_t x = 12*FW + FWNUM + (j-1)*(2+3*FWNUM) - 3; + +#if MAX_GVARS == 6 + if (i==0 && j!=9) putsStrIdx(x+2, FH+1, STR_FP, j, SMLSIZE); +#elif MAX_GVARS <= 5 + if (i==0 && j!=9) putsStrIdx(x+2, 2*FH, STR_FP, j, SMLSIZE); +#endif + + switch(j) + { + case 0: + editName(4*FW-3, y, g_model.gvars[i].name, LEN_GVAR_NAME, event, attr); + break; + + default: + { + PhaseData *phase = &g_model.phaseData[j-1]; + int16_t & v = phase->gvars[i]; + int16_t vmin, vmax; + if (v > GVAR_MAX) { + uint8_t p = v - GVAR_MAX - 1; + if (p >= j-1) p++; + putsFlightPhase(x-15, y, p+1, attr|SMLSIZE); + vmin = GVAR_MAX+1; vmax = GVAR_MAX+MAX_PHASES-1; + } + else { + if (abs(v) >= 100) + lcd_outdezAtt(x, y+1, v, attr | TINSIZE); + else + lcd_outdezAtt(x, y, v, attr); + vmin = -GVAR_MAX; vmax = GVAR_MAX; + } + if (attr) { + if (event == EVT_KEY_LONG(KEY_ENTER)) { + s_editMode = 2; // TODO constant for that ... + v = (v > GVAR_MAX ? 0 : GVAR_MAX+1); + eeDirty(EE_MODEL); + } + else if (s_editMode>0 || p1valdiff) { + v = checkIncDec(event, v, vmin, vmax, EE_MODEL); + } + } + break; + } + } + } + } + +#if defined(CPUARM) + #define INCDEC_DECLARE_VARS() uint8_t incdecFlag = EE_MODEL; IsValueAvailable isValueAvailable = NULL + #define INCDEC_SET_FLAG(f) incdecFlag = (EE_MODEL|(f)) + #define INCDEC_ENABLE_CHECK(fn) isValueAvailable = fn + #define CHECK_INCDEC_PARAM(event, var, min, max) checkIncDec(event, var, min, max, incdecFlag, isValueAvailable) +#elif defined(CPUM64) + #define INCDEC_DECLARE_VARS() + #define INCDEC_SET_FLAG(f) + #define INCDEC_ENABLE_CHECK(fn) + #define CHECK_INCDEC_PARAM(event, var, min, max) checkIncDec(event, var, min, max, EE_MODEL) +#else + #define INCDEC_DECLARE_VARS() uint8_t incdecFlag = EE_MODEL + #define INCDEC_SET_FLAG(f) incdecFlag = (EE_MODEL|(f)) + #define INCDEC_ENABLE_CHECK(fn) + #define CHECK_INCDEC_PARAM(event, var, min, max) checkIncDec(event, var, min, max, incdecFlag) +#endif + + if (m_posVert > 0 && m_posHorz < 0 && event==EVT_KEY_LONG(KEY_ENTER)) { + killEvents(event); + if (g_model.gvars[sub].popup) + MENU_ADD_ITEM(STR_DISABLE_POPUP); + else + MENU_ADD_ITEM(STR_ENABLE_POPUP); + MENU_ADD_ITEM(STR_CLEAR); + menuHandler = onGVARSMenu; + } +} +#endif + +#if defined(CPUARM) + #define INCDEC_DECLARE_VARS() uint8_t incdecFlag = EE_MODEL; IsValueAvailable isValueAvailable = NULL + #define INCDEC_SET_FLAG(f) incdecFlag = (EE_MODEL|(f)) + #define INCDEC_ENABLE_CHECK(fn) isValueAvailable = fn + #define CHECK_INCDEC_PARAM(event, var, min, max) checkIncDec(event, var, min, max, incdecFlag, isValueAvailable) +#elif defined(CPUM64) + #define INCDEC_DECLARE_VARS() + #define INCDEC_SET_FLAG(f) + #define INCDEC_ENABLE_CHECK(fn) + #define CHECK_INCDEC_PARAM(event, var, min, max) checkIncDec(event, var, min, max, EE_MODEL) +#else + #define INCDEC_DECLARE_VARS() uint8_t incdecFlag = EE_MODEL + #define INCDEC_SET_FLAG(f) incdecFlag = (EE_MODEL|(f)) + #define INCDEC_ENABLE_CHECK(fn) + #define CHECK_INCDEC_PARAM(event, var, min, max) checkIncDec(event, var, min, max, incdecFlag) +#endif + +enum CustomSwitchFields { + CSW_FIELD_FUNCTION, + CSW_FIELD_V1, + CSW_FIELD_V2, + CSW_FIELD_ANDSW, +#if defined(CPUARM) + CSW_FIELD_DURATION, + CSW_FIELD_DELAY, +#endif + CSW_FIELD_COUNT, + CSW_FIELD_LAST = CSW_FIELD_COUNT-1 +}; + +#if LCD_W >= 212 + #define CSW_1ST_COLUMN (4*FW-3) + #define CSW_2ND_COLUMN (8*FW+1) + #define CSW_3RD_COLUMN (14*FW) + #define CSW_4TH_COLUMN (21*FW+1) + #define CSW_5TH_COLUMN (26*FW+1) + #define CSW_6TH_COLUMN (31*FW+1) +#else + #define CSW_1ST_COLUMN (4*FW-3) + #define CSW_2ND_COLUMN (8*FW-3) + #define CSW_3RD_COLUMN (13*FW-6) + #define CSW_4TH_COLUMN (18*FW+2) +#endif + +#if defined(CPUARM) && LCD_W < 212 + +#define CSWONE_2ND_COLUMN (11*FW) + +void menuModelCustomSwitchOne(uint8_t event) +{ + TITLE(STR_MENUCUSTOMSWITCH); + + CustomSwData * cs = cswAddress(s_currIdx); + uint8_t sw = SWSRC_SW1+s_currIdx; + putsSwitches(14*FW, 0, sw, (getSwitch(sw) ? BOLD : 0)); + SIMPLE_SUBMENU_NOTITLE(CSW_FIELD_COUNT); + + int8_t sub = m_posVert; + + for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); + uint8_t cstate = cswFamily(cs->func); + switch(i) { + case CSW_FIELD_FUNCTION: + lcd_putsLeft(y, STR_FUNC); + lcd_putsiAtt(CSWONE_2ND_COLUMN, y, STR_VCSWFUNC, cs->func, attr); + if (attr) { + CHECK_INCDEC_MODELVAR_ZERO(event, cs->func, CS_MAXF); + if (cstate != cswFamily(cs->func)) { + cs->v1 = 0; + cs->v2 = 0; + } + } + break; + case CSW_FIELD_V1: + { + lcd_putsLeft(y, STR_V1); + int8_t v1_min=0, v1_max=MIXSRC_LAST_TELEM; + if (cstate == CS_VBOOL) { + putsSwitches(CSWONE_2ND_COLUMN, y, cs->v1, attr); + v1_min = SWSRC_OFF+1; v1_max = SWSRC_ON-1; + } + else if (cstate == CS_VTIMER) { + lcd_outdezAtt(CSWONE_2ND_COLUMN, y, cs->v1+1, LEFT|attr); + v1_max = 99; + } + else { + putsMixerSource(CSWONE_2ND_COLUMN, y, cs->v1, attr); + } + if (attr) { + CHECK_INCDEC_MODELVAR(event, cs->v1, v1_min, v1_max); + } + break; + } + case CSW_FIELD_V2: + { + lcd_putsLeft(y, STR_V2); + int8_t v2_min=0, v2_max=MIXSRC_LAST_TELEM; + if (cstate == CS_VBOOL) { + putsSwitches(CSWONE_2ND_COLUMN, y, cs->v2, attr); + v2_min = SWSRC_OFF+1; v2_max = SWSRC_ON-1; + } + else if (cstate == CS_VTIMER) { + lcd_outdezAtt(CSWONE_2ND_COLUMN, y, cs->v2+1, LEFT|attr); + v2_max = 99; + } + else if (cstate == CS_VCOMP) { + putsMixerSource(CSWONE_2ND_COLUMN, y, cs->v2, attr); + } + else { +#if defined(FRSKY) + if (cs->v1 >= MIXSRC_FIRST_TELEM) { + putsTelemetryChannel(CSWONE_2ND_COLUMN, y, cs->v1 - MIXSRC_FIRST_TELEM, convertCswTelemValue(cs), attr|LEFT); + v2_max = maxTelemValue(cs->v1 - MIXSRC_FIRST_TELEM + 1); + if (cstate == CS_VOFS) { + v2_min = -128; + v2_max -= 128; + } + else { + v2_max = min((uint8_t)127, (uint8_t)v2_max); + v2_min = -v2_max; + } + if (cs->v2 > v2_max) { + cs->v2 = v2_max; + eeDirty(EE_MODEL); + } + } + else +#endif + { + v2_min = -125; v2_max = 125; + lcd_outdezAtt(CSWONE_2ND_COLUMN, y, cs->v2, LEFT|attr); + } + } + + if (attr) { + CHECK_INCDEC_MODELVAR(event, cs->v2, v2_min, v2_max); + } + break; + } + case CSW_FIELD_ANDSW: + lcd_putsLeft(y, STR_AND_SWITCH); + putsSwitches(CSWONE_2ND_COLUMN, y, cs->andsw, attr); + if (attr) CHECK_INCDEC_MODELVAR(event, cs->andsw, -MAX_SWITCH, MAX_SWITCH); + break; + case CSW_FIELD_DURATION: + lcd_putsLeft(y, STR_DURATION); + if (cs->duration > 0) + lcd_outdezAtt(CSWONE_2ND_COLUMN, y, 5*cs->duration, attr|PREC1|LEFT); + else + lcd_putsiAtt(CSWONE_2ND_COLUMN, y, STR_MMMINV, 0, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, cs->duration, MAX_CSW_DURATION); + break; + case CSW_FIELD_DELAY: + lcd_putsLeft(y, STR_DELAY); + if (cs->delay > 0) + lcd_outdezAtt(CSWONE_2ND_COLUMN, y, 5*cs->delay, attr|PREC1|LEFT); + else + lcd_putsiAtt(CSWONE_2ND_COLUMN, y, STR_MMMINV, 0, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, cs->delay, MAX_CSW_DELAY); + break; + } + } +} + +void menuModelCustomSwitches(uint8_t event) +{ + SIMPLE_MENU(STR_MENUCUSTOMSWITCHES, menuTabModel, e_CustomSwitches, NUM_CSW+1); + + uint8_t y = 0; + uint8_t k = 0; + int8_t sub = m_posVert - 1; + + switch (event) { +#if defined(ROTARY_ENCODER_NAVIGATION) + case EVT_ROTARY_BREAK: +#endif + case EVT_KEY_FIRST(KEY_RIGHT): + case EVT_KEY_FIRST(KEY_ENTER): + if (sub >= 0) { + s_currIdx = sub; + pushMenu(menuModelCustomSwitchOne); + } + break; + } + + for (uint8_t i=0; ifunc > 0) { + // CSW func + lcd_putsiAtt(CSW_1ST_COLUMN, y, STR_VCSWFUNC, cs->func, 0); + + // CSW params + uint8_t cstate = cswFamily(cs->func); + + if (cstate == CS_VBOOL) { + putsSwitches(CSW_2ND_COLUMN, y, cs->v1, 0); + putsSwitches(CSW_3RD_COLUMN, y, cs->v2, 0); + } + else if (cstate == CS_VCOMP) { + putsMixerSource(CSW_2ND_COLUMN, y, cs->v1, 0); + putsMixerSource(CSW_3RD_COLUMN, y, cs->v2, 0); + } + else if (cstate == CS_VTIMER) { + lcd_outdezAtt(CSW_2ND_COLUMN, y, cs->v1+1, LEFT); + lcd_outdezAtt(CSW_3RD_COLUMN, y, cs->v2+1, LEFT); + } + else { + putsMixerSource(CSW_2ND_COLUMN, y, cs->v1, 0); + if (cs->v1 >= MIXSRC_FIRST_TELEM) { + putsTelemetryChannel(CSW_3RD_COLUMN, y, cs->v1 - MIXSRC_FIRST_TELEM, convertCswTelemValue(cs), LEFT); + } + else { + lcd_outdezAtt(CSW_3RD_COLUMN, y, cs->v2, LEFT); + } + } + + // CSW and switch + putsSwitches(CSW_4TH_COLUMN, y, cs->andsw, 0); + } + } +} + +#else + +#if defined(PCBTARANIS) +enum ClipboardType { + CLIPBOARD_TYPE_NONE, + CLIPBOARD_TYPE_CUSTOM_SWITCH, + CLIPBOARD_TYPE_CUSTOM_FUNCTION, +}; + +struct Clipboard { + ClipboardType type; + union { + CustomSwData csw; + CustomFnData cfn; + } data; +}; + +Clipboard clipboard; + +void onCustomSwitchesMenu(const char *result) +{ + int8_t sub = m_posVert-1; + CustomSwData * cs = cswAddress(sub); + + if (result == STR_COPY) { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; + clipboard.data.csw = *cs; + } + else if (result == STR_PASTE) { + *cs = clipboard.data.csw; + eeDirty(EE_MODEL); + } + else if (result == STR_DELETE) { + memset(cs, 0, sizeof(CustomSwData)); + eeDirty(EE_MODEL); + } +} +#endif + +void menuModelCustomSwitches(uint8_t event) +{ + INCDEC_DECLARE_VARS(); + + MENU(STR_MENUCUSTOMSWITCHES, menuTabModel, e_CustomSwitches, NUM_CSW+1, {0, NAVIGATION_LINE_BY_LINE|CSW_FIELD_LAST/*repeated...*/}); + + uint8_t y = 0; + uint8_t k = 0; + int8_t sub = m_posVert - 1; + horzpos_t horz = m_posHorz; + +#if LCD_W >= 212 + if (horz>=0) { + displayColumnHeader(STR_CSW_HEADERS, horz); + } +#endif + +#if defined(PCBTARANIS) + if (sub>=0 && horz<0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) { + killEvents(event); + CustomSwData * cs = cswAddress(sub); + if (cs->func) MENU_ADD_ITEM(STR_COPY); + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) + MENU_ADD_ITEM(STR_PASTE); + if (cs->func || cs->v1 || cs->v2 || cs->delay || cs->duration || cs->andsw) MENU_ADD_ITEM(STR_DELETE); + menuHandler = onCustomSwitchesMenu; + } +#endif + + for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS) : 0); + uint8_t attr1 = (horz==1 ? attr : 0); + uint8_t attr2 = (horz==2 ? attr : 0); + CustomSwData * cs = cswAddress(k); + + // CSW name + uint8_t sw = SWSRC_SW1+k; + putsSwitches(0, y, sw, (getSwitch(sw) ? BOLD : 0) | ((sub==k && horz<0) ? INVERS : 0)); + + // CSW func + lcd_putsiAtt(CSW_1ST_COLUMN, y, STR_VCSWFUNC, cs->func, horz==0 ? attr : 0); + + // CSW params + uint8_t cstate = cswFamily(cs->func); +#if defined(CPUARM) + int16_t v1_min=0, v1_max=MIXSRC_LAST_TELEM, v2_min=0, v2_max=MIXSRC_LAST_TELEM; +#else + int8_t v1_min=0, v1_max=MIXSRC_LAST_TELEM, v2_min=0, v2_max=MIXSRC_LAST_TELEM; +#endif + + if (cstate == CS_VBOOL) { + putsSwitches(CSW_2ND_COLUMN, y, cs->v1, attr1); + putsSwitches(CSW_3RD_COLUMN, y, cs->v2, attr2); + v1_min = SWSRC_OFF+1; v1_max = SWSRC_ON-1; + v2_min = SWSRC_OFF+1; v2_max = SWSRC_ON-1; + INCDEC_SET_FLAG(INCDEC_SWITCH); + INCDEC_ENABLE_CHECK(NULL); + } + else if (cstate == CS_VCOMP) { + putsMixerSource(CSW_2ND_COLUMN, y, cs->v1, attr1); + putsMixerSource(CSW_3RD_COLUMN, y, cs->v2, attr2); + INCDEC_SET_FLAG(INCDEC_SOURCE); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } + else if (cstate == CS_VTIMER) { + lcd_outdezAtt(CSW_2ND_COLUMN, y, cswTimerValue(cs->v1), LEFT|PREC1|attr1); + lcd_outdezAtt(CSW_3RD_COLUMN, y, cswTimerValue(cs->v2), LEFT|PREC1|attr2); + v1_min = v2_min = -128; + v1_max = v2_max = 122; + INCDEC_SET_FLAG(0); + INCDEC_ENABLE_CHECK(NULL); + } + else { + putsMixerSource(CSW_2ND_COLUMN, y, cs->v1, attr1); + if (horz == 1) { + INCDEC_SET_FLAG(INCDEC_SOURCE); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } + else { + INCDEC_SET_FLAG(0); + INCDEC_ENABLE_CHECK(NULL); + } +#if defined(FRSKY) + if (cs->v1 >= MIXSRC_FIRST_TELEM) { + putsTelemetryChannel(CSW_3RD_COLUMN, y, cs->v1 - MIXSRC_FIRST_TELEM, convertCswTelemValue(cs), LEFT|attr2); + v2_max = maxTelemValue(cs->v1 - MIXSRC_FIRST_TELEM + 1); +#if defined(CPUARM) + v2_min = minTelemValue(cs->v1 - MIXSRC_FIRST_TELEM + 1); + if (cs->v2 < v2_min || cs->v2 > v2_max) { + cs->v2 = 0; + eeDirty(EE_MODEL); + } +#else + if (cstate == CS_VOFS) { + v2_min = -128; + v2_max -= 128; + } + else { + v2_max = min((uint8_t)127, (uint8_t)v2_max); + v2_min = -v2_max; + } + if (cs->v2 > v2_max) { + cs->v2 = v2_max; + eeDirty(EE_MODEL); + } +#endif + } + else { + lcd_outdezAtt(CSW_3RD_COLUMN, y, cs->v2, LEFT|attr2); + v2_min = -125; v2_max = 125; + } +#else + if (cs->v1 >= MIXSRC_FIRST_TELEM) { + putsTelemetryChannel(CSW_3RD_COLUMN, y, cs->v1 - MIXSRC_FIRST_TELEM, convertCswTelemValue(cs), LEFT|attr2); + v2_min = -128; v2_max = 127; + } + else { + lcd_outdezAtt(CSW_3RD_COLUMN, y, cs->v2, LEFT|attr2); + v2_min = -125; v2_max = 125; + } +#endif + } + + // CSW and switch + putsSwitches(CSW_4TH_COLUMN, y, cs->andsw, horz==3 ? attr : 0); + +#if defined(CPUARM) + // CSW duration + if (cs->duration > 0) + lcd_outdezAtt(CSW_5TH_COLUMN, y, 5*cs->duration, (horz==4 ? attr : 0)|PREC1|LEFT); + else + lcd_putsiAtt(CSW_5TH_COLUMN, y, STR_MMMINV, 0, horz==4 ? attr : 0); + + // CSW delay + if (cs->delay > 0) + lcd_outdezAtt(CSW_6TH_COLUMN, y, 5*cs->delay, (horz==5 ? attr : 0)|PREC1|LEFT); + else + lcd_putsiAtt(CSW_6TH_COLUMN, y, STR_MMMINV, 0, horz==5 ? attr : 0); +#endif + + if ((s_editMode>0 || p1valdiff) && attr) { + switch (horz) { + case CSW_FIELD_FUNCTION: + { + CHECK_INCDEC_MODELVAR_ZERO(event, cs->func, CS_MAXF); + uint8_t new_cstate = cswFamily(cs->func); + if (cstate != new_cstate) + cs->v1 = cs->v2 = (new_cstate==CS_VTIMER ? -119/*1.0*/ : 0); + break; + } + case CSW_FIELD_V1: + cs->v1 = CHECK_INCDEC_PARAM(event, cs->v1, v1_min, v1_max); + break; + case CSW_FIELD_V2: + cs->v2 = CHECK_INCDEC_PARAM(event, cs->v2, v2_min, v2_max); +#if defined(PCBTARANIS) + if (cstate==CS_VOFS && cs->v1!=0 && event==EVT_KEY_LONG(KEY_ENTER)) { + killEvents(event); + getvalue_t x = getValue(cs->v1); + TRACE("AUTO x=%d", x); + if (cs->v1 < MIXSRC_GVAR1) + cs->v2 = calcRESXto100(x); + else if (cs->v1 - MIXSRC_FIRST_TELEM + 1 == TELEM_ALT) + cs->v2 *= 100; + eeDirty(EE_MODEL); + } +#endif + break; + case CSW_FIELD_ANDSW: +#if defined(CPUARM) + CHECK_INCDEC_MODELSWITCH(event, cs->andsw, -MAX_CSW_ANDSW, MAX_CSW_ANDSW); +#else + CHECK_INCDEC_MODELVAR_ZERO(event, cs->andsw, MAX_CSW_ANDSW); +#endif + break; +#if defined(CPUARM) + case CSW_FIELD_DURATION: + CHECK_INCDEC_MODELVAR_ZERO(event, cs->duration, MAX_CSW_DURATION); + break; + case CSW_FIELD_DELAY: + CHECK_INCDEC_MODELVAR_ZERO(event, cs->delay, MAX_CSW_DELAY); + break; +#endif + } + } + } +} +#endif + +#if LCD_W >= 212 + #define MODEL_CUSTOM_FUNC_1ST_COLUMN (5+4*FW) + #define MODEL_CUSTOM_FUNC_2ND_COLUMN (9*FW) + #define MODEL_CUSTOM_FUNC_3RD_COLUMN (21*FW) + #define MODEL_CUSTOM_FUNC_4TH_COLUMN (33*FW-3) + #define MODEL_CUSTOM_FUNC_4TH_COLUMN_ONOFF (34*FW-3) +#else + #define MODEL_CUSTOM_FUNC_1ST_COLUMN (3) + #define MODEL_CUSTOM_FUNC_2ND_COLUMN (5*FW-2) + #define MODEL_CUSTOM_FUNC_3RD_COLUMN (15*FW+2) + #define MODEL_CUSTOM_FUNC_4TH_COLUMN (20*FW) + #if defined(GRAPHICS) + #define MODEL_CUSTOM_FUNC_4TH_COLUMN_ONOFF (20*FW) + #else + #define MODEL_CUSTOM_FUNC_4TH_COLUMN_ONOFF (18*FW+2) + #endif +#endif + +#if defined(CPUARM) && defined(SDCARD) +void onCustomFunctionsFileSelectionMenu(const char *result) +{ + int8_t sub = m_posVert - 1; + + if (result == STR_UPDATE_LIST) { + char directory[] = SOUNDS_PATH; + strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); + if (!listSdFiles(directory, SOUNDS_EXT, sizeof(g_model.funcSw[sub].param), NULL)) { + POPUP_WARNING(STR_NO_SOUNDS_ON_SD); + s_menu_flags = 0; + } + } + else { + // The user choosed a wav file in the list + memcpy(g_model.funcSw[sub].param.name, result, sizeof(g_model.funcSw[sub].param.name)); + eeDirty(EE_MODEL); + } +} +#endif + +#if defined(PCBTARANIS) +void onCustomFunctionsMenu(const char *result) +{ + int8_t sub = m_posVert-1; + CustomFnData * cfn = &g_model.funcSw[sub]; + + if (result == STR_COPY) { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; + clipboard.data.cfn = *cfn; + } + else if (result == STR_PASTE) { + *cfn = clipboard.data.cfn; + eeDirty(EE_MODEL); + } + else if (result == STR_CLEAR) { + memset(cfn, 0, sizeof(CustomFnData)); + eeDirty(EE_MODEL); + } + else if (result == STR_INSERT) { + memmove(cfn+1, cfn, (NUM_CFN-sub-1)*sizeof(CustomFnData)); + memset(cfn, 0, sizeof(CustomFnData)); + eeDirty(EE_MODEL); + } + else if (result == STR_DELETE) { + memmove(cfn, cfn+1, (NUM_CFN-sub-1)*sizeof(CustomFnData)); + memset(&g_model.funcSw[NUM_CFN-1], 0, sizeof(CustomFnData)); + eeDirty(EE_MODEL); + } +} + +#endif + +void menuModelCustomFunctions(uint8_t event) +{ + MENU(STR_MENUCUSTOMFUNC, menuTabModel, e_CustomFunctions, NUM_CFN+1, {0, NAVIGATION_LINE_BY_LINE|3/*repeated*/}); + + uint8_t y; + uint8_t k = 0; + int8_t sub = m_posVert - 1; + +#if defined(PCBTARANIS) + if (sub>=0 && m_posHorz<0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) { + killEvents(event); + CustomFnData *sd = &g_model.funcSw[sub]; + if (!CFN_EMPTY(sd)) + MENU_ADD_ITEM(STR_COPY); + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) + MENU_ADD_ITEM(STR_PASTE); + if (!CFN_EMPTY(sd) && CFN_EMPTY(&g_model.funcSw[NUM_CFN-1])) + MENU_ADD_ITEM(STR_INSERT); + if (CFN_EMPTY(sd)) + MENU_ADD_ITEM(STR_DELETE); + else + MENU_ADD_ITEM(STR_CLEAR); + menuHandler = onCustomFunctionsMenu; + } +#endif + + for (uint8_t i=0; i= 212 + putsStrIdx(0, y, STR_CF, k+1, (sub==k && m_posHorz<0) ? INVERS : 0); +#endif + + CustomFnData *sd = &g_model.funcSw[k]; + for (uint8_t j=0; j<4; j++) { + uint8_t attr = ((sub==k && m_posHorz==j) ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0); + uint8_t active = (attr && (s_editMode>0 || p1valdiff)); + switch (j) { + case 0: + putsSwitches(MODEL_CUSTOM_FUNC_1ST_COLUMN, y, sd->swtch, attr | ((activeFnSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0)); + if (active || AUTOSWITCH_ENTER_LONG()) CHECK_INCDEC_MODELSWITCH(event, sd->swtch, SWSRC_FIRST, SWSRC_LAST); + break; + + case 1: + if (sd->swtch) { + uint8_t func_displayed; + if (CFN_FUNC(sd) < FUNC_TRAINER) { + func_displayed = 0; + putsChn(MODEL_CUSTOM_FUNC_2ND_COLUMN+6*FW, y, CFN_CH_NUMBER(sd)+1, attr); + } + else if (CFN_FUNC(sd) <= FUNC_TRAINER + NUM_STICKS) { + func_displayed = 1; + if (CFN_FUNC(sd) != FUNC_TRAINER) + putsMixerSource(MODEL_CUSTOM_FUNC_2ND_COLUMN+7*FW, y, MIXSRC_Rud+CFN_FUNC(sd)-FUNC_TRAINER-1, attr); + } +#if defined(DEBUG) + else if (CFN_FUNC(sd) == FUNC_TEST) { +#if defined(GVARS) + func_displayed = FUNC_TEST - FUNC_TRAINER - NUM_STICKS - MAX_GVARS + 2; +#else + func_displayed = FUNC_TEST - FUNC_TRAINER - NUM_STICKS + 1; +#endif + } +#endif +#if defined(GVARS) + else if (CFN_FUNC(sd) >= FUNC_ADJUST_GV1) { + func_displayed = FUNC_ADJUST_GV1 - FUNC_TRAINER - NUM_STICKS + 1; + putsStrIdx(MODEL_CUSTOM_FUNC_2ND_COLUMN+7*FW, y, STR_GV, CFN_FUNC(sd)-FUNC_ADJUST_GV1+1, attr); + } +#endif + else { + func_displayed = 2 + CFN_FUNC(sd) - FUNC_TRAINER - NUM_STICKS - 1; + } + lcd_putsiAtt(MODEL_CUSTOM_FUNC_2ND_COLUMN, y, STR_VFSWFUNC, func_displayed, attr); + if (active) { +#if defined(CPUARM) + CHECK_INCDEC_MODELVAR_ZERO(event, CFN_FUNC(sd), FUNC_MAX-1); +#else + if (CFN_FUNC(sd) < FUNC_TRAINER) { + CHECK_INCDEC_MODELVAR_ZERO(event, sd->internal.func_safety.func, 16); + } + else { + CHECK_INCDEC_MODELVAR_ZERO(event, CFN_FUNC(sd), FUNC_MAX-1); + if (CFN_FUNC(sd) < FUNC_TRAINER) + sd->internal.func_safety.func = 15; + } +#endif + if (checkIncDec_Ret) CFN_RESET(sd); + } + } + else if (attr) { + REPEAT_LAST_CURSOR_MOVE(); + } + break; + + case 2: + if (sd->swtch) { + INCDEC_DECLARE_VARS(); + int16_t val_displayed = CFN_PARAM(sd); + int8_t val_min = 0; + uint8_t val_max = 255; + if (CFN_FUNC(sd) == FUNC_PLAY_SOUND) { +#if defined(AUDIO) + val_max = AU_FRSKY_LAST-AU_FRSKY_FIRST-1; + lcd_putsiAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, STR_FUNCSOUNDS, val_displayed, attr); +#else + break; +#endif + } +#if defined(HAPTIC) + else if (CFN_FUNC(sd) == FUNC_HAPTIC) { + val_max = 3; + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT); + } +#endif +#if defined(CPUARM) && defined(SDCARD) + else if (CFN_FUNC(sd) == FUNC_PLAY_TRACK || CFN_FUNC(sd) == FUNC_BACKGND_MUSIC) { +#if LCD_W >= 212 + xcoord_t x = MODEL_CUSTOM_FUNC_3RD_COLUMN; +#else + xcoord_t x = (CFN_FUNC(sd) == FUNC_PLAY_TRACK ? MODEL_CUSTOM_FUNC_2ND_COLUMN + FW + FW*strlen(TR_PLAY_TRACK) : MODEL_CUSTOM_FUNC_3RD_COLUMN); +#endif + if (ZEXIST(sd->param.name)) + lcd_putsnAtt(x, y, sd->param.name, sizeof(sd->param.name), attr); + else + lcd_putsiAtt(x, y, STR_VCSWFUNC, 0, attr); + if (active && event==EVT_KEY_BREAK(KEY_ENTER)) { + s_editMode = 0; + char directory[] = SOUNDS_PATH; + strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); + if (listSdFiles(directory, SOUNDS_EXT, sizeof(sd->param.name), sd->param.name)) { + menuHandler = onCustomFunctionsFileSelectionMenu; + } + else { + POPUP_WARNING(STR_NO_SOUNDS_ON_SD); + s_menu_flags = 0; + } + } + break; + } + else if (CFN_FUNC(sd) == FUNC_PLAY_VALUE) { + val_max = MIXSRC_FIRST_TELEM + TELEM_DISPLAY_MAX - 1; + putsMixerSource(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } +#endif +#if defined(CPUARM) + else if (CFN_FUNC(sd) == FUNC_VOLUME) { + val_max = MIXSRC_LAST_CH; + putsMixerSource(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } +#elif defined(VOICE) + else if (CFN_FUNC(sd) == FUNC_PLAY_TRACK) { +#if defined(GVARS) + if (attr && event==EVT_KEY_LONG(KEY_ENTER)) { + killEvents(event); + s_editMode = !s_editMode; + active = true; + val_displayed = (val_displayed > 250 ? 0 : 251); + } + if (val_displayed > 250) { + putsStrIdx(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, STR_GV, val_displayed-250, attr); + } + else { + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed+PROMPT_CUSTOM_BASE, attr|LEFT); + } +#else + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed+PROMPT_CUSTOM_BASE, attr|LEFT); +#endif + } + else if (CFN_FUNC(sd) == FUNC_PLAY_BOTH) { + lcd_putcAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN+3*FWNUM, y, '|', attr); + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN+3*FWNUM, y, val_displayed+PROMPT_CUSTOM_BASE, attr); + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN+2+3*FWNUM, y, (val_displayed+PROMPT_CUSTOM_BASE+1)%10, attr|LEFT); + } + else if (CFN_FUNC(sd) == FUNC_PLAY_VALUE) { + val_max = MIXSRC_FIRST_TELEM + TELEM_DISPLAY_MAX - 1; + putsMixerSource(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } +#endif +#if defined(SDCARD) + else if (CFN_FUNC(sd) == FUNC_LOGS) { + if (val_displayed) { + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr|PREC1|LEFT); + lcd_putc(lcdLastPos, y, 's'); + } + else { + lcd_putsiAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, STR_MMMINV, 0, attr); + } + } +#endif + else if (CFN_FUNC(sd) == FUNC_RESET) { + val_max = FUNC_RESET_PARAM_LAST; + lcd_putsiAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, STR_VFSWRESET, CFN_PARAM(sd), attr); + } + else if (CFN_FUNC(sd) < FUNC_TRAINER) { + val_displayed = (int8_t)CFN_PARAM(sd); + val_min = -125; val_max = 125; + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT); + } +#if defined(GVARS) + else if (CFN_FUNC(sd) >= FUNC_ADJUST_GV1 +#if defined(DEBUG) + && CFN_FUNC(sd) <= FUNC_ADJUST_GVLAST +#endif + ) { + switch (CFN_GVAR_MODE(sd)) { + case FUNC_ADJUST_GVAR_CONSTANT: + val_displayed = (int8_t)CFN_PARAM(sd); + val_min = -125; val_max = 125; + lcd_outdezAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT); + break; + case FUNC_ADJUST_GVAR_SOURCE: + val_max = MIXSRC_LAST_CH; + putsMixerSource(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr); + INCDEC_ENABLE_CHECK(isSourceAvailable); + break; + case FUNC_ADJUST_GVAR_GVAR: + val_max = MAX_GVARS-1; + putsStrIdx(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, STR_GV, val_displayed+1, attr); + break; + default: // FUNC_ADJUST_GVAR_INC + val_max = 1; + lcd_putsiAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, PSTR("\002-1+1"), val_displayed, attr); + break; + } + + if (attr && event==EVT_KEY_LONG(KEY_ENTER)) { + killEvents(event); + s_editMode = !s_editMode; + active = true; + CFN_GVAR_MODE(sd) += 1; + val_displayed = 0; + } + } +#endif + else { + if (attr) m_posHorz = (CURSOR_MOVED_LEFT(event) ? 1 : 3); + break; + } + + if (active) { + CFN_PARAM(sd) = CHECK_INCDEC_PARAM(event, val_displayed, val_min, val_max); + } + } + else if (attr) { + REPEAT_LAST_CURSOR_MOVE(); + } + break; + + case 3: + if (sd->swtch && (CFN_FUNC(sd) <= FUNC_INSTANT_TRIM +#if defined(GVARS) + || CFN_FUNC(sd) >= FUNC_ADJUST_GV1 +#endif +#if defined(CPUARM) + || CFN_FUNC(sd) == FUNC_VOLUME +#endif + )) { + menu_lcd_onoff(MODEL_CUSTOM_FUNC_4TH_COLUMN_ONOFF, y, CFN_ACTIVE(sd), attr); + if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_ACTIVE(sd), 1); + } + else if (sd->swtch && HAS_REPEAT_PARAM(sd)) { + if (CFN_PLAY_REPEAT(sd) == 0) { +#if LCD_W >= 212 + lcd_putsAtt(MODEL_CUSTOM_FUNC_4TH_COLUMN+2, y, "1x", attr); +#else + lcd_putcAtt(MODEL_CUSTOM_FUNC_4TH_COLUMN_ONOFF+1, y, '-', attr); +#endif + } +#if defined(CPUARM) + else if (CFN_PLAY_REPEAT(sd) == CFN_PLAY_REPEAT_NOSTART) { +#if LCD_W >= 212 + lcd_putcAtt(MODEL_CUSTOM_FUNC_4TH_COLUMN-2, y, '!', attr); + lcd_putsAtt(MODEL_CUSTOM_FUNC_4TH_COLUMN+2, y, "1x", attr); +#else + lcd_putcAtt(MODEL_CUSTOM_FUNC_4TH_COLUMN_ONOFF+1, y, '!', attr); +#endif + } +#endif + else { + lcd_outdezAtt(MODEL_CUSTOM_FUNC_4TH_COLUMN+2+FW, y, CFN_PLAY_REPEAT(sd)*CFN_PLAY_REPEAT_MUL, attr); +#if LCD_W >= 212 + lcd_putcAtt(MODEL_CUSTOM_FUNC_4TH_COLUMN+2+FW, y, 's', attr); +#endif + } +#if defined(CPUARM) + if (active) CFN_PLAY_REPEAT(sd) = checkIncDecModel(event, CFN_PLAY_REPEAT(sd)==CFN_PLAY_REPEAT_NOSTART?-1:CFN_PLAY_REPEAT(sd), -1, 60/CFN_PLAY_REPEAT_MUL); +#else + if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_PLAY_REPEAT(sd), 60/CFN_PLAY_REPEAT_MUL); +#endif + } + else if (attr) { + REPEAT_LAST_CURSOR_MOVE(); + } + break; + } + } + } +} + +#if defined(LUA_MODEL_SCRIPTS) +void onModelCustomScriptMenu(const char *result) +{ + ScriptData &sd = g_model.scriptsData[s_currIdx]; + + if (result == STR_UPDATE_LIST) { + if (!listSdFiles(SCRIPTS_PATH, MIXES_EXT, sizeof(sd.file), NULL)) { + POPUP_WARNING(STR_NO_BITMAPS_ON_SD); + s_menu_flags = 0; + } + } + else { + // The user choosed a lua file in the list + copySelection(sd.file, result, sizeof(sd.file)); + memset(sd.inputs, 0, sizeof(sd.inputs)); + eeDirty(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 (12*FW) +#define SCRIPT_ONE_3RD_COLUMN_POS (23*FW) + +void menuModelCustomScriptOne(uint8_t event) +{ + TITLE(STR_MENUCUSTOMSCRIPT); + + ScriptData &sd = g_model.scriptsData[s_currIdx]; + + putsStrIdx(lcdLastPos+FW, 0, "LUA", s_currIdx+1, 0); + + SUBMENU_NOTITLE(3+scriptInternalData[s_currIdx].inputsCount, { 0, 0, LABEL(inputs), 0/*repeated*/ }); + + int8_t sub = m_posVert; + + for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); + + if (i == ITEM_MODEL_CUSTOMSCRIPT_FILE) { + lcd_putsLeft(y, "Script"); + if (ZEXIST(sd.file) > 0) + lcd_putsnAtt(SCRIPT_ONE_2ND_COLUMN_POS, y, sd.file, sizeof(sd.file), attr); + else + lcd_putsiAtt(SCRIPT_ONE_2ND_COLUMN_POS, y, STR_VCSWFUNC, 0, attr); + if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && !READ_ONLY()) { + s_editMode = 0; + if (listSdFiles(SCRIPTS_PATH, MIXES_EXT, sizeof(sd.file), sd.file, LIST_NONE_SD_FILE)) { + menuHandler = onModelCustomScriptMenu; + } + else { + POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); + s_menu_flags = 0; + } + } + } + else if (i == ITEM_MODEL_CUSTOMSCRIPT_NAME) { + lcd_putsLeft(y, "Name"); + editName(SCRIPT_ONE_2ND_COLUMN_POS, y, sd.name, sizeof(sd.name), event, attr); + } + else if (i == ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL) { + lcd_putsLeft(y, "Inputs"); + } + else if (i <= ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL+scriptInternalData[s_currIdx].inputsCount) { + int inputIdx = i-ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL-1; + lcd_putsnAtt(INDENT_WIDTH, y, scriptInternalData[s_currIdx].inputs[inputIdx].name, 10, 0); + if (scriptInternalData[s_currIdx].inputs[inputIdx].type == 0) { + lcd_outdezAtt(SCRIPT_ONE_2ND_COLUMN_POS, y, g_model.scriptsData[s_currIdx].inputs[inputIdx]+scriptInternalData[s_currIdx].inputs[inputIdx].def, attr|LEFT); + if (attr) { + CHECK_INCDEC_MODELVAR(event, g_model.scriptsData[s_currIdx].inputs[inputIdx], scriptInternalData[s_currIdx].inputs[inputIdx].min-scriptInternalData[s_currIdx].inputs[inputIdx].def, scriptInternalData[s_currIdx].inputs[inputIdx].max-scriptInternalData[s_currIdx].inputs[inputIdx].def); + } + } + else { + putsMixerSource(SCRIPT_ONE_2ND_COLUMN_POS, y, g_model.scriptsData[s_currIdx].inputs[inputIdx]+scriptInternalData[s_currIdx].inputs[inputIdx].def, attr); + if (attr) { + uint8_t *source = (uint8_t *)&g_model.scriptsData[s_currIdx].inputs[inputIdx]; + CHECK_INCDEC_MODELSOURCE(event, *source, scriptInternalData[s_currIdx].inputs[inputIdx].min-scriptInternalData[s_currIdx].inputs[inputIdx].def, scriptInternalData[s_currIdx].inputs[inputIdx].max-scriptInternalData[s_currIdx].inputs[inputIdx].def); + } + } + } + } + + if (scriptInternalData[s_currIdx].outputsCount > 0) { + lcd_vline(SCRIPT_ONE_3RD_COLUMN_POS-4, FH+1, LCD_H-FH-1); + lcd_puts(SCRIPT_ONE_3RD_COLUMN_POS, FH+1, "Outputs"); + + for (int i=0; i= 0) { + s_currIdx = sub; + pushMenu(menuModelCustomScriptOne); + } + break; + } + + for (int i=0; i= 212 + #define TELEM_COL1 (1*FW) + #define TELEM_COL2 (16*FW) + #define TELEM_COL3 (28*FW) + #define TELEM_BARS_COLMIN (3*FW+56) + #define TELEM_BARS_COLMAX (20*FW-3) + #define TELEM_SCRTYPE_COL (10*FW) +#else + #define TELEM_COL1 INDENT_WIDTH + #if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_CZ) + #define TELEM_COL2 (9*FW) + #else + #define TELEM_COL2 (8*FW) + #endif + #define TELEM_BARS_COLMIN (56-3*FW) + #define TELEM_BARS_COLMAX (14*FW-3) + #define TELEM_SCRTYPE_COL (10*FW) +#endif + +#if defined(PCBTARANIS) + #define CHANNEL_ROWS (uint8_t)-1, 1, 0, 0, 0, + #define RSSI_ROWS (uint8_t)-1, 0, 0, +#else + #define CHANNEL_ROWS (uint8_t)-1, 1, 0, 2, 2, + #define RSSI_ROWS (uint8_t)-1, 1, 1, +#endif + +#if defined(GAUGES) + #define SCREEN_TYPE_ROWS 0 +#else + #define SCREEN_TYPE_ROWS (uint8_t)-1 +#endif + +#if defined(PCBSTD) + #define VARIO_RANGE_ROWS 1 +#else + #define VARIO_RANGE_ROWS 3 +#endif + +void menuModelTelemetry(uint8_t event) +{ + MENU(STR_MENUTELEMETRY, menuTabModel, e_Telemetry, ITEM_TELEMETRY_MAX+1, {0, CHANNEL_ROWS CHANNEL_ROWS RSSI_ROWS USRDATA_LINES 0, 0, IF_CPUARM(0) IF_VARIO(LABEL(Vario)) IF_VARIO(0) IF_VARIO(VARIO_RANGE_ROWS) CASE_PCBTARANIS(LABEL(TopBar)) CASE_PCBTARANIS(0) SCREEN_TYPE_ROWS, 2, 2, 2, 2, SCREEN_TYPE_ROWS, 2, 2, 2, 2, IF_CPUARM(SCREEN_TYPE_ROWS) IF_CPUARM(2) IF_CPUARM(2) IF_CPUARM(2) IF_CPUARM(2) }); + + uint8_t sub = m_posVert - 1; + +#if !defined(PCBTARANIS) + switch (event) { + case EVT_KEY_BREAK(KEY_DOWN): + case EVT_KEY_BREAK(KEY_UP): + case EVT_KEY_BREAK(KEY_LEFT): + case EVT_KEY_BREAK(KEY_RIGHT): + if (s_editMode>0 && sub<=ITEM_TELEMETRY_RSSI_ALARM2) + frskySendAlarms(); // update FrSky module when edit mode exited + break; + } +#endif + + for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS); + uint8_t attr = (sub == k ? blink : 0); + uint8_t ch = (k >= ITEM_TELEMETRY_A2_LABEL) ? 1 : 0; + FrSkyChannelData & channel = g_model.frsky.channels[ch]; + uint8_t dest=TELEM_A1-1+ch; + switch(k) { + case ITEM_TELEMETRY_A1_LABEL: + case ITEM_TELEMETRY_A2_LABEL: + lcd_putsLeft(y, STR_ACHANNEL); + lcd_outdezAtt(2*FW, y, ch+1, 0); + putsTelemetryChannel(TELEM_COL2+6*FW, y, dest, frskyData.analog[ch].value, LEFT); + break; + + case ITEM_TELEMETRY_A1_RANGE: + case ITEM_TELEMETRY_A2_RANGE: + lcd_putsLeft(y, STR_RANGE); + putsTelemetryChannel(TELEM_COL2, y, dest, 255-channel.offset, (m_posHorz<=0 ? attr : 0)|NO_UNIT|LEFT); + lcd_putsiAtt(lcdLastPos, y, STR_VTELEMUNIT, channel.type, m_posHorz!=0 ? attr : 0); + if (attr && (s_editMode>0 || p1valdiff)) { + if (m_posHorz == 0) { + uint16_t ratio = checkIncDec(event, channel.ratio, 0, 256, EE_MODEL); + if (checkIncDec_Ret) { + if (ratio == 127 && channel.multiplier > 0) { + channel.multiplier--; channel.ratio = 255; + } + else if (ratio == 256) { + if (channel.multiplier < FRSKY_MULTIPLIER_MAX) { channel.multiplier++; channel.ratio = 128; } + } + else { + channel.ratio = ratio; + } + } + } + else { + CHECK_INCDEC_MODELVAR_ZERO(event, channel.type, UNIT_A1A2_MAX); + } + } + break; + + case ITEM_TELEMETRY_A1_OFFSET: + case ITEM_TELEMETRY_A2_OFFSET: + lcd_putsLeft(y, STR_OFFSET); + putsTelemetryChannel(TELEM_COL2, y, dest, 0, LEFT|attr); + if (attr) channel.offset = checkIncDec(event, channel.offset, -256, 256, EE_MODEL); + break; + + case ITEM_TELEMETRY_A1_ALARM1: + case ITEM_TELEMETRY_A1_ALARM2: + case ITEM_TELEMETRY_A2_ALARM1: + case ITEM_TELEMETRY_A2_ALARM2: + { + uint8_t j = ((k==ITEM_TELEMETRY_A1_ALARM1 || k==ITEM_TELEMETRY_A2_ALARM1) ? 0 : 1); +#if defined(PCBTARANIS) + lcd_putsLeft(y, (j==0 ? STR_LOWALARM : STR_CRITICALALARM)); + putsTelemetryChannel(TELEM_COL2, y, dest, channel.alarms_value[j], LEFT|attr); + if (attr && (s_editMode>0 || p1valdiff)) { + channel.alarms_value[j] = checkIncDec(event, channel.alarms_value[j], 0, 255, EE_MODEL); + } +#else + lcd_putsLeft(y, STR_ALARM); + lcd_putsiAtt(TELEM_COL2, y, STR_VALARM, ALARM_LEVEL(ch, j), m_posHorz<=0 ? attr : 0); + lcd_putsiAtt(TELEM_COL2+4*FW, y, STR_VALARMFN, ALARM_GREATER(ch, j), (m_posHorz<0 || m_posHorz==1) ? attr : 0); + putsTelemetryChannel(TELEM_COL2+6*FW, y, dest, channel.alarms_value[j], ((m_posHorz<0 || m_posHorz==2) ? attr : 0) | LEFT); + + if (attr && (s_editMode>0 || p1valdiff)) { + uint8_t t; + switch (m_posHorz) { + case 0: + t = ALARM_LEVEL(ch, j); + channel.alarms_level = (channel.alarms_level & ~(3<<(2*j))) + (checkIncDecModel(event, t, 0, 3) << (2*j)); + break; + case 1: + t = ALARM_GREATER(ch, j); + if (t != checkIncDecModel(event, t, 0, 1)) { + channel.alarms_greater ^= (1 << j); + frskySendAlarms(); + } + break; + case 2: + channel.alarms_value[j] = checkIncDec(event, channel.alarms_value[j], 0, 255, EE_MODEL); + break; + } + } +#endif + break; + } + + case ITEM_TELEMETRY_RSSI_LABEL: + lcd_putsLeft(y, PSTR("RSSI")); + break; + + case ITEM_TELEMETRY_RSSI_ALARM1: + case ITEM_TELEMETRY_RSSI_ALARM2: { + uint8_t j = k-ITEM_TELEMETRY_RSSI_ALARM1; +#if defined(PCBTARANIS) + lcd_putsLeft(y, (j==0 ? STR_LOWALARM : STR_CRITICALALARM)); + lcd_outdezNAtt(TELEM_COL2, y, getRssiAlarmValue(j), LEFT|attr, 3); + if (attr && (s_editMode>0 || p1valdiff)) { + CHECK_INCDEC_MODELVAR(event, g_model.frsky.rssiAlarms[j].value, -30, 30); + } +#else + lcd_putsLeft(y, STR_ALARM); + lcd_putsiAtt(TELEM_COL2, y, STR_VALARM, ((2+j+g_model.frsky.rssiAlarms[j].level)%4), m_posHorz<=0 ? attr : 0); + lcd_putc(TELEM_COL2+4*FW, y, '<'); + lcd_outdezNAtt(TELEM_COL2+6*FW, y, getRssiAlarmValue(j), LEFT|(m_posHorz!=0 ? attr : 0), 3); + + if (attr && (s_editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.rssiAlarms[j].level, -3, 2); // circular (saves flash) + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.rssiAlarms[j].value, -30, 30); + break; + } + } +#endif + break; + } + +#if defined(FRSKY_HUB) || defined(WS_HOW_HIGH) + case ITEM_TELEMETRY_USR_LABEL: + lcd_putsLeft(y, STR_USRDATA); + break; + +#if !defined(PCBTARANIS) + case ITEM_TELEMETRY_USR_PROTO: + lcd_putsLeft(y, STR_PROTO); + lcd_putsiAtt(TELEM_COL2, y, STR_VTELPROTO, g_model.frsky.usrProto, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.usrProto, USR_PROTO_LAST); + break; +#endif + + case ITEM_TELEMETRY_USR_BLADES: + lcd_putsLeft(y, STR_BLADES); + lcd_outdezAtt(TELEM_COL2+FWNUM, y, 2+g_model.frsky.blades, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.blades, 3); + break; +#endif + + case ITEM_TELEMETRY_USR_VOLTAGE_SOURCE: + lcd_putsLeft(y, STR_VOLTAGE); + lcd_putsiAtt(TELEM_COL2, y, STR_VOLTSRC, g_model.frsky.voltsSource+1, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.voltsSource, 3); + break; + + case ITEM_TELEMETRY_USR_CURRENT_SOURCE: + lcd_putsLeft(y, STR_CURRENT); + lcd_putsiAtt(TELEM_COL2, y, STR_VOLTSRC, g_model.frsky.currentSource, attr); + if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.currentSource, 3); + break; + +#if defined(CPUARM) + case ITEM_TELEMTETRY_PERSISTENT_MAH: + g_model.frsky.mAhPersistent = onoffMenuItem(g_model.frsky.mAhPersistent, TELEM_COL2, y, STR_PERSISTENT_MAH, attr, event); + break; +#endif + +#if defined(VARIO) + case ITEM_TELEMETRY_VARIO_LABEL: + lcd_putsLeft(y, STR_VARIO); + break; + + case ITEM_TELEMETRY_VARIO_SOURCE: + lcd_putsLeft(y, STR_SOURCE); + lcd_putsiAtt(TELEM_COL2, y, STR_VARIOSRC, g_model.frsky.varioSource, attr); + if (attr) CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioSource, 0, VARIO_SOURCE_LAST); + break; + + case ITEM_TELEMETRY_VARIO_RANGE: + lcd_putsLeft(y, STR_LIMIT); +#if defined(PCBSTD) + lcd_outdezAtt(TELEM_COL2, y, 5+g_model.frsky.varioCenterMax, (m_posHorz==0 ? attr : 0)|PREC1|LEFT); + lcd_outdezAtt(TELEM_COL2+8*FW, y, 10+g_model.frsky.varioMax, (m_posHorz==1 ? attr : 0)); + if (attr && (s_editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioCenterMax, -15, +15); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioMax, -7, 7); + break; + } + } +#else + lcd_outdezAtt(TELEM_COL2, y, -10+g_model.frsky.varioMin, (m_posHorz<=0 ? attr : 0)|LEFT); + if (g_model.frsky.varioCenterMin == -16) + lcd_putsAtt(TELEM_COL2+4*FW-2, y, STR_OFF, m_posHorz==1 ? attr : 0); + else + lcd_outdezAtt(TELEM_COL2+7*FW-2, y, -5+g_model.frsky.varioCenterMin, ((m_posHorz<0 || m_posHorz==1) ? attr : 0)|PREC1); + lcd_outdezAtt(TELEM_COL2+10*FW, y, 5+g_model.frsky.varioCenterMax, ((m_posHorz<0 || m_posHorz==2) ? attr : 0)|PREC1); + lcd_outdezAtt(TELEM_COL2+13*FW+2, y, 10+g_model.frsky.varioMax, ((m_posHorz<0 || m_posHorz==3) ? attr : 0)); + if (attr && (s_editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioMin, -7, 7); + break; + case 1: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioCenterMin, -16, 5+min(10, g_model.frsky.varioCenterMax+5)); + break; + case 2: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioCenterMax, -5+max(-10, g_model.frsky.varioCenterMin-5), +15); + break; + case 3: + CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioMax, -7, 7); + break; + } + } +#endif + break; +#endif + +#if defined(PCBTARANIS) + case ITEM_TELEMETRY_TOP_BAR_LABEL: + lcd_putsLeft(y, STR_TOP_BAR); + break; + + case ITEM_TELEMETRY_TOP_BAR_ALTITUDE: + g_model.frsky.altitudeDisplayed = onoffMenuItem(g_model.frsky.altitudeDisplayed, TELEM_COL2, y, STR_ALTITUDE, attr, event); + break; +#endif + + case ITEM_TELEMETRY_SCREEN_LABEL1: + case ITEM_TELEMETRY_SCREEN_LABEL2: +#if defined(CPUARM) + case ITEM_TELEMETRY_SCREEN_LABEL3: + { + uint8_t screenIndex = (k < ITEM_TELEMETRY_SCREEN_LABEL2 ? 0 : (k < ITEM_TELEMETRY_SCREEN_LABEL3 ? 1 : 2)); + putsStrIdx(0*FW, y, STR_SCREEN, screenIndex+1); +#if defined(GAUGES) + bool screenType = IS_BARS_SCREEN(screenIndex); + if (screenType != selectMenuItem(TELEM_SCRTYPE_COL, y, PSTR(""), STR_VSCREEN, screenType, 0, 1, attr, event)) + g_model.frsky.screensType ^= (1 << screenIndex); +#endif + break; + } +#else + { + uint8_t screenIndex = (k < ITEM_TELEMETRY_SCREEN_LABEL2 ? 1 : 2); + putsStrIdx(0*FW, y, STR_SCREEN, screenIndex); +#if defined(GAUGES) + bool screenType = g_model.frsky.screensType & screenIndex; + if (screenType != selectMenuItem(TELEM_SCRTYPE_COL, y, PSTR(""), STR_VSCREEN, screenType, 0, 1, attr, event)) + g_model.frsky.screensType ^= screenIndex; +#endif + break; + } +#endif + + case ITEM_TELEMETRY_SCREEN_LINE1: + case ITEM_TELEMETRY_SCREEN_LINE2: + case ITEM_TELEMETRY_SCREEN_LINE3: + case ITEM_TELEMETRY_SCREEN_LINE4: + case ITEM_TELEMETRY_SCREEN_LINE5: + case ITEM_TELEMETRY_SCREEN_LINE6: + case ITEM_TELEMETRY_SCREEN_LINE7: + case ITEM_TELEMETRY_SCREEN_LINE8: + +#if defined(CPUARM) + case ITEM_TELEMETRY_SCREEN_LINE9: + case ITEM_TELEMETRY_SCREEN_LINE10: + case ITEM_TELEMETRY_SCREEN_LINE11: + case ITEM_TELEMETRY_SCREEN_LINE12: +#endif + + { + uint8_t screenIndex, lineIndex; + if (k < ITEM_TELEMETRY_SCREEN_LABEL2) { + screenIndex = 0; + lineIndex = k-ITEM_TELEMETRY_SCREEN_LINE1; + } +#if defined(CPUARM) + else if (k >= ITEM_TELEMETRY_SCREEN_LABEL3) { + screenIndex = 2; + lineIndex = k-ITEM_TELEMETRY_SCREEN_LINE9; + } +#endif + else { + screenIndex = 1; + lineIndex = k-ITEM_TELEMETRY_SCREEN_LINE5; + } + +#if 0 + putsStrIdx(0, y, PSTR(INDENT"Line"), lineIndex+1, m_posHorz<0 ? attr : 0); +#endif + +#if defined(GAUGES) + if (IS_BARS_SCREEN(screenIndex)) { + FrSkyBarData & bar = g_model.frsky.screens[screenIndex].bars[lineIndex]; + uint8_t barSource = bar.source; + lcd_putsiAtt(TELEM_COL1, y, STR_VTELEMCHNS, barSource, m_posHorz==0 ? attr : 0); + if (barSource) { + putsTelemetryChannel(TELEM_BARS_COLMIN, y, barSource-1, convertTelemValue(barSource, bar.barMin), (m_posHorz==1 ? attr : 0) | LEFT); + putsTelemetryChannel(TELEM_BARS_COLMAX, y, barSource-1, convertTelemValue(barSource, 255-bar.barMax), (m_posHorz==2 ? attr : 0) | LEFT); + } + else if (attr) { + MOVE_CURSOR_FROM_HERE(); + } + if (attr && (s_editMode>0 || p1valdiff)) { + switch (m_posHorz) { + case 0: + bar.source = checkIncDecModel(event, barSource, 0, TELEM_DISPLAY_MAX); + if (checkIncDec_Ret) { + bar.barMin = 0; + bar.barMax = 255-maxTelemValue(bar.source); + } + break; + case 1: + bar.barMin = checkIncDec(event, bar.barMin, 0, 254-bar.barMax, EE_MODEL|NO_INCDEC_MARKS); + break; + case 2: + bar.barMax = 255 - checkIncDec(event, 255-bar.barMax, bar.barMin+1, maxTelemValue(barSource), EE_MODEL|NO_INCDEC_MARKS); + break; + } + } + } + else +#endif + { + for (uint8_t c=0; c0 || p1valdiff)) { + CHECK_INCDEC_MODELVAR_ZERO(event, value, (lineIndex==3 && c==0) ? TELEM_STATUS_MAX : TELEM_DISPLAY_MAX); + } + } + if (attr && m_posHorz == NUM_LINE_ITEMS) { + REPEAT_LAST_CURSOR_MOVE(); + } + } + break; + } + } + } +} +#endif + +#if defined(TEMPLATES) +void menuModelTemplates(uint8_t event) +{ + SIMPLE_MENU(STR_MENUTEMPLATES, menuTabModel, e_Templates, 1+TMPL_COUNT); + + uint8_t sub = m_posVert - 1; + + if (sub < TMPL_COUNT) { + if (s_warning_result) { + s_warning_result = 0; + applyTemplate(sub); + AUDIO_WARNING2(); + } + if (event==EVT_KEY_BREAK(KEY_ENTER)) { + POPUP_CONFIRMATION(STR_VTEMPLATES+1 + (sub * LEN2_VTEMPLATES)); + s_editMode = 0; + } + } + + uint8_t y = 1 + 1*FH; + uint8_t k = 0; + for (uint8_t i=0; i + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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. + * + */ + +#ifndef myeeprom_h +#define myeeprom_h + +#include + +#define WARN_THR_BIT 0x01 +#define WARN_BEP_BIT 0x80 +#define WARN_SW_BIT 0x02 +#define WARN_MEM_BIT 0x04 +#define WARN_BVAL_BIT 0x38 + +#define WARN_THR (!(g_eeGeneral.warnOpts & WARN_THR_BIT)) +#define WARN_BEP (!(g_eeGeneral.warnOpts & WARN_BEP_BIT)) +#define WARN_SW (!(g_eeGeneral.warnOpts & WARN_SW_BIT)) +#define WARN_MEM (!(g_eeGeneral.warnOpts & WARN_MEM_BIT)) +#define BEEP_VAL ( (g_eeGeneral.warnOpts & WARN_BVAL_BIT) >>3 ) + +#if defined(PCBTARANIS) + #define EEPROM_VER 216 +#elif defined(PCBSKY9X) + #define EEPROM_VER 216 +#elif defined(CPUM2560) || defined(CPUM2561) + #define EEPROM_VER 216 +#elif defined(CPUM128) + #define EEPROM_VER 216 +#else + #define EEPROM_VER 216 +#endif + +#ifndef PACK +#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#endif + +#if defined(PCBTARANIS) + #define MAX_MODELS 60 + #define NUM_CHNOUT 32 // number of real output channels CH1-CH32 + #define MAX_PHASES 9 + #define MAX_MIXERS 64 + #define MAX_EXPOS 64 + #define NUM_CSW 32 // number of custom switches + #define NUM_CFN 32 // number of functions assigned to switches + #define MAX_SCRIPTS 3 + #define MAX_INPUTS 32 +#elif defined(CPUARM) + #define MAX_MODELS 60 + #define NUM_CHNOUT 32 // number of real output channels CH1-CH32 + #define MAX_PHASES 9 + #define MAX_MIXERS 64 + #define MAX_EXPOS 32 + #define NUM_CSW 32 // number of custom switches + #define NUM_CFN 32 // number of functions assigned to switches +#elif defined(CPUM2560) || defined(CPUM2561) + #define MAX_MODELS 30 + #define NUM_CHNOUT 16 // number of real output channels CH1-CH16 + #define MAX_PHASES 6 + #define MAX_MIXERS 32 + #define MAX_EXPOS 16 + #define NUM_CSW 15 // number of custom switches + #define NUM_CFN 24 // number of functions assigned to switches +#elif defined(CPUM128) + #define MAX_MODELS 30 + #define NUM_CHNOUT 16 // number of real output channels CH1-CH16 + #define MAX_PHASES 5 + #define MAX_MIXERS 32 + #define MAX_EXPOS 14 + #define NUM_CSW 15 // number of custom switches + #define NUM_CFN 24 // number of functions assigned to switches +#else + #define MAX_MODELS 16 + #define NUM_CHNOUT 16 // number of real output channels CH1-CH16 + #define MAX_PHASES 5 + #define MAX_MIXERS 32 + #define MAX_EXPOS 14 + #define NUM_CSW 12 // number of custom switches + #define NUM_CFN 16 // number of functions assigned to switches +#endif + +#define MAX_TIMERS 2 + +#if defined(PCBTARANIS) + enum CurveType { + CURVE_TYPE_STANDARD, + CURVE_TYPE_CUSTOM, + CURVE_TYPE_LAST = CURVE_TYPE_CUSTOM + }; +PACK(typedef struct t_CurveInfo { + uint8_t type:3; + uint8_t smooth:1; + uint8_t spare:4; + int8_t points; +}) CurveInfo; + #define MAX_CURVES 32 + #define NUM_POINTS 512 + #define CURVDATA CurveInfo +#elif defined(CPUARM) + #define MAX_CURVES 16 + #define NUM_POINTS 512 + #define CURVDATA int16_t +#else + #define MAX_CURVES 8 + #define NUM_POINTS (112-MAX_CURVES) + #define CURVDATA int8_t +#endif + +#if defined(PCBTARANIS) || defined(PCBSKY9X) + #define NUM_MODULES 2 +#else + #define NUM_MODULES 1 +#endif + +typedef int16_t gvar_t; + +#if !defined(PCBSTD) + #define LEN_GVAR_NAME 6 + #define GVAR_MAX 1024 + #define GVAR_LIMIT 500 + PACK(typedef struct { + char name[LEN_GVAR_NAME]; + uint8_t popup:1; + uint8_t spare:7; + }) global_gvar_t; +#endif + +#define RESERVE_RANGE_FOR_GVARS 10 +// even we do not spend space in EEPROM for 10 GVARS, we reserve the space inside the range of values, like offset, weight, etc. + +#if defined(PCBSTD) && defined(GVARS) + #define MAX_GVARS 5 + #define MODEL_GVARS_DATA gvar_t gvars[MAX_GVARS]; + #define PHASE_GVARS_DATA + #define GVAR_VALUE(x, p) g_model.gvars[x] +#elif defined(PCBSTD) + #define MAX_GVARS 0 + #define MODEL_GVARS_DATA + #define PHASE_GVARS_DATA +#else + #if defined(CPUARM) + #define MAX_GVARS 9 + #else + #define MAX_GVARS 5 + #endif + #define MODEL_GVARS_DATA global_gvar_t gvars[MAX_GVARS]; + #define PHASE_GVARS_DATA gvar_t gvars[MAX_GVARS] + #define GVAR_VALUE(x, p) g_model.phaseData[p].gvars[x] +#endif + +PACK(typedef struct t_TrainerMix { + uint8_t srcChn:6; // 0-7 = ch1-8 + uint8_t mode:2; // off,add-mode,subst-mode + int8_t studWeight; +}) TrainerMix; + +PACK(typedef struct t_TrainerData { + int16_t calib[4]; + TrainerMix mix[4]; +}) TrainerData; + +PACK(typedef struct t_FrSkyRSSIAlarm { + int8_t level:2; + int8_t value:6; +}) FrSkyRSSIAlarm; + +#if defined(PCBTARANIS) +enum MainViews { + VIEW_TIMERS, + VIEW_INPUTS, + VIEW_SWITCHES, + VIEW_COUNT +}; +#else +enum MainViews { + VIEW_OUTPUTS_VALUES, + VIEW_OUTPUTS_BARS, + VIEW_INPUTS, + VIEW_TIMER2, + VIEW_COUNT +}; +#endif + +enum BeeperMode { + e_mode_quiet = -2, + e_mode_alarms, + e_mode_nokeys, + e_mode_all +}; + +#if defined(CPUARM) + #define EXTRA_GENERAL_FIELDS \ + uint8_t backlightBright; \ + int8_t currentCalib; \ + int8_t temperatureWarn; \ + uint8_t mAhWarn; \ + uint16_t mAhUsed; \ + uint32_t globalTimer; \ + int8_t temperatureCalib; \ + uint8_t btBaudrate; \ + uint8_t optrexDisplay; \ + uint8_t sticksGain; \ + uint8_t rotarySteps; \ + uint8_t countryCode; \ + uint8_t imperial; \ + char ttsLanguage[2]; \ + int8_t beepVolume; \ + int8_t wavVolume; \ + int8_t varioVolume; \ + int8_t backgroundVolume; +#elif defined(PXX) + #define EXTRA_GENERAL_FIELDS uint8_t countryCode; +#else + #define EXTRA_GENERAL_FIELDS +#endif + +PACK(typedef struct t_ModuleData { + int8_t rfProtocol; + uint8_t channelsStart; + int8_t channelsCount; // 0=8 channels + uint8_t failsafeMode; + int16_t failsafeChannels[NUM_CHNOUT]; + int8_t ppmDelay; + int8_t ppmFrameLength; + uint8_t ppmPulsePol; +}) ModuleData; + +#define SET_DEFAULT_PPM_FRAME_LENGTH(idx) g_model.moduleData[idx].ppmFrameLength = 4 * max((int8_t)0, g_model.moduleData[idx].channelsCount) + +#define MAX_SCRIPT_INPUTS 10 +#define MAX_SCRIPT_OUTPUTS 6 +PACK(typedef struct t_ScriptData { + char file[10]; + char name[10]; + int8_t inputs[MAX_SCRIPT_INPUTS]; +}) ScriptData; + +#if defined(PCBTARANIS) + enum ModuleIndex { + INTERNAL_MODULE, + EXTERNAL_MODULE, + TRAINER_MODULE + }; + #define MODELDATA_BITMAP char bitmap[LEN_BITMAP_NAME]; + #define MODELDATA_EXTRA uint8_t externalModule; uint8_t trainerMode; ModuleData moduleData[NUM_MODULES+1]; char curveNames[MAX_CURVES][6]; ScriptData scriptsData[MAX_SCRIPTS]; char inputNames[MAX_INPUTS][4]; + #define LIMITDATA_EXTRA char name[LEN_CHANNEL_NAME]; int8_t curve; + #define swstate_t uint16_t +#elif defined(PCBSKY9X) + #define MODELDATA_BITMAP + #define MODELDATA_EXTRA ModuleData moduleData[NUM_MODULES]; + #define LIMITDATA_EXTRA + #define swstate_t uint8_t +#else + #define MODELDATA_BITMAP + #define MODELDATA_EXTRA + #define LIMITDATA_EXTRA + #define swstate_t uint8_t +#endif + +enum BacklightMode { + e_backlight_mode_off = 0, + e_backlight_mode_keys = 1, + e_backlight_mode_sticks = 2, + e_backlight_mode_all = e_backlight_mode_keys+e_backlight_mode_sticks, + e_backlight_mode_on +}; + +#if defined(FSPLASH) || defined(XSPLASH) + #define SPLASH_MODE uint8_t splashMode:3 +#else + #define SPLASH_MODE uint8_t splashMode:1; uint8_t spare4:2 +#endif + +#define ALTERNATE_VIEW 0x10 +PACK(typedef struct t_EEGeneral { + uint8_t version; + uint16_t variant; + int16_t calibMid[NUM_STICKS+NUM_POTS]; + int16_t calibSpanNeg[NUM_STICKS+NUM_POTS]; + int16_t calibSpanPos[NUM_STICKS+NUM_POTS]; + uint16_t chkSum; + int8_t currModel; + uint8_t contrast; + uint8_t vBatWarn; + int8_t vBatCalib; + int8_t backlightMode; + TrainerData trainer; + uint8_t view; // index of view in main screen + int8_t buzzerMode:2; // -2=quiet, -1=only alarms, 0=no keys, 1=all + uint8_t fai:1; + int8_t beepMode:2; // -2=quiet, -1=only alarms, 0=no keys, 1=all + uint8_t alarmsFlash:1; + uint8_t disableMemoryWarning:1; + uint8_t disableAlarmWarning:1; + uint8_t stickMode:2; + int8_t timezone:5; + uint8_t spare1:1; + uint8_t inactivityTimer; + uint8_t mavbaud:3; + SPLASH_MODE; /* 3bits */ + int8_t hapticMode:2; // -2=quiet, -1=only alarms, 0=no keys, 1=all + uint8_t blOffBright:4; + uint8_t blOnBright:4; + uint8_t lightAutoOff; + uint8_t templateSetup; // RETA order for receiver channels + int8_t PPM_Multiplier; + int8_t hapticLength; + uint8_t reNavigation; // not used on STOCK board + int8_t beepLength:3; + uint8_t hapticStrength:3; + uint8_t gpsFormat:1; + uint8_t unexpectedShutdown:1; + uint8_t speakerPitch; + int8_t speakerVolume; + int8_t vBatMin; + int8_t vBatMax; + + EXTRA_GENERAL_FIELDS + + swstate_t switchUnlockStates; + +}) EEGeneral; + +#if defined(PCBTARANIS) + #define LEN_MODEL_NAME 12 + #define LEN_BITMAP_NAME 10 + #define LEN_EXPOMIX_NAME 8 + #define LEN_FP_NAME 10 + #define LEN_CHANNEL_NAME 6 +#elif defined(PCBSKY9X) + #define LEN_MODEL_NAME 10 + #define LEN_EXPOMIX_NAME 6 + #define LEN_FP_NAME 6 +#else + #define LEN_MODEL_NAME 10 + #define LEN_FP_NAME 6 +#endif + +#if defined(PCBTARANIS) +enum CurveRefType { + CURVE_REF_DIFF, + CURVE_REF_EXPO, + CURVE_REF_FUNC, + CURVE_REF_CUSTOM +}; +PACK(typedef struct t_CurveRef { + uint8_t type; + int8_t value; +}) CurveRef; +#else + #define MODE_DIFFERENTIAL 0 + #define MODE_EXPO 0 + #define MODE_CURVE 1 +#endif + +#if defined(PCBTARANIS) +PACK(typedef struct t_ExpoData { + uint8_t srcRaw; + uint16_t scale; + uint8_t chn; + int8_t swtch; + uint16_t phases; + int8_t weight; + int8_t carryTrim; + char name[LEN_EXPOMIX_NAME]; + int8_t offset; + CurveRef curve; + uint8_t spare; +}) ExpoData; +#define MIN_EXPO_WEIGHT -100 +#define EXPO_VALID(ed) ((ed)->srcRaw) +#define EXPO_MODE_ENABLE(ed, v) (true) +#elif defined(CPUARM) +PACK(typedef struct t_ExpoData { + uint8_t mode; // 0=end, 1=pos, 2=neg, 3=both + uint8_t chn; + int8_t swtch; + uint16_t phases; + int8_t weight; + uint8_t curveMode; + char name[LEN_EXPOMIX_NAME]; + int8_t curveParam; +}) ExpoData; +#define MIN_EXPO_WEIGHT 0 +#define EXPO_VALID(ed) ((ed)->mode) +#define EXPO_MODE_ENABLE(ed, v) ((v<0 && (ed->mode&1)) || (v>=0 && (ed->mode&2))) +#elif defined(CPUM2560) || defined(CPUM2561) +PACK(typedef struct t_ExpoData { + uint8_t mode:2; // 0=end, 1=pos, 2=neg, 3=both + uint8_t chn:2; + uint8_t curveMode:1; + uint8_t spare:3; + uint8_t phases; + int8_t swtch; + uint8_t weight; + int8_t curveParam; +}) ExpoData; +#define MIN_EXPO_WEIGHT 0 +#define EXPO_VALID(ed) ((ed)->mode) +#define EXPO_MODE_ENABLE(ed, v) ((v<0 && (ed->mode&1)) || (v>=0 && (ed->mode&2))) +#else +PACK(typedef struct t_ExpoData { + uint8_t mode:2; // 0=end, 1=pos, 2=neg, 3=both + int8_t swtch:6; + uint8_t chn:2; + uint8_t phases:5; + uint8_t curveMode:1; + uint8_t weight; // One spare bit here (used for GVARS) + int8_t curveParam; +}) ExpoData; +#define MIN_EXPO_WEIGHT 0 +#define EXPO_VALID(ed) ((ed)->mode) +#define EXPO_MODE_ENABLE(ed, v) ((v<0 && (ed->mode&1)) || (v>=0 && (ed->mode&2))) +#endif + +#if defined(CPUARM) + #define limit_min_max_t int16_t + #define LIMIT_MAX(lim) (GV_IS_GV_VALUE(lim->max+1000, 0, 1250) ? GET_GVAR(lim->max+1000, 0, 125, s_perout_flight_phase)*10 : lim->max+1000) + #define LIMIT_MIN(lim) (GV_IS_GV_VALUE(lim->min-1000, -1250, 0) ? GET_GVAR(lim->min-1000, -125, 0, s_perout_flight_phase)*10 : lim->min-1000) + #define LIMIT_OFS(lim) (GV_IS_GV_VALUE(lim->offset, -1000, 1000) ? GET_GVAR(lim->offset, -100, 100, s_perout_flight_phase)*10 : lim->offset) + #define LIMIT_MAX_RESX(lim) calc1000toRESX(LIMIT_MAX(lim)) + #define LIMIT_MIN_RESX(lim) calc1000toRESX(LIMIT_MIN(lim)) + #define LIMIT_OFS_RESX(lim) calc1000toRESX(LIMIT_OFS(lim)) +#else + #define limit_min_max_t int8_t + #define LIMIT_MAX(lim) (lim->max+100) + #define LIMIT_MIN(lim) (lim->min-100) + #define LIMIT_OFS(lim) (lim->offset) + #define LIMIT_MAX_RESX(lim) calc100toRESX(LIMIT_MAX(lim)) + #define LIMIT_MIN_RESX(lim) calc100toRESX(LIMIT_MIN(lim)) + #define LIMIT_OFS_RESX(lim) calc1000toRESX(LIMIT_OFS(lim)) +#endif + +PACK(typedef struct t_LimitData { + limit_min_max_t min; + limit_min_max_t max; + int8_t ppmCenter; + int16_t offset:14; + uint16_t symetrical:1; + uint16_t revert:1; + + LIMITDATA_EXTRA + +}) LimitData; + +#define TRIM_OFF (1) +#define TRIM_ON (0) +#define TRIM_RUD (-1) +#define TRIM_ELE (-2) +#define TRIM_THR (-3) +#define TRIM_AIL (-4) + +#define MLTPX_ADD 0 +#define MLTPX_MUL 1 +#define MLTPX_REP 2 + +#if defined(CPUARM) +#define DELAY_STEP 10 +#define SLOW_STEP 10 +#define DELAY_MAX (25*DELAY_STEP) /* 25 seconds */ +#define SLOW_MAX (25*SLOW_STEP) /* 25 seconds */ +#if defined(PCBTARANIS) +PACK(typedef struct t_MixData { + uint8_t destCh; + uint16_t phases; + uint8_t mltpx; // multiplex method: 0 means +=, 1 means *=, 2 means := + int16_t weight; + int8_t swtch; + CurveRef curve; + uint8_t mixWarn:4; // mixer warning + uint8_t srcVariant:4; + uint8_t delayUp; + uint8_t delayDown; + uint8_t speedUp; + uint8_t speedDown; + uint8_t srcRaw; + int16_t offset; + char name[LEN_EXPOMIX_NAME]; + uint8_t spare; +}) MixData; +#else +PACK(typedef struct t_MixData { + uint8_t destCh; + uint16_t phases; + uint8_t curveMode:1; + uint8_t noExpo:1; + int8_t carryTrim:3; + uint8_t mltpx:2; // multiplex method: 0 means +=, 1 means *=, 2 means := + uint8_t spare:1; + int16_t weight; + int8_t swtch; + int8_t curveParam; + uint8_t mixWarn:4; // mixer warning + uint8_t srcVariant:4; + uint8_t delayUp; + uint8_t delayDown; + uint8_t speedUp; + uint8_t speedDown; + uint8_t srcRaw; + int16_t offset; + char name[LEN_EXPOMIX_NAME]; +}) MixData; +#endif +#define MD_WEIGHT(md) (md->weight) +#define MD_WEIGHT_TO_UNION(md, var) var.word = md->weight +#define MD_UNION_TO_WEIGHT(var, md) md->weight = var.word +// #define MD_SETWEIGHT(md, val) md->weight = val + +PACK( union u_int8int16_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; +}); + +#define MD_OFFSET(md) (md->offset) +#define MD_OFFSET_TO_UNION(md, var) var.word = md->offset +#define MD_UNION_TO_OFFSET(var, md) md->offset = var.word +// #define MD_SETOFFSET(md, val) md->offset = val + +#else +#define DELAY_STEP 2 +#define SLOW_STEP 2 +#define DELAY_MAX 15 /* 7.5 seconds */ +#define SLOW_MAX 15 /* 7.5 seconds */ + +#if defined(CPUM2560) || defined(CPUM2561) +PACK(typedef struct t_MixData { + uint8_t destCh:4; // 0, 1..NUM_CHNOUT + uint8_t curveMode:1; // O=curve, 1=differential + uint8_t noExpo:1; + uint8_t weightMode:1; + uint8_t offsetMode:1; + uint8_t srcRaw; + int8_t weight; + int8_t swtch; + uint8_t phases; + uint8_t mltpx:2; // multiplex method: 0 means +=, 1 means *=, 2 means := + int8_t carryTrim:3; + uint8_t mixWarn:2; // mixer warning + uint8_t spare:1; + uint8_t delayUp:4; + uint8_t delayDown:4; + uint8_t speedUp:4; + uint8_t speedDown:4; + int8_t curveParam; + int8_t offset; +}) MixData; +#else +PACK(typedef struct t_MixData { + uint8_t destCh:4; // 0, 1..NUM_CHNOUT + uint8_t curveMode:1; // O=curve, 1=differential + uint8_t noExpo:1; + uint8_t weightMode:1; + uint8_t offsetMode:1; + int8_t weight; + int8_t swtch:6; + uint8_t mltpx:2; // multiplex method: 0 means +=, 1 means *=, 2 means := + uint8_t phases:5; + int8_t carryTrim:3; + uint8_t srcRaw:6; + uint8_t mixWarn:2; // mixer warning + uint8_t delayUp:4; + uint8_t delayDown:4; + uint8_t speedUp:4; + uint8_t speedDown:4; + int8_t curveParam; + int8_t offset; +}) MixData; +#endif +PACK( union u_gvarint_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; + + u_gvarint_t(int8_t l, uint8_t h) {bytes_t.lo=l; bytes_t.hi=h?255:0;} // hi bit is negativ sign + +private: + // prevent unwanted constructors, also saves program + u_gvarint_t() {} + u_gvarint_t(const u_gvarint_t&) {} +}); +#define MD_WEIGHT(md) (u_gvarint_t(md->weight,md->weightMode).word) + +PACK( union u_int8int16_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; +}); + +#define MD_WEIGHT_TO_UNION(md, var) var.bytes_t.lo=md->weight; var.bytes_t.hi=md->weightMode?255:0 +#define MD_UNION_TO_WEIGHT(var, md) md->weight=var.bytes_t.lo; if (var.word<0) md->weightMode=1; else md->weightMode=0 +// #define MD_SETWEIGHT(md, val) md->weight=val; if (val<0) md->weightMode=1; else md->weightMode=0 + +#define MD_OFFSET(md) (u_gvarint_t(md->offset,md->offsetMode).word) +#define MD_OFFSET_TO_UNION(md, var) var.bytes_t.lo=md->offset; var.bytes_t.hi=md->offsetMode?255:0 +#define MD_UNION_TO_OFFSET(var, md) md->offset=var.bytes_t.lo; if (var.word<0) md->offsetMode=1; else md->offsetMode=0 /* set negative sign */ +// #define MD_SETOFFSET(md, val) md->offset=val; if (val<0) md->offsetMode=1; else md->offsetMode=0 + +#endif + +#if defined(CPUARM) +#define MAX_CSW_DURATION 120 /*60s*/ +#define MAX_CSW_DELAY 120 /*60s*/ +#define MAX_CSW_ANDSW MAX_SWITCH +typedef int16_t csw_telemetry_value_t; +PACK(typedef struct t_CustomSwData { // Custom Switches data + int16_t v1; + int16_t v2; + uint8_t func; + uint8_t delay; + uint8_t duration; + int8_t andsw; +}) CustomSwData; +#else +typedef uint8_t csw_telemetry_value_t; +#define MAX_CSW_ANDSW 15 +PACK(typedef struct t_CustomSwData { // Custom Switches data + int8_t v1; //input + int8_t v2; //offset + uint8_t func:4; + uint8_t andsw:4; +}) CustomSwData; +#endif + +enum Functions { +#if defined(CPUARM) + FUNC_SAFETY_CH1, + FUNC_SAFETY_CH16=FUNC_SAFETY_CH1+15, +#else + FUNC_SAFETY_GROUP1, + FUNC_SAFETY_GROUP2, + FUNC_SAFETY_GROUP3, + FUNC_SAFETY_GROUP4, +#endif + FUNC_TRAINER, + FUNC_TRAINER_RUD, + FUNC_TRAINER_ELE, + FUNC_TRAINER_THR, + FUNC_TRAINER_AIL, + FUNC_INSTANT_TRIM, + FUNC_PLAY_SOUND, +#if !defined(PCBTARANIS) + FUNC_HAPTIC, +#endif + FUNC_RESET, + FUNC_VARIO, + FUNC_PLAY_TRACK, +#if !defined(CPUARM) + FUNC_PLAY_BOTH, +#endif + FUNC_PLAY_VALUE, +#if !defined(PCBSTD) + FUNC_LOGS, +#endif +#if defined(CPUARM) + FUNC_VOLUME, +#endif + FUNC_BACKLIGHT, +#if defined(CPUARM) + FUNC_BACKGND_MUSIC, + FUNC_BACKGND_MUSIC_PAUSE, +#endif +#if defined(GVARS) + FUNC_ADJUST_GV1, + FUNC_ADJUST_GVLAST = (FUNC_ADJUST_GV1 + (MAX_GVARS-1)), +#endif +#if defined(DEBUG) + FUNC_TEST, // should remain the last before MAX as not added in companion9x +#endif + FUNC_MAX +}; + +#if defined(GVARS) + #define IS_ADJUST_GV_FUNCTION(sd) (CFN_FUNC(sd) >= FUNC_ADJUST_GV1 && CFN_FUNC(sd) <= FUNC_ADJUST_GVLAST) +#else + #define IS_ADJUST_GV_FUNCTION(sd) (0) +#endif + +#if defined(VOICE) + #define HAS_REPEAT_PARAM(sd) (CFN_FUNC(sd) == FUNC_PLAY_SOUND || (CFN_FUNC(sd) >= FUNC_PLAY_TRACK && CFN_FUNC(sd) <= FUNC_PLAY_VALUE)) +#else + #define HAS_REPEAT_PARAM(sd) (CFN_FUNC(sd) == FUNC_PLAY_SOUND) +#endif + +enum ResetFunctionParam { + FUNC_RESET_TIMER1, + FUNC_RESET_TIMER2, + FUNC_RESET_ALL, +#if defined(FRSKY) + FUNC_RESET_TELEMETRY, +#endif +#if ROTARY_ENCODERS > 0 + FUNC_RESET_ROTENC1, +#endif +#if ROTARY_ENCODERS > 1 + FUNC_RESET_ROTENC2, +#endif + FUNC_RESET_PARAMS_COUNT, + FUNC_RESET_PARAM_LAST = FUNC_RESET_PARAMS_COUNT-1 +}; + +enum AdjustGvarFunctionParam { + FUNC_ADJUST_GVAR_CONSTANT, + FUNC_ADJUST_GVAR_SOURCE, + FUNC_ADJUST_GVAR_GVAR, + FUNC_ADJUST_GVAR_INC, +}; + +#if defined(CPUARM) +#if defined(PCBTARANIS) + #define LEN_CFN_NAME 10 +#else + #define LEN_CFN_NAME 6 +#endif +PACK(typedef struct t_CustomFnData { // Function Switches data + int8_t swtch; + uint8_t func; + PACK(union { + char name[LEN_CFN_NAME]; + struct { + int16_t val; + int16_t ext1; + int16_t ext2; + } composite; + }) param; + uint8_t mode:2; + uint8_t active:6; +}) CustomFnData; +#define CFN_EMPTY(p) (!(p)->swtch) +#define CFN_FUNC(p) ((p)->func) +#define CFN_ACTIVE(p) ((p)->active) +#define CFN_CH_NUMBER(p) (CFN_FUNC(p)) +#define CFN_PLAY_REPEAT(p) ((p)->active) +#define CFN_PLAY_REPEAT_MUL 1 +#define CFN_PLAY_REPEAT_NOSTART 0x3F +#define CFN_GVAR_MODE(p) ((p)->mode) +#define CFN_PARAM(p) ((p)->param.composite.val) +#define CFN_RESET(p) (p->active = 0, memset(&(p)->param, 0, sizeof((p)->param))) +#else +PACK(typedef struct t_CustomFnData { + int8_t swtch; // input + union { + struct { + uint8_t param:3; + uint8_t func:5; + } func_param; + + struct { + uint8_t active:1; + uint8_t param:2; + uint8_t func:5; + } func_param_enable; + + struct { + uint8_t active:1; + uint8_t func:7; + } func_safety; + } internal; + uint8_t param; +}) CustomFnData; +#define CFN_FUNC(p) ((p)->internal.func_param.func) +#define CFN_ACTIVE(p) ((p)->internal.func_param_enable.active) +#define CFN_CH_NUMBER(p) ((p)->internal.func_safety.func) +#define CFN_PLAY_REPEAT(p) ((p)->internal.func_param.param) +#define CFN_PLAY_REPEAT_MUL 10 +#define CFN_GVAR_MODE(p) ((p)->internal.func_param_enable.param) +#define CFN_PARAM(p) ((p)->param) +#define CFN_RESET(p) ((p)->internal.func_param_enable.active = 0, CFN_PARAM(p) = 0) +#endif + +enum TelemetryUnit { + UNIT_VOLTS, + UNIT_AMPS, + UNIT_METERS_PER_SECOND, + UNIT_RAW, + UNIT_KMH, + UNIT_METERS, + UNIT_DEGREES, + UNIT_PERCENT, + UNIT_MILLIAMPS, + UNIT_A1A2_MAX = UNIT_MILLIAMPS, + UNIT_MAH, + UNIT_WATTS, + UNIT_DBM, + UNIT_FEET, + UNIT_KTS, + UNIT_HOURS, + UNIT_MINUTES, + UNIT_SECONDS, + UNIT_RPMS, + UNIT_G, +}; + +#if defined(CPUARM) +PACK(typedef struct t_FrSkyChannelData { + uint8_t ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. + int16_t offset:12; + uint16_t type:4; // channel unit (0=volts, ...) + uint8_t alarms_value[2]; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. + uint8_t alarms_level:4; + uint8_t alarms_greater:2; // 0=LT(<), 1=GT(>) + uint8_t spare:2; + uint8_t multiplier; // 0=no multiplier, 1=*2 multiplier +}) FrSkyChannelData; +#else +PACK(typedef struct t_FrSkyChannelData { + uint8_t ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. + int16_t offset:12; + uint16_t type:4; // channel unit (0=volts, ...) + uint8_t alarms_value[2]; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc. + uint8_t alarms_level:4; + uint8_t alarms_greater:2; // 0=LT(<), 1=GT(>) + uint8_t multiplier:2; // 0=no multiplier, 1=*2 multiplier +}) FrSkyChannelData; +#endif + +enum TelemetrySource { + TELEM_NONE, + TELEM_TX_VOLTAGE, + TELEM_TM1, + TELEM_TM2, + TELEM_RSSI_TX, + TELEM_RSSI_RX, + TELEM_A1, + TELEM_A2, + TELEM_ALT, + TELEM_RPM, + TELEM_FUEL, + TELEM_T1, + TELEM_T2, + TELEM_SPEED, + TELEM_DIST, + TELEM_GPSALT, + TELEM_CELL, + TELEM_CELLS_SUM, + TELEM_VFAS, + TELEM_CURRENT, + TELEM_CONSUMPTION, + TELEM_POWER, + TELEM_ACCx, + TELEM_ACCy, + TELEM_ACCz, + TELEM_HDG, + TELEM_VSPD, + TELEM_MIN_A1, + TELEM_MIN_A2, + TELEM_MIN_ALT, + TELEM_MAX_ALT, + TELEM_MAX_RPM, + TELEM_MAX_T1, + TELEM_MAX_T2, + TELEM_MAX_SPEED, + TELEM_MAX_DIST, + // TODO TELEM_MIN_CELL, + // TODO TELEM_MIN_VFAS, + TELEM_MAX_CURRENT, + TELEM_MAX_POWER, + TELEM_ACC, + TELEM_GPS_TIME, + TELEM_CSW_MAX = TELEM_POWER, + TELEM_NOUSR_MAX = TELEM_A2, +#if defined(FRSKY) + TELEM_DISPLAY_MAX = TELEM_MAX_POWER, +#else + TELEM_DISPLAY_MAX = TELEM_TM2, // because used also in PlayValue +#endif + TELEM_STATUS_MAX = TELEM_GPS_TIME, +#if defined(FRSKY_SPORT) + TELEM_SWR = TELEM_RSSI_TX, + TELEM_FIRST_STREAMED_VALUE = TELEM_RSSI_RX, +#else + TELEM_FIRST_STREAMED_VALUE = TELEM_RSSI_TX, +#endif +}; + +enum VarioSource { +#if !defined(FRSKY_SPORT) + VARIO_SOURCE_ALTI, + VARIO_SOURCE_ALTI_PLUS, +#endif + VARIO_SOURCE_VARIO, + VARIO_SOURCE_A1, + VARIO_SOURCE_A2, + VARIO_SOURCE_LAST = VARIO_SOURCE_A2 +}; + +#if defined(FRSKY_HUB) + #define NUM_TELEMETRY TELEM_CSW_MAX +#elif defined(WS_HOW_HIGH) + #define NUM_TELEMETRY TELEM_ALT +#elif defined(FRSKY) + #define NUM_TELEMETRY TELEM_A2 +#elif defined(MAVLINK) + #define NUM_TELEMETRY 4 +#else + #define NUM_TELEMETRY TELEM_TM2 +#endif + +PACK(typedef struct t_FrSkyBarData { + uint8_t source; + uint8_t barMin; // minimum for bar display + uint8_t barMax; // ditto for max display (would usually = ratio) +}) FrSkyBarData; + +#if defined(PCBTARANIS) + #define NUM_LINE_ITEMS 3 +#else + #define NUM_LINE_ITEMS 2 +#endif +PACK(typedef struct t_FrSkyLineData { + uint8_t sources[NUM_LINE_ITEMS]; +}) FrSkyLineData; + +typedef union t_FrSkyScreenData { + FrSkyBarData bars[4]; + FrSkyLineData lines[4]; +} FrSkyScreenData; + +enum FrskyUsrProtocols { + USR_PROTO_NONE, + USR_PROTO_FRSKY, + USR_PROTO_WS_HOW_HIGH, + USR_PROTO_LAST = USR_PROTO_WS_HOW_HIGH, +}; + +enum FrskySource { + FRSKY_SOURCE_NONE, + FRSKY_SOURCE_A1, + FRSKY_SOURCE_A2, + FRSKY_SOURCE_FAS, + FRSKY_SOURCE_CELLS, +}; + +#if defined(CPUARM) +#define MAX_FRSKY_SCREENS 3 +PACK(typedef struct t_FrSkyData { + FrSkyChannelData channels[2]; + uint8_t usrProto; // Protocol in FrSky user data, 0=None, 1=FrSky hub, 2=WS HowHigh, 3=Halcyon + uint8_t voltsSource:7; + uint8_t altitudeDisplayed:1; + uint8_t blades; // How many blades for RPMs, 0=2 blades, 1=3 blades + uint8_t currentSource; + uint8_t screensType; + FrSkyScreenData screens[MAX_FRSKY_SCREENS]; + uint8_t varioSource; + int8_t varioCenterMax; + int8_t varioCenterMin; + int8_t varioMin; + int8_t varioMax; + FrSkyRSSIAlarm rssiAlarms[2]; + uint16_t mAhPersistent:1; + uint16_t storedMah:15; + int8_t fasOffset; +}) FrSkyData; +#else +#define MAX_FRSKY_SCREENS 2 +PACK(typedef struct t_FrSkyData { + FrSkyChannelData channels[2]; + uint8_t usrProto:2; // Protocol in FrSky user data, 0=None, 1=FrSky hub, 2=WS HowHigh, 3=Halcyon + uint8_t blades:2; // How many blades for RPMs, 0=2 blades, 1=3 blades + uint8_t screensType:2; + uint8_t voltsSource:2; + int8_t varioMin:4; + int8_t varioMax:4; + FrSkyRSSIAlarm rssiAlarms[2]; + FrSkyScreenData screens[MAX_FRSKY_SCREENS]; + uint8_t varioSource:3; + int8_t varioCenterMin:5; + uint8_t currentSource:3; + int8_t varioCenterMax:5; + int8_t fasOffset; +}) FrSkyData; +#endif + +#if defined(MAVLINK) +PACK(typedef struct t_MavlinkData { + uint8_t rc_rssi_scale:4; + uint8_t pc_rssi_en:1; + uint8_t spare1:3; + uint8_t spare2[3]; +}) MavlinkData; +#endif + +PACK(typedef struct t_SwashRingData { // Swash Ring data + uint8_t invertELE:1; + uint8_t invertAIL:1; + uint8_t invertCOL:1; + uint8_t type:5; + uint8_t collectiveSource; + uint8_t value; +}) SwashRingData; + +#define TRIM_EXTENDED_MAX 500 +#define TRIM_EXTENDED_MIN (-TRIM_EXTENDED_MAX) +#define TRIM_MAX 125 +#define TRIM_MIN (-TRIM_MAX) + +#define ROTARY_ENCODER_MAX 1024 + +#if defined(PCBTARANIS) +#define NUM_ROTARY_ENCODERS 0 +#define NUM_ROTARY_ENCODERS_EXTRA 0 +#define ROTARY_ENCODER_ARRAY_EXTRA +#define ROTARY_ENCODER_ARRAY int16_t rotaryEncoders[1]; +#elif defined(PCBSKY9X) +#define NUM_ROTARY_ENCODERS_EXTRA 0 +#define NUM_ROTARY_ENCODERS 1 +#define ROTARY_ENCODER_ARRAY int16_t rotaryEncoders[1]; +#define ROTARY_ENCODER_ARRAY_EXTRA +#elif defined(PCBGRUVIN9X) && ROTARY_ENCODERS > 2 +#define NUM_ROTARY_ENCODERS_EXTRA 2 +#define NUM_ROTARY_ENCODERS (2+NUM_ROTARY_ENCODERS_EXTRA) +#define ROTARY_ENCODER_ARRAY int16_t rotaryEncoders[2]; +#define ROTARY_ENCODER_ARRAY_EXTRA int16_t rotaryEncodersExtra[MAX_PHASES][NUM_ROTARY_ENCODERS_EXTRA]; +#elif defined(CPUM2560) && ROTARY_ENCODERS <= 2 +#define NUM_ROTARY_ENCODERS_EXTRA 0 +#define NUM_ROTARY_ENCODERS 2 +#define ROTARY_ENCODER_ARRAY int16_t rotaryEncoders[2]; +#define ROTARY_ENCODER_ARRAY_EXTRA +#else +#define NUM_ROTARY_ENCODERS_EXTRA 0 +#define NUM_ROTARY_ENCODERS 0 +#define ROTARY_ENCODER_ARRAY +#define ROTARY_ENCODER_ARRAY_EXTRA +#endif + +#if defined(PCBSTD) + #define TRIMS_ARRAY int8_t trim[4]; int8_t trim_ext:8 + #define TRIMS_ARRAY_SIZE 5 +#else + #define TRIMS_ARRAY int16_t trim[4] + #define TRIMS_ARRAY_SIZE 8 +#endif + +#if defined(CPUARM) +PACK(typedef struct t_PhaseData { + TRIMS_ARRAY; + int8_t swtch; // swtch of phase[0] is not used + char name[LEN_FP_NAME]; + uint8_t fadeIn; + uint8_t fadeOut; + ROTARY_ENCODER_ARRAY; + PHASE_GVARS_DATA; +}) PhaseData; +#else +PACK(typedef struct t_PhaseData { + TRIMS_ARRAY; + int8_t swtch; // swtch of phase[0] is not used + char name[LEN_FP_NAME]; + uint8_t fadeIn:4; + uint8_t fadeOut:4; + ROTARY_ENCODER_ARRAY; + PHASE_GVARS_DATA; +}) PhaseData; +#endif + +enum SwitchSources { + SWSRC_NONE = 0, + + SWSRC_FIRST_SWITCH, + +#if defined(PCBTARANIS) + SWSRC_SA0 = SWSRC_FIRST_SWITCH, + SWSRC_SA1, + SWSRC_SA2, + SWSRC_SB0, + SWSRC_SB1, + SWSRC_SB2, + SWSRC_SC0, + SWSRC_SC1, + SWSRC_SC2, + SWSRC_SD0, + SWSRC_SD1, + SWSRC_SD2, + SWSRC_SE0, + SWSRC_SE1, + SWSRC_SE2, + SWSRC_SF0, + SWSRC_SF2, + SWSRC_SG0, + SWSRC_SG1, + SWSRC_SG2, + SWSRC_SH0, + SWSRC_SH2, +#else + SWSRC_ID0 = SWSRC_FIRST_SWITCH, + SWSRC_ID1, + SWSRC_ID2, +#if defined(EXTRA_3POS) + SWSRC_ID3, + SWSRC_ID4, + SWSRC_ID5, +#endif + SWSRC_THR, + SWSRC_RUD, + SWSRC_ELE, + SWSRC_AIL, + SWSRC_GEA, + SWSRC_TRN, +#endif + + SWSRC_FIRST_CSW, + SWSRC_SW1 = SWSRC_FIRST_CSW, + SWSRC_SW2, + SWSRC_SW3, + SWSRC_SW4, + SWSRC_SW5, + SWSRC_SW6, + SWSRC_SW7, + SWSRC_SW8, + SWSRC_SW9, + SWSRC_SWA, + SWSRC_SWB, + SWSRC_SWC, + SWSRC_LAST_CSW = SWSRC_SW1+NUM_CSW-1, + + SWSRC_ON, + + SWSRC_FIRST_MOMENT_SWITCH, + SWSRC_LAST_MOMENT_SWITCH = SWSRC_FIRST_MOMENT_SWITCH+SWSRC_ON-1, + +#if !defined(PCBSTD) + SWSRC_TRAINER_SHORT, + SWSRC_TRAINER_LONG, +#endif + +#if ROTARY_ENCODERS > 0 + SWSRC_FIRST_ROTENC_SWITCH, + SWSRC_LAST_ROTENC_SWITCH = SWSRC_FIRST_ROTENC_SWITCH+(2*ROTARY_ENCODERS)-1, +#endif + + SWSRC_COUNT, + SWSRC_FIRST = -SWSRC_LAST_MOMENT_SWITCH, + SWSRC_LAST = SWSRC_COUNT-1, + + SWSRC_OFF = -SWSRC_ON, + SWSRC_TRAINER = SWSRC_SW1-1, +}; + +enum MixSources { + MIXSRC_NONE, + +#if defined(PCBTARANIS) + MIXSRC_FIRST_INPUT, + MIXSRC_LAST_INPUT = MIXSRC_FIRST_INPUT+MAX_INPUTS-1, + + MIXSRC_FIRST_LUA, + MIXSRC_LAST_LUA = MIXSRC_FIRST_LUA+(MAX_SCRIPTS*MAX_SCRIPT_OUTPUTS)-1, +#endif + + MIXSRC_Rud, + MIXSRC_Ele, + MIXSRC_Thr, + MIXSRC_Ail, + + MIXSRC_FIRST_POT, +#if defined(PCBTARANIS) + MIXSRC_S1 = MIXSRC_FIRST_POT, + MIXSRC_S2, + MIXSRC_S3, + MIXSRC_S4, + MIXSRC_LAST_POT = MIXSRC_S4, +#else + MIXSRC_P1 = MIXSRC_FIRST_POT, + MIXSRC_P2, + #if defined(EXTRA_3POS) + MIXSRC_LAST_POT = MIXSRC_P2, + #else + MIXSRC_P3, + MIXSRC_LAST_POT = MIXSRC_P3, + #endif +#endif + +#if defined(PCBSKY9X) + MIXSRC_REa, + MIXSRC_LAST_ROTARY_ENCODER = MIXSRC_REa, +#elif defined(PCBGRUVIN9X) || defined(PCBMEGA2560) + MIXSRC_REa, + MIXSRC_REb, + #if ROTARY_ENCODERS > 2 + MIXSRC_REc, + MIXSRC_REd, + MIXSRC_LAST_ROTARY_ENCODER = MIXSRC_REd, + #else + MIXSRC_LAST_ROTARY_ENCODER = MIXSRC_REb, + #endif +#endif + + MIXSRC_MAX, + + MIXSRC_CYC1, + MIXSRC_CYC2, + MIXSRC_CYC3, + + MIXSRC_TrimRud, + MIXSRC_TrimEle, + MIXSRC_TrimThr, + MIXSRC_TrimAil, + + MIXSRC_FIRST_SWITCH, + +#if defined(PCBTARANIS) + MIXSRC_SA = MIXSRC_FIRST_SWITCH, + MIXSRC_SB, + MIXSRC_SC, + MIXSRC_SD, + MIXSRC_SE, + MIXSRC_SF, + MIXSRC_SG, + MIXSRC_SH, +#else + MIXSRC_3POS = MIXSRC_FIRST_SWITCH, + #if defined(EXTRA_3POS) + MIXSRC_3POS2, + #endif + MIXSRC_THR, + MIXSRC_RUD, + MIXSRC_ELE, + MIXSRC_AIL, + MIXSRC_GEA, + MIXSRC_TRN, +#endif + MIXSRC_FIRST_CSW, + MIXSRC_SW1 = MIXSRC_FIRST_CSW, + MIXSRC_SW9 = MIXSRC_SW1 + 8, + MIXSRC_SWA, + MIXSRC_SWB, + MIXSRC_SWC, + MIXSRC_LAST_CSW = MIXSRC_FIRST_CSW+NUM_CSW-1, + + MIXSRC_FIRST_PPM, + MIXSRC_LAST_PPM = MIXSRC_FIRST_PPM + 7, + + MIXSRC_FIRST_CH, + MIXSRC_CH1 = MIXSRC_FIRST_CH, + MIXSRC_CH2, + MIXSRC_CH3, + MIXSRC_CH4, + MIXSRC_CH5, + MIXSRC_CH6, + MIXSRC_CH7, + MIXSRC_CH8, + MIXSRC_CH9, + MIXSRC_CH10, + MIXSRC_CH11, + MIXSRC_CH12, + MIXSRC_CH13, + MIXSRC_CH14, + MIXSRC_CH15, + MIXSRC_CH16, + MIXSRC_LAST_CH = MIXSRC_CH1+NUM_CHNOUT-1, + + MIXSRC_GVAR1, + MIXSRC_LAST_GVAR = MIXSRC_GVAR1+MAX_GVARS-1, + + MIXSRC_FIRST_TELEM, + MIXSRC_LAST_TELEM = MIXSRC_FIRST_TELEM+NUM_TELEMETRY-1 +}; + +#define MIXSRC_FIRST (MIXSRC_NONE+1) +#define MIXSRC_LAST MIXSRC_LAST_CH +#define INPUTSRC_FIRST MIXSRC_Rud +#define INPUTSRC_LAST MIXSRC_LAST_TELEM + +#define MIN_POINTS 3 +#define MAX_POINTS 17 + +#define TMRMODE_NONE 0 +#define TMRMODE_ABS 1 +#define TMRMODE_THR 2 +#define TMRMODE_THR_REL 3 +#define TMRMODE_THR_TRG 4 + +#define COUNTDOWN_SILENT 0 +#define COUNTDOWN_BEEPS 1 +#define COUNTDOWN_VOICE 2 + +#if defined(CPUARM) || defined(CPUM2560) +PACK(typedef struct t_TimerData { + int8_t mode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw + uint16_t start; + uint8_t countdownBeep:2; + uint8_t minuteBeep:1; + uint8_t persistent:1; + uint8_t spare:4; + uint16_t value; +}) TimerData; +#else +PACK(typedef struct t_TimerData { + int8_t mode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw + uint16_t start:12; + uint16_t countdownBeep:1; + uint16_t minuteBeep:1; + uint16_t spare:2; +}) TimerData; +#endif + +enum Protocols { + PROTO_PPM, +#if !defined(CPUARM) + PROTO_PPM16, + PROTO_PPMSIM, +#endif +#if defined(PXX) || defined(DSM2) || defined(IRPROTOS) + PROTO_PXX, +#endif +#if defined(DSM2) || defined(IRPROTOS) + PROTO_DSM2_LP45, + PROTO_DSM2_DSM2, + PROTO_DSM2_DSMX, +#endif +#if defined(IRPROTOS) + // we will need 4 bytes for proto :( + PROTO_SILV, + PROTO_TRAC09, + PROTO_PICZ, + PROTO_SWIFT, +#endif + PROTO_MAX, + PROTO_NONE +}; + +enum RFProtocols { + RF_PROTO_OFF = -1, + RF_PROTO_X16, + RF_PROTO_D8, + RF_PROTO_LR12, + RF_PROTO_LAST = RF_PROTO_LR12 +}; + +enum DSM2Protocols { + DSM2_PROTO_LP45, + DSM2_PROTO_DSM2, + DSM2_PROTO_DSMX, +}; + +enum ModuleTypes { + MODULE_TYPE_NONE = 0, + MODULE_TYPE_PPM, + MODULE_TYPE_XJT, +#if defined(DSM2) + MODULE_TYPE_DSM2, +#endif + MODULE_TYPE_COUNT +}; + +enum FailsafeModes { + FAILSAFE_HOLD, + FAILSAFE_CUSTOM, + FAILSAFE_NOPULSES, + FAILSAFE_LAST = FAILSAFE_NOPULSES +}; + +#if defined(FRSKY) || !defined(PCBSTD) + #define TELEMETRY_DATA FrSkyData frsky; +#elif defined(MAVLINK) + #define TELEMETRY_DATA MavlinkData mavlink; +#else + #define TELEMETRY_DATA +#endif + +#if defined(CPUARM) || defined(PCBGRUVIN9X) || defined(PCBMEGA2560) + #define BeepANACenter uint16_t +#else + #define BeepANACenter uint8_t +#endif + +PACK(typedef struct t_ModelHeader { + char name[LEN_MODEL_NAME]; // must be first for eeLoadModelName + uint8_t modelId; + MODELDATA_BITMAP +}) ModelHeader; + +#if defined (CPUARM) + #define ARM_FIELD(x) x; + #define AVR_FIELD(x) +#else + #define ARM_FIELD(x) + #define AVR_FIELD(x) x; +#endif + +PACK(typedef struct t_ModelData { + ModelHeader header; + TimerData timers[MAX_TIMERS]; + uint8_t protocol:3; // not used on Taranis + uint8_t thrTrim:1; // Enable Throttle Trim + AVR_FIELD(int8_t ppmNCH:4) + ARM_FIELD(int8_t spare2:4) + uint8_t trimInc:3; // Trim Increments + uint8_t disableThrottleWarning:1; + ARM_FIELD(uint8_t displayText:1) + AVR_FIELD(uint8_t pulsePol:1) + uint8_t extendedLimits:1; + uint8_t extendedTrims:1; + uint8_t throttleReversed:1; + AVR_FIELD(int8_t ppmDelay) + BeepANACenter beepANACenter; // 1<<0->A1.. 1<<6->A7 + MixData mixData[MAX_MIXERS]; + LimitData limitData[NUM_CHNOUT]; + ExpoData expoData[MAX_EXPOS]; + + CURVDATA curves[MAX_CURVES]; + int8_t points[NUM_POINTS]; + + CustomSwData customSw[NUM_CSW]; + CustomFnData funcSw[NUM_CFN]; + SwashRingData swashR; + PhaseData phaseData[MAX_PHASES]; + + AVR_FIELD(int8_t ppmFrameLength) // 0=22.5ms (10ms-30ms) 0.5ms increments + uint8_t thrTraceSrc; + + swstate_t switchWarningStates; + + MODEL_GVARS_DATA + + TELEMETRY_DATA + + ROTARY_ENCODER_ARRAY_EXTRA + + MODELDATA_EXTRA + +}) ModelData; + +extern EEGeneral g_eeGeneral; +extern ModelData g_model; + +#define TOTAL_EEPROM_USAGE (sizeof(ModelData)*MAX_MODELS + sizeof(EEGeneral)) + +#endif +/*eof*/ diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index 37aad7040..8fabf48f8 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -337,6 +337,14 @@ void Open9xSim::refreshDiplay() bmp->render(); bmf->setImage(bmp); + + // Recalc RPM based on Blades setting during simulator - T.Foley + // Final value will change as user adjusts blades, spur or gear live in simulator + frskyData.hub.rpm = 303; + frskyData.hub.rpm *= (uint8_t)60; + frskyData.hub.rpm /= (g_model.frsky.blades+1); + float gear_ratio = (((float)g_model.frsky.spur_gear+1) / ((float)g_model.frsky.pinion_gear+1)); + frskyData.hub.rpm = (float)(frskyData.hub.rpm / gear_ratio); } } diff --git a/radio/src/simu.cpp~ b/radio/src/simu.cpp~ new file mode 100644 index 000000000..37aad7040 --- /dev/null +++ b/radio/src/simu.cpp~ @@ -0,0 +1,425 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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 "fx.h" +#include "FXExpression.h" +#include "FXPNGImage.h" +#include +#include "fxkeys.h" +#include "opentx.h" +#include +#include + +#define W2 LCD_W*2 +#define H2 LCD_H*2 + +int g_snapshot_idx = 0; + +class Open9xSim: public FXMainWindow +{ + FXDECLARE(Open9xSim) +public: + Open9xSim(){}; + Open9xSim(FXApp* a); + long onKeypress(FXObject*,FXSelector,void*); + long onTimeout(FXObject*,FXSelector,void*); + void makeSnapshot(const FXDrawable* drawable); + void doEvents(); + void refreshDiplay(); + +private: + FXImage *bmp; + FXImageFrame *bmf; + bool firstTime; + +public: + FXSlider *sliders[8]; + FXKnob *knobs[NUM_POTS]; +}; +// Message Map +FXDEFMAP(Open9xSim) Open9xSimMap[]={ + + //________Message_Type_________ID_____________________Message_Handler_______ + FXMAPFUNC(SEL_TIMEOUT, 2, Open9xSim::onTimeout), + FXMAPFUNC(SEL_KEYPRESS, 0, Open9xSim::onKeypress), + }; + +FXIMPLEMENT(Open9xSim,FXMainWindow,Open9xSimMap,ARRAYNUMBER(Open9xSimMap)) + +Open9xSim::Open9xSim(FXApp* a) +:FXMainWindow(a,"OpenTXSimu",NULL,NULL,DECOR_ALL,20,90,0,0) +{ + firstTime=true; + for(int i=0; i<(LCD_W*LCD_H/8); i++) displayBuf[i]=0;//rand(); + bmp = new FXPPMImage(getApp(),NULL,IMAGE_OWNED|IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP, W2, H2); + + FXHorizontalFrame *hf11=new FXHorizontalFrame(this,LAYOUT_CENTER_X); + FXHorizontalFrame *hf1=new FXHorizontalFrame(this,LAYOUT_FILL_X); + + //rh lv rv lh + for(int i=0; i<4; i++){ + switch(i) + { +#define L LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FIX_X|LAYOUT_FIX_Y +#undef X0 +#define X0 10 +#define Y0 10 + case 0: + sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_HORIZONTAL,X0+0,Y0+100,100,20); + break; + case 1: + sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_VERTICAL,X0+100,Y0+0,20,100); + break; + case 2: + sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_VERTICAL,X0+120,Y0+0,20,100); + break; + case 3: + sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_HORIZONTAL,X0+140,Y0+100,100,20); + break; + default:; + } + sliders[i]->setRange(-1024, 1024); + sliders[i]->setTickDelta(7); + sliders[i]->setValue(0); + } + + for(int i=0; isetRange(-1024, 1024); + knobs[i]->setValue(0); + } + + bmf = new FXImageFrame(this,bmp); + + getApp()->addTimeout(this,2,100); +} + +void Open9xSim::makeSnapshot(const FXDrawable* drawable) +{ + // Construct and create an FXImage object + FXPNGImage snapshot(getApp(), NULL, 0, drawable->getWidth(), drawable->getHeight()); + snapshot.create(); + + // Create a window device context and lock it onto the image + FXDCWindow dc(&snapshot); + + // Draw from the widget to this + dc.drawArea(drawable, 0, 0, drawable->getWidth(), drawable->getHeight(), 0, 0); + + // Release lock + dc.end(); + + // Grab pixels from server side back to client side + snapshot.restore(); + + // Save recovered pixels to a file + FXFileStream stream; + char buf[100]; + + do { + stream.close(); + sprintf(buf,"snapshot_%02d.png", ++g_snapshot_idx); + } while (stream.open(buf, FXStreamLoad)); + + if (stream.open(buf, FXStreamSave)) { + snapshot.savePixels(stream); + stream.close(); + printf("Snapshot written: %s\n", buf); + } + else { + printf("Cannot create snapshot %s\n", buf); + } +} +void Open9xSim::doEvents() +{ + getApp()->runOneEvent(false); +} + +long Open9xSim::onKeypress(FXObject*,FXSelector,void*v) +{ + FXEvent *evt=(FXEvent*)v; + // printf("keypress %x\n", evt->code); + if (evt->code=='s'){ + makeSnapshot(bmf); + } + return 0; +} + +long Open9xSim::onTimeout(FXObject*,FXSelector,void*) +{ + if(hasFocus()) { + static int keys1[]={ +#if defined(PCBTARANIS) + KEY_Page_Up, KEY_MENU, + KEY_Page_Down, KEY_PAGE, + KEY_Return, KEY_ENTER, + KEY_BackSpace, KEY_EXIT, + KEY_Up, KEY_PLUS, + KEY_Down, KEY_MINUS, +#else + KEY_Return, KEY_MENU, + KEY_BackSpace, KEY_EXIT, + KEY_Right, KEY_RIGHT, + KEY_Left, KEY_LEFT, + KEY_Up, KEY_UP, + KEY_Down, KEY_DOWN, +#endif +#if defined(ROTARY_ENCODER_NAVIGATION) + KEY_F, BTN_REa, +#endif + }; + +#if defined(PCBSKY9X) + Coproc_temp = 23; + Coproc_maxtemp = 28; +#endif + +#if defined(PCBSKY9X) + temperature = 31; + maxTemperature = 42; +#endif + + for (unsigned int i=0; igetKeyState(keys1[i])); + } + +#ifdef __APPLE__ + // gruvin: Can't use Function keys on the Mac -- too many other app conflicts. + // The ordering of these keys, Q/W,E/R,T/Y,U/I matches the on screen + // order of trim sliders + static FXuint trimKeys[] = { KEY_E, KEY_R, KEY_U, KEY_I, KEY_R, KEY_E, KEY_Y, KEY_T, KEY_Q, KEY_W }; +#else + static FXuint trimKeys[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8 }; +#endif + + for (unsigned i=0; igetKeyState(trimKeys[i])); + } + +#if defined(ROTARY_ENCODER_NAVIGATION) + static bool rotencAction = false; + if (getApp()->getKeyState(KEY_G)) { + if (!rotencAction) g_rotenc[0] += ROTARY_ENCODER_GRANULARITY; + rotencAction = true; + } + else if (getApp()->getKeyState(KEY_D)) { + if (!rotencAction) g_rotenc[0] -= ROTARY_ENCODER_GRANULARITY; + rotencAction = true; + } + else { + rotencAction = false; + } +#endif + +#define SWITCH_KEY(key, swtch, states) \ + static bool state##key = 0; \ + static int8_t state_##swtch = 2; \ + static int8_t inc_##swtch = 1; \ + if (getApp()->getKeyState(KEY_##key)) { \ + if (!state##key) { \ + state_##swtch = (state_##swtch+inc_##swtch); \ + if (state_##swtch == 1+states) inc_##swtch = -1; \ + else if (state_##swtch == 2) inc_##swtch = 1; \ + state##key = true; \ + } \ + } \ + else { \ + state##key = false; \ + } \ + simuSetSwitch(swtch, state_##swtch-states); + +#if defined(PCBTARANIS) + SWITCH_KEY(A, 0, 3); + SWITCH_KEY(B, 1, 3); + SWITCH_KEY(C, 2, 3); + SWITCH_KEY(D, 3, 3); + SWITCH_KEY(E, 4, 3); + SWITCH_KEY(F, 5, 2); + SWITCH_KEY(G, 6, 3); + SWITCH_KEY(H, 7, 2); +#else + SWITCH_KEY(1, 0, 2); + SWITCH_KEY(2, 1, 2); + SWITCH_KEY(3, 2, 2); + SWITCH_KEY(4, 3, 3); + SWITCH_KEY(5, 4, 2); + SWITCH_KEY(6, 5, 2); + SWITCH_KEY(7, 6, 2); +#endif + } + + per10ms(); + refreshDiplay(); + getApp()->addTimeout(this,2,5); + return 0; +} + +#if defined(PCBTARANIS) +#define BL_COLOR FXRGB(47,123,227) +#else +#define BL_COLOR FXRGB(150,200,152) +#endif + +void Open9xSim::refreshDiplay() +{ + if (lcd_refresh) { + lcd_refresh = false; + FXColor offColor = IS_BACKLIGHT_ON() ? BL_COLOR : FXRGB(200,200,200); +#if !defined(PCBTARANIS) + FXColor onColor = FXRGB(0,0,0); +#endif + for (int x=0;xsetPixel(2*x, 2*y, color); + bmp->setPixel(2*x+1, 2*y, color); + bmp->setPixel(2*x, 2*y+1, color); + bmp->setPixel(2*x+1, 2*y+1, color); + } +#else + if (lcd_buf[x+(y/8)*LCD_W] & (1<<(y%8))) { + bmp->setPixel(2*x, 2*y, onColor); + bmp->setPixel(2*x+1, 2*y, onColor); + bmp->setPixel(2*x, 2*y+1, onColor); + bmp->setPixel(2*x+1, 2*y+1, onColor); + } +#endif + else { + bmp->setPixel(2*x, 2*y, offColor); + bmp->setPixel(2*x+1, 2*y, offColor); + bmp->setPixel(2*x, 2*y+1, offColor); + bmp->setPixel(2*x+1, 2*y+1, offColor); + } + } + } + + bmp->render(); + bmf->setImage(bmp); + } +} + +Open9xSim *th9xSim; +void doFxEvents() +{ + //puts("doFxEvents"); + th9xSim->getApp()->runOneEvent(false); + th9xSim->refreshDiplay(); +} + +int main(int argc,char **argv) +{ + // Each FOX GUI program needs one, and only one, application object. + // The application objects coordinates some common stuff shared between + // all the widgets; for example, it dispatches events, keeps track of + // all the windows, and so on. + // We pass the "name" of the application, and its "vendor", the name + // and vendor are used to search the registry database (which stores + // persistent information e.g. fonts and colors). + FXApp application("Open9xSim", "thus"); + + // Here we initialize the application. We pass the command line arguments + // because FOX may sometimes need to filter out some of the arguments. + // This opens up the display as well, and reads the registry database + // so that persistent settings are now available. + application.init(argc,argv); + + // This creates the main window. We pass in the title to be displayed + // above the window, and possibly some icons for when its iconified. + // The decorations determine stuff like the borders, close buttons, + // drag handles, and so on the Window Manager is supposed to give this + // window. + //FXMainWindow *main=new FXMainWindow(&application,"Hello",NULL,NULL,DECOR_ALL); + th9xSim = new Open9xSim(&application); + application.create(); + + // Pretty self-explanatory:- this shows the window, and places it in the + // middle of the screen. +#ifndef __APPLE__ + th9xSim->show(PLACEMENT_SCREEN); +#else + th9xSim->show(); // Otherwise the main window gets centred across my two monitors, split down the middle. +#endif + +#if defined(FRSKY) && !defined(FRSKY_SPORT) + frskyStreaming = 1; +#endif + + printf("Model size = %d\n", (int)sizeof(g_model)); + + StartEepromThread(argc >= 2 ? argv[1] : "eeprom.bin"); + StartMainThread(); + +#if defined(PCBTARANIS) + simuSetSwitch(0, 0); + simuSetSwitch(1, 0); +#endif + + return application.run(); +} + +uint16_t anaIn(uint8_t chan) +{ + if (chansliders[chan]->getValue(); + else if (chanknobs[chan-NUM_STICKS]->getValue(); +#if defined(PCBTARANIS) + else if (chan == 8) + return 1000; +#elif defined(PCBSKY9X) + else if (chan == 7) + return 1500; + else if (chan == 8) + return 100; +#elif defined(PCBGRUVIN9X) + else if (chan == 7) + return 150; +#else + else if (chan == 7) + return 1500; +#endif + else + return 0; +} diff --git a/radio/src/telemetry/frsky.cpp b/radio/src/telemetry/frsky.cpp index f2e7f7905..fc6e6ed4a 100644 --- a/radio/src/telemetry/frsky.cpp +++ b/radio/src/telemetry/frsky.cpp @@ -175,7 +175,15 @@ void parseTelemHubByte(uint8_t byte) switch ((uint8_t)structPos) { case offsetof(FrskySerialData, rpm): - frskyData.hub.rpm *= (uint8_t)60/(g_model.frsky.blades+2); + //frskyData.hub.rpm *= (uint8_t)60/(g_model.frsky.blades+2); + // Change minimum Blades to 1 to allow for single sensors/raw data - T.Foley + // Added Spur and Pinion Ratio Calculation to provide final head/drive RPM after gearing + // + frskyData.hub.rpm *= (uint8_t)60; + frskyData.hub.rpm /= (g_model.frsky.blades+1); + float gear_ratio = (((float)g_model.frsky.spur_gear+1) / ((float)g_model.frsky.pinion_gear+1)); + frskyData.hub.rpm = (float)(frskyData.hub.rpm / gear_ratio); + if (frskyData.hub.rpm > frskyData.hub.maxRpm) frskyData.hub.maxRpm = frskyData.hub.rpm; break; diff --git a/radio/src/telemetry/frsky.cpp~ b/radio/src/telemetry/frsky.cpp~ new file mode 100644 index 000000000..35d3bf5cc --- /dev/null +++ b/radio/src/telemetry/frsky.cpp~ @@ -0,0 +1,926 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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" + +// Enumerate FrSky packet codes +#define LINKPKT 0xfe +#define USRPKT 0xfd +#define A11PKT 0xfc +#define A12PKT 0xfb +#define A21PKT 0xfa +#define A22PKT 0xf9 +#define ALRM_REQUEST 0xf8 +#define RSSI1PKT 0xf7 +#define RSSI2PKT 0xf6 +#define RSSI_REQUEST 0xf1 + +#define START_STOP 0x7e +#define BYTESTUFF 0x7d +#define STUFF_MASK 0x20 + +#if defined(TELEMETREZ) +#define PRIVATE 0x1B +#endif + +uint8_t frskyRxBuffer[FRSKY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1) +uint8_t frskyTxBuffer[FRSKY_TX_PACKET_SIZE]; // Ditto for transmit buffer +#if !defined(CPUARM) +uint8_t frskyTxBufferCount = 0; +#endif +uint8_t frskyStreaming = 0; +#if defined(WS_HOW_HIGH) +uint8_t frskyUsrStreaming = 0; +#endif +uint8_t link_counter = 0; +FrskyData frskyData; + +#if defined(FRSKY_HUB) || defined(WS_HOW_HIGH) +void checkMinMaxAltitude() +{ + if (TELEMETRY_ALT_BP > frskyData.hub.maxAltitude) + frskyData.hub.maxAltitude = TELEMETRY_ALT_BP; + if (TELEMETRY_ALT_BP < frskyData.hub.minAltitude) + frskyData.hub.minAltitude = TELEMETRY_ALT_BP; +} +#endif + +#if defined(VARIO) && !defined(FRSKY_SPORT) && (defined(FRSKY_HUB) || defined(WS_HOW_HIGH)) +void evalVario(int16_t altitude_bp, uint16_t altitude_ap) +{ + int32_t varioAltitude_cm = (int32_t)altitude_bp * 100 + (altitude_bp > 0 ? altitude_ap : -altitude_ap); + uint8_t varioAltitudeQueuePointer = frskyData.hub.varioAltitudeQueuePointer + 1; + if (varioAltitudeQueuePointer >= VARIO_QUEUE_LENGTH) + varioAltitudeQueuePointer = 0; + frskyData.hub.varioAltitudeQueuePointer = varioAltitudeQueuePointer; + frskyData.hub.varioSpeed -= frskyData.hub.varioAltitudeQueue[varioAltitudeQueuePointer] ; + frskyData.hub.varioAltitudeQueue[varioAltitudeQueuePointer] = varioAltitude_cm - frskyData.hub.varioAltitude_cm; + frskyData.hub.varioAltitude_cm = varioAltitude_cm; + frskyData.hub.varioSpeed += frskyData.hub.varioAltitudeQueue[varioAltitudeQueuePointer] ; +} +#else +#define evalVario(...) +#endif + +#if defined(FRSKY_HUB) +typedef enum { + TS_IDLE = 0, // waiting for 0x5e frame marker + TS_DATA_ID, // waiting for dataID + TS_DATA_LOW, // waiting for data low byte + TS_DATA_HIGH, // waiting for data high byte + TS_XOR = 0x80 // decode stuffed byte +} TS_STATE; + +void parseTelemHubByte(uint8_t byte) +{ + static int8_t structPos; + static uint8_t lowByte; + static TS_STATE state = TS_IDLE; + + if (byte == 0x5e) { + state = TS_DATA_ID; + return; + } + if (state == TS_IDLE) { + return; + } + if (state & TS_XOR) { + byte = byte ^ 0x60; + state = (TS_STATE)(state - TS_XOR); + } + if (byte == 0x5d) { + state = (TS_STATE)(state | TS_XOR); + return; + } + if (state == TS_DATA_ID) { + if (byte > 0x3f) { + state = TS_IDLE; + } + else { + structPos = byte*2; + state = TS_DATA_LOW; + } + return; + } + if (state == TS_DATA_LOW) { + lowByte = byte; + state = TS_DATA_HIGH; + return; + } + + state = TS_IDLE; + +#if defined(GPS) + if ((uint8_t)structPos == offsetof(FrskySerialData, gpsLatitude_bp)) { + if (lowByte || byte) + frskyData.hub.gpsFix = 1; + else if (frskyData.hub.gpsFix > 0 && frskyData.hub.gpsLatitude_bp > 1) + frskyData.hub.gpsFix = 0; + } + else if ((uint8_t)structPos == offsetof(FrskySerialData, gpsLongitude_bp)) { + if (lowByte || byte) + frskyData.hub.gpsFix = 1; + else if (frskyData.hub.gpsFix > 0 && frskyData.hub.gpsLongitude_bp > 1) + frskyData.hub.gpsFix = 0; + } + + if ((uint8_t)structPos == offsetof(FrskySerialData, gpsAltitude_bp) || + ((uint8_t)structPos >= offsetof(FrskySerialData, gpsAltitude_ap) && (uint8_t)structPos <= offsetof(FrskySerialData, gpsLatitudeNS) && (uint8_t)structPos != offsetof(FrskySerialData, baroAltitude_bp) && (uint8_t)structPos != offsetof(FrskySerialData, baroAltitude_ap))) { + // If we don't have a fix, we may discard the value + if (frskyData.hub.gpsFix <= 0) + return; + } +#endif + +#if 0 + uint16_t previousValue = *((uint16_t *)(((uint8_t*)&frskyData.hub) + structPos)); +#endif + + ((uint8_t*)&frskyData.hub)[structPos] = lowByte; + ((uint8_t*)&frskyData.hub)[structPos+1] = byte; + + switch ((uint8_t)structPos) { + + case offsetof(FrskySerialData, rpm): + //frskyData.hub.rpm *= (uint8_t)60/(g_model.frsky.blades+2); + // Change minimum Blades to 1 to allow for single sensors/raw data - T.Foley + // Added Spur and Pinion Ratio Calculation to provide final head/drive RPM after gearing + // + frskyData.hub.rpm *= (uint8_t)60; + frskyData.hub.rpm /= (g_model.frsky.blades+1); + float gear_ratio = (((float)g_model.frsky.spur_gear+1) / ((float)g_model.frsky.pinion_gear+1)); + frskyData.hub.rpm = (float)(frskyData.hub.rpm / gear_ratio); + if (frskyData.hub.rpm > frskyData.hub.maxRpm) + frskyData.hub.maxRpm = frskyData.hub.rpm; + break; + + case offsetof(FrskySerialData, temperature1): + if (frskyData.hub.temperature1 > frskyData.hub.maxTemperature1) + frskyData.hub.maxTemperature1 = frskyData.hub.temperature1; + break; + + case offsetof(FrskySerialData, temperature2): + if (frskyData.hub.temperature2 > frskyData.hub.maxTemperature2) + frskyData.hub.maxTemperature2 = frskyData.hub.temperature2; + break; + + case offsetof(FrskySerialData, current): + if (frskyData.hub.current > frskyData.hub.maxCurrent) + frskyData.hub.maxCurrent = frskyData.hub.current; + break; + + case offsetof(FrskySerialData, currentConsumption): + // we receive data from openXsensor. stops the calculation of consumption and power + frskyData.hub.openXsensor = 1; + break; + + case offsetof(FrskySerialData, volts_ap): +#if defined(FAS_BSS) + frskyData.hub.vfas = (frskyData.hub.volts_bp * 10 + frskyData.hub.volts_ap); +#else + frskyData.hub.vfas = ((frskyData.hub.volts_bp * 100 + frskyData.hub.volts_ap * 10) * 21) / 110; +#endif + /* TODO later if (!frskyData.hub.minVfas || frskyData.hub.minVfas > frskyData.hub.vfas) + frskyData.hub.minVfas = frskyData.hub.vfas; */ + break; + + case offsetof(FrskySerialData, baroAltitude_bp): + // First received barometer altitude => Altitude offset + if (!frskyData.hub.baroAltitudeOffset) + frskyData.hub.baroAltitudeOffset = -frskyData.hub.baroAltitude_bp; + if (g_model.frsky.varioSource == VARIO_SOURCE_ALTI) { + evalVario(frskyData.hub.baroAltitude_bp, 0); + } + frskyData.hub.baroAltitude_bp += frskyData.hub.baroAltitudeOffset; + checkMinMaxAltitude(); + break; + + case offsetof(FrskySerialData, baroAltitude_ap): + if (g_model.frsky.varioSource == VARIO_SOURCE_ALTI_PLUS) { + evalVario(frskyData.hub.baroAltitude_bp-frskyData.hub.baroAltitudeOffset, frskyData.hub.baroAltitude_ap); + } + break; + +#if defined(GPS) + case offsetof(FrskySerialData, gpsAltitude_ap): + if (!frskyData.hub.gpsAltitudeOffset) + frskyData.hub.gpsAltitudeOffset = -frskyData.hub.gpsAltitude_bp; + frskyData.hub.gpsAltitude_bp += frskyData.hub.gpsAltitudeOffset; + if (!frskyData.hub.baroAltitudeOffset) { + if (frskyData.hub.gpsAltitude_bp > frskyData.hub.maxAltitude) + frskyData.hub.maxAltitude = frskyData.hub.gpsAltitude_bp; + if (frskyData.hub.gpsAltitude_bp < frskyData.hub.minAltitude) + frskyData.hub.minAltitude = frskyData.hub.gpsAltitude_bp; + } + if (!frskyData.hub.pilotLatitude && !frskyData.hub.pilotLongitude) { + // First received GPS position => Pilot GPS position + getGpsPilotPosition(); + } + else if (frskyData.hub.gpsDistNeeded || g_menuStack[g_menuStackPtr] == menuTelemetryFrsky) { + getGpsDistance(); + } + break; + + case offsetof(FrskySerialData, gpsSpeed_bp): + // Speed => Max speed + if (frskyData.hub.gpsSpeed_bp > frskyData.hub.maxGpsSpeed) + frskyData.hub.maxGpsSpeed = frskyData.hub.gpsSpeed_bp; + break; +#endif + + case offsetof(FrskySerialData, volts): + // Voltage => Cell number + Cell voltage + { + uint8_t battnumber = ((frskyData.hub.volts & 0x00F0) >> 4); + if (battnumber < 12) { + if (frskyData.hub.cellsCount < battnumber+1) { + frskyData.hub.cellsCount = battnumber+1; + } + uint8_t cellVolts = (uint8_t)(((((frskyData.hub.volts & 0xFF00) >> 8) + ((frskyData.hub.volts & 0x000F) << 8)))/10); + frskyData.hub.cellVolts[battnumber] = cellVolts; + if (!frskyData.hub.minCellVolts || cellVolts < frskyData.hub.minCellVolts || battnumber==frskyData.hub.minCellIdx) { + frskyData.hub.minCellIdx = battnumber; + frskyData.hub.minCellVolts = cellVolts; + } + } + break; + } + +#if defined(GPS) + case offsetof(FrskySerialData, hour): + frskyData.hub.hour = ((uint8_t)(frskyData.hub.hour + g_eeGeneral.timezone + 24)) % 24; + break; +#endif + + case offsetof(FrskySerialData, accelX): + case offsetof(FrskySerialData, accelY): + case offsetof(FrskySerialData, accelZ): + *(int16_t*)(&((uint8_t*)&frskyData.hub)[structPos]) /= 10; + break; + +#if 0 + case offsetof(FrskySerialData, gpsAltitude_bp): + case offsetof(FrskySerialData, fuelLevel): + case offsetof(FrskySerialData, gpsLongitude_bp): + case offsetof(FrskySerialData, gpsLatitude_bp): + case offsetof(FrskySerialData, gpsCourse_bp): + case offsetof(FrskySerialData, day): + case offsetof(FrskySerialData, year): + case offsetof(FrskySerialData, sec): + case offsetof(FrskySerialData, gpsSpeed_ap): + case offsetof(FrskySerialData, gpsLongitude_ap): + case offsetof(FrskySerialData, gpsLatitude_ap): + case offsetof(FrskySerialData, gpsCourse_ap): + case offsetof(FrskySerialData, gpsLongitudeEW): + case offsetof(FrskySerialData, gpsLatitudeNS): + case offsetof(FrskySerialData, varioSpeed): + case offsetof(FrskySerialData, power): /* because sent by openXsensor */ + case offsetof(FrskySerialData, vfas): + case offsetof(FrskySerialData, volts_bp): + break; + + default: + *((uint16_t *)(((uint8_t*)&frskyData.hub) + structPos)) = previousValue; + break; +#endif + } +} +#endif + +#if defined(WS_HOW_HIGH) +void parseTelemWSHowHighByte(uint8_t byte) +{ + if (frskyUsrStreaming < (FRSKY_TIMEOUT10ms*3 - 10)) { + ((uint8_t*)&frskyData.hub)[offsetof(FrskySerialData, baroAltitude_bp)] = byte; + checkMinMaxAltitude(); + if (g_model.frsky.varioSource == VARIO_SOURCE_ALTI) { + evalVario(frskyData.hub.baroAltitude_bp, 0); + } + } + else { + // At least 100mS passed since last data received + ((uint8_t*)&frskyData.hub)[offsetof(FrskySerialData, baroAltitude_bp)+1] = byte; + } + // baroAltitude_bp unit here is feet! + frskyUsrStreaming = FRSKY_TIMEOUT10ms*3; // reset counter +} +#endif + +void processFrskyPacket(uint8_t *packet) +{ + // What type of packet? + switch (packet[0]) + { + case LINKPKT: // A1/A2/RSSI values + { + link_counter += 32; + frskyData.analog[0].set(packet[1], g_model.frsky.channels[0].type); + frskyData.analog[1].set(packet[2], g_model.frsky.channels[1].type); + frskyData.rssi[0].set(packet[3]); + frskyData.rssi[1].set(packet[4] / 2); + frskyStreaming = FRSKY_TIMEOUT10ms; // reset counter only if valid frsky packets are being detected +#if defined(VARIO) + uint8_t varioSource = g_model.frsky.varioSource - VARIO_SOURCE_A1; + if (varioSource < 2) + frskyData.hub.varioSpeed = applyChannelRatio(varioSource, frskyData.analog[varioSource].value); +#endif + break; + } +#if defined(FRSKY_HUB) || defined (WS_HOW_HIGH) + case USRPKT: // User Data packet + uint8_t numBytes = 3 + (packet[1] & 0x07); // sanitize in case of data corruption leading to buffer overflow + for (uint8_t i=3; i>1) * (voltage>>1)) / 25; +#endif + + frskyData.hub.currentPrescale += current; + if (frskyData.hub.currentPrescale >= 3600) { + frskyData.hub.currentConsumption += 1; + frskyData.hub.currentPrescale -= 3600; + } + } + + if (frskyData.hub.power > frskyData.hub.maxPower) + frskyData.hub.maxPower = frskyData.hub.power; + } +} + +void telemetryWakeup() +{ +#if defined(PCBSKY9X) + rxPdcUsart(processSerialData); // Receive serial data here +#endif + + // Attempt to transmit any waiting Fr-Sky alarm set packets every 50ms (subject to packet buffer availability) + static uint8_t frskyTxDelay = 5; + if (frskyAlarmsSendState && (--frskyTxDelay == 0)) { + frskyTxDelay = 5; // 50ms + frskySendNextAlarm(); + } + +#ifndef SIMU +#if defined(WS_HOW_HIGH) + if (frskyUsrStreaming > 0) { + frskyUsrStreaming--; + } +#endif + + if (frskyStreaming > 0) { + frskyStreaming--; + } + else { + frskyData.rssi[0].set(0); + frskyData.rssi[1].set(0); + } +#endif + +#if defined(VARIO) + if (TELEMETRY_STREAMING() && !IS_FAI_ENABLED()) + varioWakeup(); +#endif +} + +#if 0 +// not used any more +bool FRSKY_alarmRaised(uint8_t idx) +{ + for (int i=0; i<2; i++) { + if (ALARM_LEVEL(idx, i) != alarm_off) { + if (ALARM_GREATER(idx, i)) { + if (frskyData.analog[idx].value > g_model.frsky.channels[idx].alarms_value[i]) + return true; + } + else { + if (frskyData.analog[idx].value < g_model.frsky.channels[idx].alarms_value[i]) + return true; + } + } + } + return false; +} +#endif + +#if !defined(CPUARM) +inline void FRSKY_EnableTXD(void) +{ + frskyTxBufferCount = 0; + UCSR0B |= (1 << TXEN0); // enable TX +} + +inline void FRSKY_EnableRXD(void) +{ + UCSR0B |= (1 << RXEN0); // enable RX + UCSR0B |= (1 << RXCIE0); // enable Interrupt +} +#endif + +void FRSKY_Init(void) +{ + // clear frsky variables + resetTelemetry(); + +#if defined(PCBTARANIS) + // TODO +#elif defined(PCBSKY9X) + startPdcUsartReceive() ; +#elif !defined(SIMU) + + DDRE &= ~(1 << DDE0); // set RXD0 pin as input + PORTE &= ~(1 << PORTE0); // disable pullup on RXD0 pin + +#undef BAUD +#define BAUD 9600 +#include + + UBRR0H = UBRRH_VALUE; + UBRR0L = UBRRL_VALUE; + UCSR0A &= ~(1 << U2X0); // disable double speed operation. + + // set 8N1 + UCSR0B = 0 | (0 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0) | (0 << RXEN0) | (0 << TXEN0) | (0 << UCSZ02); + UCSR0C = 0 | (1 << UCSZ01) | (1 << UCSZ00); + + + while (UCSR0A & (1 << RXC0)) UDR0; // flush receive buffer + + // These should be running right from power up on a FrSky enabled '9X. + FRSKY_EnableTXD(); // enable FrSky-Telemetry reception + FRSKY_EnableRXD(); // enable FrSky-Telemetry reception +#endif +} + +void FrskyValueWithMin::set(uint8_t value) +{ + if (this->value == 0) { + this->value = value; + } + else { + sum += value; + if (link_counter == 0) { + this->value = sum / 8; + sum = 0; + } + } + + if (value && (!min || value < min)) + min = value; +} + +void FrskyValueWithMinMax::set(uint8_t value, uint8_t unit) +{ + FrskyValueWithMin::set(value); + if (unit != UNIT_VOLTS) { + this->value = value; + } + if (!max || value > max) + max = value; +} + +void resetTelemetry() +{ + memclear(&frskyData, sizeof(frskyData)); + +#if defined(FRSKY_HUB) + frskyData.hub.gpsLatitude_bp = 2; + frskyData.hub.gpsLongitude_bp = 2; + frskyData.hub.gpsFix = -1; +#endif + +#ifdef SIMU + frskyData.analog[0].set(120, UNIT_VOLTS); + frskyData.analog[1].set(240, UNIT_VOLTS); + frskyData.rssi[0].value = 75; + frskyData.rssi[1].value = 75; + frskyData.hub.fuelLevel = 75; + frskyData.hub.rpm = 12000; + +#if defined(GPS) + frskyData.hub.gpsFix = 1; + frskyData.hub.gpsLatitude_bp = 4401; + frskyData.hub.gpsLatitude_ap = 7710; + frskyData.hub.gpsLongitude_bp = 1006; + frskyData.hub.gpsLongitude_ap = 8872; + frskyData.hub.gpsSpeed_bp = (100 * 250) / 463; + getGpsPilotPosition(); + + frskyData.hub.gpsLatitude_bp = 4401; + frskyData.hub.gpsLatitude_ap = 7455; + frskyData.hub.gpsLongitude_bp = 1006; + frskyData.hub.gpsLongitude_ap = 9533; + getGpsDistance(); +#endif + + frskyData.hub.cellsCount = 6; + + frskyData.hub.gpsAltitude_bp = 50; + frskyData.hub.baroAltitude_bp = 50; + frskyData.hub.minAltitude = 10; + frskyData.hub.maxAltitude = 500; + + frskyData.hub.accelY = 100; + frskyData.hub.temperature1 = -30; + frskyData.hub.maxTemperature1 = 100; + + frskyData.hub.current = 5; + frskyData.hub.maxCurrent = 56; +#endif +} diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 98d62ab7b..3e1ef09c7 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -185,6 +185,8 @@ const pm_char STR_BAR[] PROGMEM = TR_BAR; const pm_char STR_ALARM[] PROGMEM = TR_ALARM; const pm_char STR_USRDATA[] PROGMEM = TR_USRDATA; const pm_char STR_BLADES[] PROGMEM = TR_BLADES; +const pm_char STR_SPURGEAR[] PROGMEM = TR_SPURGEAR; // T.Foley +const pm_char STR_PINIONGEAR[] PROGMEM = TR_PINIONGEAR; // T.Foley const pm_char STR_SCREEN[] PROGMEM = TR_SCREEN; const pm_char STR_SOUND_LABEL[] PROGMEM = TR_SOUND_LABEL; const pm_char STR_LENGTH[] PROGMEM = TR_LENGTH; diff --git a/radio/src/translations.cpp~ b/radio/src/translations.cpp~ new file mode 100644 index 000000000..98d62ab7b --- /dev/null +++ b/radio/src/translations.cpp~ @@ -0,0 +1,672 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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" + +#define ISTR(x) LEN_##x TR_##x + +// The non-0-terminated-strings +const pm_char STR_OPEN9X[] PROGMEM = + ISTR(OFFON) + ISTR(MMMINV) + ISTR(NCHANNELS) +#if !defined(GRAPHICS) + ISTR(VBEEPLEN) +#endif + ISTR(VBEEPMODE) +#if defined(ROTARY_ENCODERS) + ISTR(VRENAVIG) +#endif +#if defined(ROTARY_ENCODER_NAVIGATION) + ISTR(VRENCODERS) +#endif + ISTR(TRNMODE) + ISTR(TRNCHN) + ISTR(VTRIMINC) + ISTR(RETA123) + ISTR(VPROTOS) + ISTR(POSNEG) + ISTR(VBLMODE) + ISTR(VCURVEFUNC) + ISTR(VMLTPX) + ISTR(VMLTPX2) + ISTR(VMIXTRIMS) + ISTR(VCSWFUNC) + ISTR(VFSWFUNC) + ISTR(VFSWRESET) + ISTR(FUNCSOUNDS) + ISTR(VTELEMCHNS) +#if defined(FRSKY) || defined(CPUARM) + #if defined(CPUARM) + ISTR(VTELEMUNIT_IMP) + ISTR(VTELEMUNIT_NORM) + #else + ISTR(VTELEMUNIT) + #endif + ISTR(VALARM) + ISTR(VALARMFN) + ISTR(VTELPROTO) + ISTR(GPSFORMAT) + ISTR(VOLTSRC) + ISTR(VARIOSRC) + ISTR(VSCREEN) +#endif +#if defined(TEMPLATES) + ISTR(VTEMPLATES) +#endif +#if defined(HELI) + ISTR(VSWASHTYPE) +#endif + ISTR(VKEYS) + ISTR(VSWITCHES) + ISTR(VSRCRAW) + ISTR(VTMRMODES) +#if defined(CPUM2560) || defined(CPUARM) + ISTR(DATETIME) +#endif +#if defined(CPUARM) + ISTR(VLCD) + ISTR(VUNITSSYSTEM) + ISTR(VBEEPCOUNTDOWN) +#endif +#if defined(PXX) + ISTR(COUNTRYCODES) + ISTR(VFAILSAFE) +#endif +#if defined(PCBTARANIS) + ISTR(VTRAINERMODES) +#endif +#if defined(MAVLINK) + ISTR(MAVLINK_BAUDS) + ISTR(MAVLINK_AC_MODES) + ISTR(MAVLINK_AP_MODES) +#endif + ; + +// The 0-terminated-strings +const pm_char STR_POPUPS[] PROGMEM = TR_POPUPS; +const pm_char STR_MENUWHENDONE[] PROGMEM = TR_MENUWHENDONE; +const pm_char STR_FREE[] PROGMEM = TR_FREE; +const pm_char STR_DELETEMODEL[] PROGMEM = TR_DELETEMODEL; +const pm_char STR_COPYINGMODEL[] PROGMEM = TR_COPYINGMODEL; +const pm_char STR_MOVINGMODEL[] PROGMEM = TR_MOVINGMODEL; +const pm_char STR_LOADINGMODEL[] PROGMEM = TR_LOADINGMODEL; +const pm_char STR_NAME[] PROGMEM = TR_NAME; +const pm_char STR_BITMAP[] PROGMEM = TR_BITMAP; +const pm_char STR_TIMER[] PROGMEM = TR_TIMER; +const pm_char STR_ELIMITS[] PROGMEM = TR_ELIMITS; +const pm_char STR_ETRIMS[] PROGMEM = TR_ETRIMS; +const pm_char STR_TRIMINC[] PROGMEM = TR_TRIMINC; +const pm_char STR_TTRACE[] PROGMEM = TR_TTRACE; +const pm_char STR_TTRIM[] PROGMEM = TR_TTRIM; +const pm_char STR_BEEPCTR[] PROGMEM = TR_BEEPCTR; +const pm_char STR_PROTO[] PROGMEM = TR_PROTO; +const pm_char STR_PPMFRAME[] PROGMEM = TR_PPMFRAME; +const pm_char STR_MS[] PROGMEM = TR_MS; +const pm_char STR_SWITCH[] PROGMEM = TR_SWITCH; +const pm_char STR_TRIMS[] PROGMEM = TR_TRIMS; +const pm_char STR_FADEIN[] PROGMEM = TR_FADEIN; +const pm_char STR_FADEOUT[] PROGMEM = TR_FADEOUT; +const pm_char STR_DEFAULT[] PROGMEM = TR_DEFAULT; +const pm_char STR_CHECKTRIMS[] PROGMEM = TR_CHECKTRIMS; +#ifdef HELI +const pm_char STR_SWASHTYPE[] PROGMEM = TR_SWASHTYPE; +const pm_char STR_COLLECTIVE[] PROGMEM = TR_COLLECTIVE; +const pm_char STR_SWASHRING[] PROGMEM = TR_SWASHRING; +const pm_char STR_ELEDIRECTION[] PROGMEM = TR_ELEDIRECTION; +const pm_char STR_AILDIRECTION[] PROGMEM = TR_AILDIRECTION; +const pm_char STR_COLDIRECTION[] PROGMEM = TR_COLDIRECTION; +#endif +const pm_char STR_MODE[] PROGMEM = TR_MODE; +#if defined(AUDIO) && defined(BUZZER) +const pm_char STR_SPEAKER[] PROGMEM = TR_SPEAKER; +const pm_char STR_BUZZER[] PROGMEM = TR_BUZZER; +#endif +const pm_char STR_NOFREEEXPO[] PROGMEM = TR_NOFREEEXPO; +const pm_char STR_NOFREEMIXER[] PROGMEM = TR_NOFREEMIXER; +const pm_char STR_INSERTMIX[] PROGMEM = TR_INSERTMIX; +const pm_char STR_EDITMIX[] PROGMEM = TR_EDITMIX; +const pm_char STR_SOURCE[] PROGMEM = TR_SOURCE; +const pm_char STR_WEIGHT[] PROGMEM = TR_WEIGHT; +const pm_char STR_EXPO[] PROGMEM = TR_EXPO; +const pm_char STR_SIDE[] PROGMEM = TR_SIDE; +const pm_char STR_DIFFERENTIAL[] PROGMEM = TR_DIFFERENTIAL; +const pm_char STR_OFFSET[] PROGMEM = TR_OFFSET; +const pm_char STR_TRIM[] PROGMEM = TR_TRIM; +const pm_char STR_DREX[] PROGMEM = TR_DREX; +const pm_char STR_CURVE[] PROGMEM = TR_CURVE; +const pm_char STR_FLMODE[] PROGMEM = TR_FLMODE; +const pm_char STR_MIXWARNING[] PROGMEM = TR_MIXWARNING; +const pm_char STR_OFF[] PROGMEM = TR_OFF; +const pm_char STR_MULTPX[] PROGMEM = TR_MULTPX; +const pm_char STR_DELAYDOWN[] PROGMEM = TR_DELAYDOWN; +const pm_char STR_DELAYUP[] PROGMEM = TR_DELAYUP; +const pm_char STR_SLOWDOWN[] PROGMEM = TR_SLOWDOWN; +const pm_char STR_SLOWUP[] PROGMEM = TR_SLOWUP; +const pm_char STR_MIXER[] PROGMEM = TR_MIXER; +const pm_char STR_CV[] PROGMEM = TR_CV; +const pm_char STR_GV[] PROGMEM = TR_GV; +const pm_char STR_ACHANNEL[] PROGMEM = TR_ACHANNEL; +const pm_char STR_RANGE[] PROGMEM = TR_RANGE; +const pm_char STR_BAR[] PROGMEM = TR_BAR; +const pm_char STR_ALARM[] PROGMEM = TR_ALARM; +const pm_char STR_USRDATA[] PROGMEM = TR_USRDATA; +const pm_char STR_BLADES[] PROGMEM = TR_BLADES; +const pm_char STR_SCREEN[] PROGMEM = TR_SCREEN; +const pm_char STR_SOUND_LABEL[] PROGMEM = TR_SOUND_LABEL; +const pm_char STR_LENGTH[] PROGMEM = TR_LENGTH; +#if defined(AUDIO) +const pm_char STR_SPKRPITCH[] PROGMEM = TR_SPKRPITCH; +#endif +#if defined(HAPTIC) +const pm_char STR_HAPTIC_LABEL[] PROGMEM = TR_HAPTIC_LABEL; +const pm_char STR_HAPTICSTRENGTH[] PROGMEM = TR_HAPTICSTRENGTH; +#endif +const pm_char STR_CONTRAST[] PROGMEM = TR_CONTRAST; +const pm_char STR_ALARMS_LABEL[] PROGMEM = TR_ALARMS_LABEL; +#if defined(BATTGRAPH) || defined(PCBTARANIS) +const pm_char STR_BATTERY_RANGE[] PROGMEM = TR_BATTERY_RANGE; +#endif +const pm_char STR_BATTERYWARNING[] PROGMEM = TR_BATTERYWARNING; +const pm_char STR_INACTIVITYALARM[] PROGMEM = TR_INACTIVITYALARM; +const pm_char STR_MEMORYWARNING[] PROGMEM = TR_MEMORYWARNING; +const pm_char STR_ALARMWARNING[] PROGMEM = TR_ALARMWARNING; +#if defined(ROTARY_ENCODERS) +const pm_char STR_RENAVIG[] PROGMEM = TR_RENAVIG; +#endif +const pm_char STR_THROTTLEREVERSE[] PROGMEM = TR_THROTTLEREVERSE; +const pm_char STR_MINUTEBEEP[] PROGMEM = TR_MINUTEBEEP; +const pm_char STR_BEEPCOUNTDOWN[] PROGMEM = TR_BEEPCOUNTDOWN; +const pm_char STR_PERSISTENT[] PROGMEM = TR_PERSISTENT; +const pm_char STR_BACKLIGHT_LABEL[] PROGMEM = TR_BACKLIGHT_LABEL; +const pm_char STR_BLDELAY[] PROGMEM = TR_BLDELAY; + +#if defined(PWM_BACKLIGHT) +const pm_char STR_BLONBRIGHTNESS[] PROGMEM = TR_BLONBRIGHTNESS; +const pm_char STR_BLOFFBRIGHTNESS[] PROGMEM = TR_BLOFFBRIGHTNESS; +#endif + +const pm_char STR_SPLASHSCREEN[] PROGMEM = TR_SPLASHSCREEN; +const pm_char STR_THROTTLEWARNING[] PROGMEM = TR_THROTTLEWARNING; +const pm_char STR_SWITCHWARNING[] PROGMEM = TR_SWITCHWARNING; +#ifdef FRSKY +const pm_char STR_TIMEZONE[] PROGMEM = TR_TIMEZONE; +const pm_char STR_GPSCOORD[] PROGMEM = TR_GPSCOORD; +const pm_char STR_VARIO[] PROGMEM = TR_VARIO; +#endif +const pm_char STR_RXCHANNELORD[] PROGMEM = TR_RXCHANNELORD; +const pm_char STR_SLAVE[] PROGMEM = TR_SLAVE; +const pm_char STR_MODESRC[] PROGMEM = TR_MODESRC; +const pm_char STR_MULTIPLIER[] PROGMEM = TR_MULTIPLIER; +const pm_char STR_CAL[] PROGMEM = TR_CAL; +const pm_char STR_VTRIM[] PROGMEM = TR_VTRIM; +const pm_char STR_BG[] PROGMEM = TR_BG; +const pm_char STR_MENUTOSTART[] PROGMEM = TR_MENUTOSTART; +const pm_char STR_SETMIDPOINT[] PROGMEM = TR_SETMIDPOINT; +const pm_char STR_MOVESTICKSPOTS[] PROGMEM = TR_MOVESTICKSPOTS; +const pm_char STR_RXBATT[] PROGMEM = TR_RXBATT; +const pm_char STR_TX[] PROGMEM = TR_TXnRX; +const pm_char STR_ACCEL[] PROGMEM = TR_ACCEL; +const pm_char STR_NODATA[] PROGMEM = TR_NODATA; +const pm_char STR_TM1TM2[] PROGMEM = TR_TM1TM2; +const pm_char STR_THRTHP[] PROGMEM = TR_THRTHP; +const pm_char STR_TOT[] PROGMEM = TR_TOT; +const pm_char STR_TMR1LATMAXUS[] PROGMEM = TR_TMR1LATMAXUS; +const pm_char STR_TMR1LATMINUS[] PROGMEM = TR_TMR1LATMINUS; +const pm_char STR_TMR1JITTERUS[] PROGMEM = TR_TMR1JITTERUS; +const pm_char STR_TMIXMAXMS[] PROGMEM = TR_TMIXMAXMS; +const pm_char STR_FREESTACKMINB[] PROGMEM = TR_FREESTACKMINB; +const pm_char STR_MENUTORESET[] PROGMEM = TR_MENUTORESET; +const pm_char STR_PPM[] PROGMEM = TR_PPM; +const pm_char STR_CH[] PROGMEM = TR_CH; +const pm_char STR_MODEL[] PROGMEM = TR_MODEL; +const pm_char STR_FP[] PROGMEM = TR_FP; +const pm_char STR_MIX[] PROGMEM = TR_MIX; +const pm_char STR_EEPROMLOWMEM[] PROGMEM = TR_EEPROMLOWMEM; +const pm_char STR_ALERT[] PROGMEM = TR_ALERT; +const pm_char STR_PRESSANYKEYTOSKIP[] PROGMEM = TR_PRESSANYKEYTOSKIP; +const pm_char STR_THROTTLENOTIDLE[] PROGMEM = TR_THROTTLENOTIDLE; +const pm_char STR_ALARMSDISABLED[] PROGMEM = TR_ALARMSDISABLED; +const pm_char STR_PRESSANYKEY[] PROGMEM = TR_PRESSANYKEY; +const pm_char STR_BADEEPROMDATA[] PROGMEM = TR_BADEEPROMDATA; +const pm_char STR_EEPROMFORMATTING[] PROGMEM = TR_EEPROMFORMATTING; +const pm_char STR_EEPROMOVERFLOW[] PROGMEM = TR_EEPROMOVERFLOW; +const pm_char STR_TRIMS2OFFSETS[] PROGMEM = TR_TRIMS2OFFSETS; +const pm_char STR_MENURADIOSETUP[] PROGMEM = TR_MENURADIOSETUP; + +#if defined(CPUM2560) || defined(CPUARM) +const pm_char STR_MENUDATEANDTIME[] PROGMEM = TR_MENUDATEANDTIME; +#endif + +const pm_char STR_MENUTRAINER[] PROGMEM = TR_MENUTRAINER; +const pm_char STR_MENUVERSION[] PROGMEM = TR_MENUVERSION; +const pm_char STR_MENUDIAG[] PROGMEM = TR_MENUDIAG; +const pm_char STR_MENUANA[] PROGMEM = TR_MENUANA; +const pm_char STR_MENUCALIBRATION[] PROGMEM = TR_MENUCALIBRATION; + +const pm_char STR_MENUMODELSEL[] PROGMEM = TR_MENUMODELSEL; +const pm_char STR_MENUSETUP[] PROGMEM = TR_MENUSETUP; +const pm_char STR_MENUFLIGHTPHASE[] PROGMEM = TR_MENUFLIGHTPHASE; +const pm_char STR_MENUFLIGHTPHASES[] PROGMEM = TR_MENUFLIGHTPHASES; + +#ifdef HELI +const pm_char STR_MENUHELISETUP[] PROGMEM = TR_MENUHELISETUP; +#endif + +const pm_char STR_MENUINPUTS[] PROGMEM = TR_MENUINPUTS; +const pm_char STR_MENULIMITS[] PROGMEM = TR_MENULIMITS; +const pm_char STR_MENUCURVES[] PROGMEM = TR_MENUCURVES; +const pm_char STR_MENUCURVE[] PROGMEM = TR_MENUCURVE; +const pm_char STR_MENUCUSTOMSWITCH[] PROGMEM = TR_MENUCUSTOMSWITCH; +const pm_char STR_MENUCUSTOMSWITCHES[] PROGMEM = TR_MENUCUSTOMSWITCHES; +const pm_char STR_MENUCUSTOMFUNC[] PROGMEM = TR_MENUCUSTOMFUNC; + +#if defined(LUA) +const pm_char STR_MENUCUSTOMSCRIPTS[] PROGMEM = "CUSTOM SCRIPTS"; +const pm_char STR_MENUCUSTOMSCRIPT[] PROGMEM = "CUSTOM SCRIPT"; +#endif + +#if defined(FRSKY) +const pm_char STR_MENUTELEMETRY[] PROGMEM = TR_MENUTELEMETRY; +const pm_char STR_LIMIT[] PROGMEM = TR_LIMIT; +#endif + +#if defined(TEMPLATES) +const pm_char STR_MENUTEMPLATES[] PROGMEM = TR_MENUTEMPLATES; +#endif + +const pm_char STR_MENUSTAT[] PROGMEM = TR_MENUSTAT; +const pm_char STR_MENUDEBUG[] PROGMEM = TR_MENUDEBUG; +const pm_char STR_MENUGLOBALVARS[] PROGMEM = TR_MENUGLOBALVARS; + +#if defined(DSM2) || defined(PXX) +const pm_char STR_RXNUM[] PROGMEM = TR_RXNUM; +#endif + +#if defined(PXX) +const pm_char STR_SYNCMENU[] PROGMEM = TR_SYNCMENU; +const pm_char STR_INTERNALRF[] PROGMEM = TR_INTERNALRF; +const pm_char STR_EXTERNALRF[] PROGMEM = TR_EXTERNALRF; +const pm_char STR_COUNTRYCODE[] PROGMEM = TR_COUNTRYCODE; +const pm_char STR_FAILSAFE[] PROGMEM = TR_FAILSAFE; +const pm_char STR_FAILSAFESET[] PROGMEM = TR_FAILSAFESET; +#endif + +const pm_char STR_INVERT_THR[] PROGMEM = TR_INVERT_THR; +const pm_char STR_AND_SWITCH[] PROGMEM = TR_AND_SWITCH; +extern const pm_char STR_CF[] PROGMEM = TR_CF; + +#if defined(FRSKY_HUB) +const pm_char STR_MINRSSI[] PROGMEM = TR_MINRSSI; +const pm_char STR_LATITUDE[] PROGMEM = TR_LATITUDE; +const pm_char STR_LONGITUDE[] PROGMEM = TR_LONGITUDE; +#endif + +#if defined(CPUARM) || defined(CPUM2560) +const pm_char STR_SHUTDOWN[] PROGMEM = TR_SHUTDOWN; +#endif + +const pm_char STR_BATT_CALIB[] PROGMEM = TR_BATT_CALIB; + +#if defined(CPUARM) || defined(FRSKY) +const pm_char STR_VOLTAGE[] PROGMEM = TR_VOLTAGE; +const pm_char STR_CURRENT[] PROGMEM = TR_CURRENT; +#endif + +#if defined(CPUARM) +const pm_char STR_CURRENT_CALIB[] PROGMEM = TR_CURRENT_CALIB; +const pm_char STR_UNITSSYSTEM[] PROGMEM = TR_UNITSSYSTEM; +const pm_char STR_VOICELANG[] PROGMEM = TR_VOICELANG; +const pm_char STR_MODELIDUSED[] PROGMEM = TR_MODELIDUSED; +const pm_char STR_BEEP_VOLUME[] PROGMEM = INDENT TR_BEEP_VOLUME; +const pm_char STR_WAV_VOLUME[] PROGMEM = INDENT TR_WAV_VOLUME; +const pm_char STR_VARIO_VOLUME[] PROGMEM = INDENT TR_VARIO_VOLUME; +const pm_char STR_BG_VOLUME[] PROGMEM = INDENT TR_BG_VOLUME; +const pm_char STR_PERSISTENT_MAH[] PROGMEM = TR_PERSISTENT_MAH; +#endif + +#if defined(NAVIGATION_MENUS) +const pm_char STR_SELECT_MODEL[] PROGMEM = TR_SELECT_MODEL; +const pm_char STR_CREATE_MODEL[] PROGMEM = TR_CREATE_MODEL; +const pm_char STR_COPY_MODEL[] PROGMEM = TR_COPY_MODEL; +const pm_char STR_MOVE_MODEL[] PROGMEM = TR_MOVE_MODEL; +const pm_char STR_DELETE_MODEL[] PROGMEM = TR_DELETE_MODEL; +const pm_char STR_EDIT[] PROGMEM = TR_EDIT; +const pm_char STR_INSERT_BEFORE[] PROGMEM = TR_INSERT_BEFORE; +const pm_char STR_INSERT_AFTER[] PROGMEM = TR_INSERT_AFTER; +const pm_char STR_COPY[] PROGMEM = TR_COPY; +const pm_char STR_MOVE[] PROGMEM = TR_MOVE; +const pm_char STR_PASTE[] PROGMEM = TR_PASTE; +const pm_char STR_INSERT[] PROGMEM = TR_INSERT; +const pm_char STR_DELETE[] PROGMEM = TR_DELETE; +const pm_char STR_RESET_FLIGHT[] PROGMEM = TR_RESET_FLIGHT; +const pm_char STR_RESET_TIMER1[] PROGMEM = TR_RESET_TIMER1; +const pm_char STR_RESET_TIMER2[] PROGMEM = TR_RESET_TIMER2; +const pm_char STR_RESET_TELEMETRY[] PROGMEM = TR_RESET_TELEMETRY; +const pm_char STR_STATISTICS[] PROGMEM = TR_STATISTICS; +const pm_char STR_ABOUT_US[] PROGMEM = TR_ABOUT_US; +#endif + +const pm_char STR_RESET_BTN[] PROGMEM = TR_RESET_BTN; + +#if defined(SDCARD) +const pm_char STR_BACKUP_MODEL[] PROGMEM = TR_BACKUP_MODEL; +const pm_char STR_RESTORE_MODEL[] PROGMEM = TR_RESTORE_MODEL; +const pm_char STR_SDCARD_ERROR[] PROGMEM = TR_SDCARD_ERROR; +const pm_char STR_NO_SDCARD[] PROGMEM = TR_NO_SDCARD; +const pm_char STR_INCOMPATIBLE[] PROGMEM = TR_INCOMPATIBLE; +const pm_char STR_LOGS_PATH[] PROGMEM = LOGS_PATH; +const pm_char STR_LOGS_EXT[] PROGMEM = LOGS_EXT; +const pm_char STR_MODELS_PATH[] PROGMEM = MODELS_PATH; +const pm_char STR_MODELS_EXT[] PROGMEM = MODELS_EXT; +#endif + +const pm_char STR_WARNING[] PROGMEM = TR_WARNING; +const pm_char STR_EEPROMWARN[] PROGMEM = TR_EEPROMWARN; +const pm_char STR_THROTTLEWARN[] PROGMEM = TR_THROTTLEWARN; +const pm_char STR_ALARMSWARN[] PROGMEM = TR_ALARMSWARN; +const pm_char STR_SWITCHWARN[] PROGMEM = TR_SWITCHWARN; + +const pm_char STR_SPEAKER_VOLUME[] PROGMEM = TR_SPEAKER_VOLUME; +const pm_char STR_LCD[] PROGMEM = TR_LCD; +const pm_char STR_BRIGHTNESS[] PROGMEM = TR_BRIGHTNESS; +const pm_char STR_CPU_TEMP[] PROGMEM = TR_CPU_TEMP; +const pm_char STR_CPU_CURRENT[] PROGMEM = TR_CPU_CURRENT; +const pm_char STR_CPU_MAH[] PROGMEM = TR_CPU_MAH; +const pm_char STR_COPROC[] PROGMEM = TR_COPROC; +const pm_char STR_COPROC_TEMP[] PROGMEM = TR_COPROC_TEMP; +const pm_char STR_TEMPWARNING[] PROGMEM = TR_TEMPWARNING; +const pm_char STR_CAPAWARNING[] PROGMEM = TR_CAPAWARNING; +const pm_char STR_FUNC[] PROGMEM = TR_FUNC; +const pm_char STR_V1[] PROGMEM = TR_V1; +const pm_char STR_V2[] PROGMEM = TR_V2; +const pm_char STR_DURATION[] PROGMEM = TR_DURATION; +const pm_char STR_DELAY[] PROGMEM = TR_DELAY; +const pm_char STR_SD_CARD[] PROGMEM = TR_SD_CARD; +const pm_char STR_SDHC_CARD[] PROGMEM = TR_SDHC_CARD; +const pm_char STR_NO_SOUNDS_ON_SD[] PROGMEM = TR_NO_SOUNDS_ON_SD; +const pm_char STR_NO_MODELS_ON_SD[] PROGMEM = TR_NO_MODELS_ON_SD; +const pm_char STR_NO_BITMAPS_ON_SD[] PROGMEM = TR_NO_BITMAPS_ON_SD; +const pm_char STR_NO_SCRIPTS_ON_SD[] PROGMEM = "No Scripts on SD"; // TODO TR_NO_BITMAPS_ON_SD; +const pm_char STR_PLAY_FILE[] PROGMEM = TR_PLAY_FILE; +const pm_char STR_ASSIGN_BITMAP[] PROGMEM = TR_ASSIGN_BITMAP; +const pm_char STR_EXECUTE_FILE[] PROGMEM = "Execute"; +const pm_char STR_DELETE_FILE[] PROGMEM = TR_DELETE_FILE; +const pm_char STR_COPY_FILE[] PROGMEM = TR_COPY_FILE; +const pm_char STR_RENAME_FILE[] PROGMEM = TR_RENAME_FILE; +const pm_char STR_SD_INFO[] PROGMEM = TR_SD_INFO; +const pm_char STR_SD_FORMAT[] PROGMEM = TR_SD_FORMAT; +const pm_char STR_REMOVED[] PROGMEM = TR_REMOVED; +const pm_char STR_NA[] PROGMEM = TR_NA; +const pm_char STR_HARDWARE[] PROGMEM = TR_HARDWARE; +const pm_char STR_FORMATTING[] PROGMEM = TR_FORMATTING; +const pm_char STR_TEMP_CALIB[] PROGMEM = TR_TEMP_CALIB; +const pm_char STR_TIME[] PROGMEM = TR_TIME; +const pm_char STR_BAUDRATE[] PROGMEM = TR_BAUDRATE; +const pm_char STR_SD_INFO_TITLE[] PROGMEM = TR_SD_INFO_TITLE; +const pm_char STR_SD_TYPE[] PROGMEM = TR_SD_TYPE; +const pm_char STR_SD_SPEED[] PROGMEM = TR_SD_SPEED; +const pm_char STR_SD_SECTORS[] PROGMEM = TR_SD_SECTORS; +const pm_char STR_SD_SIZE[] PROGMEM = TR_SD_SIZE; +const pm_char STR_TYPE[] PROGMEM = TR_TYPE; +const pm_char STR_GLOBAL_VARS[] PROGMEM = TR_GLOBAL_VARS; +const pm_char STR_GLOBAL_VAR[] PROGMEM = TR_GLOBAL_VAR; +const pm_char STR_OWN[] PROGMEM = TR_OWN; +const pm_char STR_ROTARY_ENCODER[] PROGMEM = TR_ROTARY_ENCODER; +const pm_char STR_DATE[] PROGMEM = TR_DATE; +const pm_char STR_CHANNELS_MONITOR[] PROGMEM = TR_CHANNELS_MONITOR; +const pm_char STR_PATH_TOO_LONG[] PROGMEM = "Path too long"; +const pm_char STR_VIEW_TEXT[] PROGMEM = "View text"; + +#if LCD_W >= 212 + const pm_char STR_MODELNAME[] PROGMEM = TR_MODELNAME; + const pm_char STR_PHASENAME[] PROGMEM = TR_PHASENAME; + const pm_char STR_MIXNAME[] PROGMEM = TR_MIXNAME; + const pm_char STR_EXPONAME[] PROGMEM = TR_EXPONAME; +#endif + +#if LCD_W >= 212 + const char * STR_PHASES_HEADERS[] = TR_PHASES_HEADERS; + const char * STR_LIMITS_HEADERS[] = TR_LIMITS_HEADERS; + const char * STR_CSW_HEADERS[] = TR_CSW_HEADERS; +#endif + +#if defined(PCBTARANIS) + const pm_char STR_BYTES[] PROGMEM = TR_BYTES; + const pm_char STR_MODULE_BIND[] PROGMEM = TR_MODULE_BIND; + const pm_char STR_SET[] PROGMEM = TR_SET; + const pm_char STR_TRAINER[] PROGMEM = TR_TRAINER; + const pm_char STR_ANTENNAPROBLEM[] PROGMEM = TR_ANTENNAPROBLEM; + const pm_char STR_MODULE[] PROGMEM = TR_MODULE; + const pm_char STR_CHANNELRANGE[] PROGMEM = TR_CHANNELRANGE; + const pm_char STR_LOWALARM[] PROGMEM = TR_LOWALARM; + const pm_char STR_CRITICALALARM[] PROGMEM = TR_CRITICALALARM; + const pm_char STR_ENABLE_POPUP[] PROGMEM = "Enable Popup"; + const pm_char STR_DISABLE_POPUP[] PROGMEM = "Disable Popup"; + const pm_char STR_CURVE_PRESET[] PROGMEM = "Preset..."; + const pm_char STR_PRESET[] PROGMEM = "Preset"; + const pm_char STR_MIRROR[] PROGMEM = "Mirror"; + const pm_char STR_CLEAR[] PROGMEM = "Clear"; + const pm_char STR_RESET[] PROGMEM = "Reset"; + const pm_char STR_COPY_TRIMS_TO_OFFSET[] = "Copy Trims To Offset"; + const pm_char STR_TOP_BAR[] PROGMEM = "Top Bar"; + const pm_char STR_ALTITUDE[] PROGMEM = INDENT "Altitude"; + const pm_char STR_SCALE[] PROGMEM = "Scale"; + const pm_char STR_VIEW_CHANNELS[] PROGMEM = "View Channels"; + const pm_char STR_VIEW_NOTES[] PROGMEM = "View Notes"; +#endif + +#if MENUS_LOCK == 1 + const pm_char STR_UNLOCKED[] PROGMEM = TR_UNLOCKED; + const pm_char STR_MODS_FORBIDDEN[] PROGMEM = TR_MODS_FORBIDDEN; +#endif + +#if defined(PCBTARANIS) || defined(DSM2) + const pm_char STR_MODULE_RANGE[] PROGMEM = TR_MODULE_RANGE; +#endif + +#if defined(MAVLINK) + const pm_char STR_MAVLINK_RC_RSSI_SCALE_LABEL[] PROGMEM = TR_MAVLINK_RC_RSSI_SCALE_LABEL; + const pm_char STR_MAVLINK_PC_RSSI_EN_LABEL[] PROGMEM = TR_MAVLINK_PC_RSSI_EN_LABEL; + const pm_char STR_MAVMENUSETUP_TITLE[] PROGMEM = TR_MAVMENUSETUP_TITLE; + const pm_char STR_MAVLINK_BAUD_LABEL[] PROGMEM = TR_MAVLINK_BAUD_LABEL; + const pm_char STR_MAVLINK_INFOS[] PROGMEM = TR_MAVLINK_INFOS; + const pm_char STR_MAVLINK_MODE[] PROGMEM = TR_MAVLINK_MODE; + const pm_char STR_MAVLINK_CUR_MODE[] PROGMEM = TR_MAVLINK_CUR_MODE; + const pm_char STR_MAVLINK_ARMED[] PROGMEM = TR_MAVLINK_ARMED; + const pm_char STR_MAVLINK_BAT_MENU_TITLE[] PROGMEM = TR_MAVLINK_BAT_MENU_TITLE; + const pm_char STR_MAVLINK_BATTERY_LABEL[] PROGMEM = TR_MAVLINK_BATTERY_LABEL; + const pm_char STR_MAVLINK_RC_RSSI_LABEL[] PROGMEM = TR_MAVLINK_RC_RSSI_LABEL; + const pm_char STR_MAVLINK_PC_RSSI_LABEL[] PROGMEM = TR_MAVLINK_PC_RSSI_LABEL; + const pm_char STR_MAVLINK_NAV_MENU_TITLE[] PROGMEM = TR_MAVLINK_NAV_MENU_TITLE; + const pm_char STR_MAVLINK_COURSE[] PROGMEM = TR_MAVLINK_COURSE; + const pm_char STR_MAVLINK_HEADING[] PROGMEM = TR_MAVLINK_HEADING; + const pm_char STR_MAVLINK_BEARING[] PROGMEM = TR_MAVLINK_BEARING; + const pm_char STR_MAVLINK_ALTITUDE[] PROGMEM = TR_MAVLINK_ALTITUDE; + const pm_char STR_MAVLINK_GPS[] PROGMEM = TR_MAVLINK_GPS; + const pm_char STR_MAVLINK_NO_FIX[] PROGMEM = TR_MAVLINK_NO_FIX; + const pm_char STR_MAVLINK_SAT[] PROGMEM = TR_MAVLINK_SAT; + const pm_char STR_MAVLINK_HDOP[] PROGMEM = TR_MAVLINK_HDOP; + const pm_char STR_MAVLINK_LAT[] PROGMEM = TR_MAVLINK_LAT; + const pm_char STR_MAVLINK_LON[] PROGMEM = TR_MAVLINK_LON; +#endif + +#if !defined(CPUM64) + const pm_char STR_ABOUTUS[] PROGMEM = TR_ABOUTUS; + const pm_char STR_ABOUT_OPENTX_1[] PROGMEM = TR_ABOUT_OPENTX_1; + const pm_char STR_ABOUT_OPENTX_2[] PROGMEM = TR_ABOUT_OPENTX_2; + const pm_char STR_ABOUT_OPENTX_3[] PROGMEM = TR_ABOUT_OPENTX_3; + const pm_char STR_ABOUT_OPENTX_4[] PROGMEM = TR_ABOUT_OPENTX_4; + const pm_char STR_ABOUT_OPENTX_5[] PROGMEM = TR_ABOUT_OPENTX_5; + + const pm_char STR_ABOUT_BERTRAND_1[] PROGMEM = TR_ABOUT_BERTRAND_1; + const pm_char STR_ABOUT_BERTRAND_2[] PROGMEM = TR_ABOUT_BERTRAND_2; + const pm_char STR_ABOUT_BERTRAND_3[] PROGMEM = TR_ABOUT_BERTRAND_3; + + const pm_char STR_ABOUT_MIKE_1[] PROGMEM = TR_ABOUT_MIKE_1; + const pm_char STR_ABOUT_MIKE_2[] PROGMEM = TR_ABOUT_MIKE_2; + const pm_char STR_ABOUT_MIKE_3[] PROGMEM = TR_ABOUT_MIKE_3; + const pm_char STR_ABOUT_MIKE_4[] PROGMEM = TR_ABOUT_MIKE_4; + + const pm_char STR_ABOUT_ROMOLO_1[] PROGMEM = TR_ABOUT_ROMOLO_1; + const pm_char STR_ABOUT_ROMOLO_2[] PROGMEM = TR_ABOUT_ROMOLO_2; + const pm_char STR_ABOUT_ROMOLO_3[] PROGMEM = TR_ABOUT_ROMOLO_3; + + const pm_char STR_ABOUT_ANDRE_1[] PROGMEM = TR_ABOUT_ANDRE_1; + const pm_char STR_ABOUT_ANDRE_2[] PROGMEM = TR_ABOUT_ANDRE_2; + const pm_char STR_ABOUT_ANDRE_3[] PROGMEM = TR_ABOUT_ANDRE_3; + + const pm_char STR_ABOUT_ROB_1[] PROGMEM = TR_ABOUT_ROB_1; + const pm_char STR_ABOUT_ROB_2[] PROGMEM = TR_ABOUT_ROB_2; + + const pm_char STR_ABOUT_MARTIN_1[] PROGMEM = TR_ABOUT_MARTIN_1; + const pm_char STR_ABOUT_MARTIN_2[] PROGMEM = TR_ABOUT_MARTIN_2; + + const pm_char STR_ABOUT_HARDWARE_1[] PROGMEM = TR_ABOUT_HARDWARE_1; + const pm_char STR_ABOUT_HARDWARE_2[] PROGMEM = TR_ABOUT_HARDWARE_2; + const pm_char STR_ABOUT_HARDWARE_3[] PROGMEM = TR_ABOUT_HARDWARE_3; + + const pm_char STR_ABOUT_PARENTS_1[] PROGMEM = TR_ABOUT_PARENTS_1; + const pm_char STR_ABOUT_PARENTS_2[] PROGMEM = TR_ABOUT_PARENTS_2; + const pm_char STR_ABOUT_PARENTS_3[] PROGMEM = TR_ABOUT_PARENTS_3; + const pm_char STR_ABOUT_PARENTS_4[] PROGMEM = TR_ABOUT_PARENTS_4; +#endif + +const pm_uchar font_5x7[] PROGMEM = { +#include "font_05x07.lbm" +#if defined(TRANSLATIONS_DE) +#include "font_de_05x07.lbm" +#elif defined(TRANSLATIONS_CZ) +#include "font_cz_05x07.lbm" +#elif defined(TRANSLATIONS_ES) +#include "font_es_05x07.lbm" +#elif defined(TRANSLATIONS_FR) +#include "font_fr_05x07.lbm" +#elif defined(TRANSLATIONS_IT) +#include "font_it_05x07.lbm" +#elif defined(TRANSLATIONS_PL) +#include "font_pl_05x07.lbm" +#elif defined(TRANSLATIONS_PT) +#include "font_pt_05x07.lbm" +#elif defined(TRANSLATIONS_SE) +#include "font_se_05x07.lbm" +#endif +}; + +const pm_uchar font_10x14[] PROGMEM = { +#include "font_10x14_compressed.lbm" +#if defined(CPUARM) + #if defined(TRANSLATIONS_DE) + #include "font_de_10x14.lbm" + #elif defined(TRANSLATIONS_CZ) + #include "font_cz_10x14.lbm" + #elif defined(TRANSLATIONS_ES) + #include "font_es_10x14.lbm" + #elif defined(TRANSLATIONS_FR) + #include "font_fr_10x14.lbm" + #elif defined(TRANSLATIONS_IT) + #include "font_it_10x14.lbm" + #elif defined(TRANSLATIONS_PL) + #include "font_pl_10x14.lbm" + #elif defined(TRANSLATIONS_PT) + #include "font_pt_10x14.lbm" + #elif defined(TRANSLATIONS_SE) + #include "font_se_10x14.lbm" + #endif +#endif +}; + +#if defined(CPUARM) +const pm_uchar font_3x5[] PROGMEM = { +#include "font_03x05.lbm" +}; + +const pm_uchar font_4x6[] PROGMEM = { +#include "font_04x06.lbm" +#if defined(TRANSLATIONS_DE) +#include "font_de_04x06.lbm" +#elif defined(TRANSLATIONS_CZ) +#include "font_cz_04x06.lbm" +#elif defined(TRANSLATIONS_ES) +#include "font_es_04x06.lbm" +#elif defined(TRANSLATIONS_FR) +#include "font_fr_04x06.lbm" +#elif defined(TRANSLATIONS_IT) +#include "font_it_04x06.lbm" +#elif defined(TRANSLATIONS_PL) +#include "font_pl_04x06.lbm" +#elif defined(TRANSLATIONS_PT) +#include "font_pt_04x06.lbm" +#elif defined(TRANSLATIONS_SE) +#include "font_se_04x06.lbm" +#endif +}; + +const pm_uchar font_8x10[] PROGMEM = { +#include "font_08x10.lbm" +#if defined(TRANSLATIONS_DE) +#include "font_de_08x10.lbm" +#elif defined(TRANSLATIONS_CZ) +#include "font_cz_08x10.lbm" +#elif defined(TRANSLATIONS_ES) +#include "font_es_08x10.lbm" +#elif defined(TRANSLATIONS_FR) +#include "font_fr_08x10.lbm" +#elif defined(TRANSLATIONS_IT) +#include "font_it_08x10.lbm" +#elif defined(TRANSLATIONS_PL) +#include "font_pl_08x10.lbm" +#elif defined(TRANSLATIONS_PT) +#include "font_pt_08x10.lbm" +#elif defined(TRANSLATIONS_SE) +#include "font_se_08x10.lbm" +#endif +}; + +const pm_uchar font_4x6_extra[] PROGMEM = { +#include "font_04x06_extra.lbm" +}; + +const pm_uchar font_5x7_extra[] PROGMEM = { +#include "font_05x07_extra.lbm" +}; + +const pm_uchar font_10x14_extra[] PROGMEM = { +#include "font_10x14_extra.lbm" +}; + +#endif + diff --git a/radio/src/translations.h b/radio/src/translations.h index 4a3e0600a..b49bb005d 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -343,6 +343,8 @@ extern const pm_char STR_BAR[]; extern const pm_char STR_ALARM[]; extern const pm_char STR_USRDATA[]; extern const pm_char STR_BLADES[]; +extern const pm_char STR_SPURGEAR[]; // T.Foley +extern const pm_char STR_PINIONGEAR[]; // T.Foley extern const pm_char STR_SCREEN[]; extern const pm_char STR_SOUND_LABEL[]; extern const pm_char STR_LENGTH[]; diff --git a/radio/src/translations.h~ b/radio/src/translations.h~ new file mode 100644 index 000000000..4a3e0600a --- /dev/null +++ b/radio/src/translations.h~ @@ -0,0 +1,754 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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. + * + */ + +#ifndef translations_h +#define translations_h + +#if defined(TRANSLATIONS_FR) +#include "translations/fr.h" +#define LEN_SPECIAL_CHARS 3 +#elif defined(TRANSLATIONS_IT) +#include "translations/it.h" +#define LEN_SPECIAL_CHARS 1 +#elif defined(TRANSLATIONS_SE) +#include "translations/se.h" +#define LEN_SPECIAL_CHARS 6 +#elif defined(TRANSLATIONS_DE) +#include "translations/de.h" +#define LEN_SPECIAL_CHARS 6 +#elif defined(TRANSLATIONS_CZ) +#include "translations/cz.h" +#define LEN_SPECIAL_CHARS 0 +#elif defined(TRANSLATIONS_ES) +#include "translations/es.h" +#define LEN_SPECIAL_CHARS 0 +#elif defined(TRANSLATIONS_PL) +#include "translations/pl.h" +#define LEN_SPECIAL_CHARS 0 +#elif defined(TRANSLATIONS_PT) +#include "translations/pt.h" +#define LEN_SPECIAL_CHARS 0 +#else +#include "translations/en.h" +#define LEN_SPECIAL_CHARS 0 +#endif + +#define PSIZE(x) ( sizeof(x) - 1 ) +#define EOFS(x) ( OFS_##x + sizeof(TR_##x) ) + +#if LCD_W >= 212 + #define TR(x,y) y +#else + #define TR(x,y) x +#endif + +// The non-0-terminated-strings + +extern const pm_char STR_OPEN9X[]; + +#define OFS_OFFON 0 +#define OFS_MMMINV (OFS_OFFON + sizeof(TR_OFFON)) +#define OFS_NCHANNELS (OFS_MMMINV + sizeof(TR_MMMINV)) +#if defined(GRAPHICS) +#define OFS_VBEEPMODE (OFS_NCHANNELS + sizeof(TR_NCHANNELS)) +#else +#define OFS_VBEEPLEN (OFS_NCHANNELS + sizeof(TR_NCHANNELS)) +#define OFS_VBEEPMODE (OFS_VBEEPLEN + sizeof(TR_VBEEPLEN)) +#endif +#if defined(ROTARY_ENCODERS) +#define OFS_VRENAVIG (OFS_VBEEPMODE + sizeof(TR_VBEEPMODE)) +#define OFS_VRENCODERS (OFS_VRENAVIG + sizeof(TR_VRENAVIG)) +#define OFS_TRNMODE (OFS_VRENCODERS + sizeof(TR_VRENCODERS)) +#elif defined(ROTARY_ENCODER_NAVIGATION) +#define OFS_VRENCODERS (OFS_VBEEPMODE + sizeof(TR_VBEEPMODE)) +#define OFS_TRNMODE (OFS_VRENCODERS + sizeof(TR_VRENCODERS)) +#else +#define OFS_TRNMODE (OFS_VBEEPMODE + sizeof(TR_VBEEPMODE)) +#endif +#define OFS_TRNCHN (OFS_TRNMODE + sizeof(TR_TRNMODE)) +#define OFS_VTRIMINC (OFS_TRNCHN + sizeof(TR_TRNCHN)) +#define OFS_RETA123 (OFS_VTRIMINC + sizeof(TR_VTRIMINC)) +#define OFS_VPROTOS (OFS_RETA123 + sizeof(TR_RETA123)) +#define OFS_POSNEG (OFS_VPROTOS + sizeof(TR_VPROTOS)) +#define OFS_VBLMODE (OFS_POSNEG + sizeof(TR_POSNEG)) +#define OFS_VCURVEFUNC (OFS_VBLMODE + sizeof(TR_VBLMODE)) +#define OFS_VMLTPX (OFS_VCURVEFUNC + sizeof(TR_VCURVEFUNC)) +#define OFS_VMLTPX2 (OFS_VMLTPX + sizeof(TR_VMLTPX)) +#define OFS_VMIXTRIMS (OFS_VMLTPX2 + sizeof(TR_VMLTPX2)) +#define OFS_VCSWFUNC (OFS_VMIXTRIMS + sizeof(TR_VMIXTRIMS)) +#define OFS_VFSWFUNC (OFS_VCSWFUNC + sizeof(TR_VCSWFUNC)) +#define OFS_VFSWRESET (OFS_VFSWFUNC + sizeof(TR_VFSWFUNC)) +#define OFS_FUNCSOUNDS (OFS_VFSWRESET + sizeof(TR_VFSWRESET)) +#define OFS_VTELEMCHNS (OFS_FUNCSOUNDS + sizeof(TR_FUNCSOUNDS)) +#if defined(FRSKY) || defined(CPUARM) + #if defined(CPUARM) + #define OFS_VTELEMUNIT_IMP (OFS_VTELEMCHNS + sizeof(TR_VTELEMCHNS)) + #define OFS_VTELEMUNIT_NORM (OFS_VTELEMUNIT_IMP + sizeof(TR_VTELEMUNIT_IMP)) + #define OFS_VALARM (OFS_VTELEMUNIT_NORM + sizeof(TR_VTELEMUNIT_NORM)) + #else + #define OFS_VTELEMUNIT (OFS_VTELEMCHNS + sizeof(TR_VTELEMCHNS)) + #define OFS_VALARM (OFS_VTELEMUNIT + sizeof(TR_VTELEMUNIT)) + #endif + #define OFS_VALARMFN (OFS_VALARM + sizeof(TR_VALARM)) + #define OFS_VTELPROTO (OFS_VALARMFN + sizeof(TR_VALARMFN)) + #define OFS_GPSFORMAT (OFS_VTELPROTO + sizeof(TR_VTELPROTO)) + #define OFS_VOLTSRC (OFS_GPSFORMAT + sizeof(TR_GPSFORMAT)) + #define OFS_VARIOSRC (OFS_VOLTSRC + sizeof(TR_VOLTSRC)) + #define OFS_VSCREEN (OFS_VARIOSRC + sizeof(TR_VARIOSRC)) + #define OFS_VTEMPLATES (OFS_VSCREEN + sizeof(TR_VSCREEN)) +#else + #define OFS_VTEMPLATES (OFS_VTELEMCHNS + sizeof(TR_VTELEMCHNS)) +#endif +#if defined(TEMPLATES) + #define OFS_VSWASHTYPE (OFS_VTEMPLATES + sizeof(TR_VTEMPLATES)) +#else + #define OFS_VSWASHTYPE (OFS_VTEMPLATES) +#endif +#if defined(HELI) + #define OFS_VKEYS (OFS_VSWASHTYPE + sizeof(TR_VSWASHTYPE)) +#else + #define OFS_VKEYS (OFS_VSWASHTYPE) +#endif +#define OFS_VSWITCHES (OFS_VKEYS + sizeof(TR_VKEYS)) +#define OFS_VSRCRAW (OFS_VSWITCHES + sizeof(TR_VSWITCHES)) +#define OFS_VTMRMODES (OFS_VSRCRAW + sizeof(TR_VSRCRAW)) +#define OFS_DATETIME (OFS_VTMRMODES + sizeof(TR_VTMRMODES)) +#if defined(CPUM2560) || defined(CPUARM) + #define OFS_VLCD (OFS_DATETIME + sizeof(TR_DATETIME)) +#else + #define OFS_VLCD (OFS_DATETIME) +#endif +#if defined(CPUARM) + #define OFS_VUNITSSYSTEM (OFS_VLCD + sizeof(TR_VLCD)) + #define OFS_VBEEPCOUNTDOWN (OFS_VUNITSSYSTEM + sizeof(TR_VUNITSSYSTEM)) + #define OFS_COUNTRYCODES (OFS_VBEEPCOUNTDOWN + sizeof(TR_VBEEPCOUNTDOWN)) +#else + #define OFS_COUNTRYCODES (OFS_VLCD) +#endif +#if defined(PXX) + #define OFS_VFAILSAFE (OFS_COUNTRYCODES + sizeof(TR_COUNTRYCODES)) +#else + #define OFS_VFAILSAFE (OFS_COUNTRYCODES) +#endif +#if defined(PXX) + #define OFS_VTRAINERMODES (OFS_VFAILSAFE + sizeof(TR_VFAILSAFE)) +#else + #define OFS_VTRAINERMODES (OFS_VFAILSAFE) +#endif +#if defined(PCBTARANIS) + #define OFS_MAVLINK_BAUDS (OFS_VTRAINERMODES + sizeof(TR_VTRAINERMODES)) +#else + #define OFS_MAVLINK_BAUDS (OFS_VTRAINERMODES) +#endif +#if defined(MAVLINK) + #define OFS_MAVLINK_AC_MODES (OFS_MAVLINK_BAUDS + sizeof(TR_MAVLINK_BAUDS)) + #define OFS_MAVLINK_AP_MODES (OFS_MAVLINK_AC_MODES + sizeof(TR_MAVLINK_AC_MODES)) + #define OFS_SPARE (OFS_MAVLINK_AP_MODES + sizeof(TR_MAVLINK_AP_MODES)) +#else + #define OFS_SPARE (OFS_MAVLINK_BAUDS) +#endif + +#define STR_OFFON (STR_OPEN9X + OFS_OFFON) +#define STR_MMMINV (STR_OPEN9X + OFS_MMMINV) +#define STR_NCHANNELS (STR_OPEN9X + OFS_NCHANNELS) +#if !defined(GRAPHICS) +#define STR_VBEEPLEN (STR_OPEN9X + OFS_VBEEPLEN) +#endif +#define STR_VBEEPMODE (STR_OPEN9X + OFS_VBEEPMODE) +#define STR_TRNMODE (STR_OPEN9X + OFS_TRNMODE) +#define STR_TRNCHN (STR_OPEN9X + OFS_TRNCHN) +#define STR_VTRIMINC (STR_OPEN9X + OFS_VTRIMINC) +#define STR_RETA123 (STR_OPEN9X + OFS_RETA123) +#define STR_VPROTOS (STR_OPEN9X + OFS_VPROTOS) +#define STR_POSNEG (STR_OPEN9X + OFS_POSNEG) +#define STR_VBLMODE (STR_OPEN9X + OFS_VBLMODE) +#define STR_VCURVEFUNC (STR_OPEN9X + OFS_VCURVEFUNC) +#define STR_VSIDE STR_VCURVEFUNC +#define LEN_VSIDE LEN_VCURVEFUNC +#define STR_VMLTPX (STR_OPEN9X + OFS_VMLTPX) +#define STR_VMLTPX2 (STR_OPEN9X + OFS_VMLTPX2) +#define STR_VMIXTRIMS (STR_OPEN9X + OFS_VMIXTRIMS) +#define STR_VCSWFUNC (STR_OPEN9X + OFS_VCSWFUNC) +#define STR_VFSWFUNC (STR_OPEN9X + OFS_VFSWFUNC) +#define STR_VFSWRESET (STR_OPEN9X + OFS_VFSWRESET) +#define STR_FUNCSOUNDS (STR_OPEN9X + OFS_FUNCSOUNDS) +#define STR_VTELEMCHNS (STR_OPEN9X + OFS_VTELEMCHNS) + +#if defined(FRSKY) || defined(CPUARM) + #if defined(CPUARM) + #define STR_VTELEMUNIT (STR_OPEN9X + (g_eeGeneral.imperial ? OFS_VTELEMUNIT_IMP : OFS_VTELEMUNIT_NORM)) + #else + #define STR_VTELEMUNIT (STR_OPEN9X + OFS_VTELEMUNIT) + #endif +#define STR_VALARM (STR_OPEN9X + OFS_VALARM) +#define STR_VALARMFN (STR_OPEN9X + OFS_VALARMFN) +#define STR_VTELPROTO (STR_OPEN9X + OFS_VTELPROTO) +#define STR_GPSFORMAT (STR_OPEN9X + OFS_GPSFORMAT) +#define STR_VOLTSRC (STR_OPEN9X + OFS_VOLTSRC) +#define STR_VARIOSRC (STR_OPEN9X + OFS_VARIOSRC) +#define STR_VSCREEN (STR_OPEN9X + OFS_VSCREEN) +#define STR_TELEMCHNS (STR_OPEN9X + OFS_TELEMCHNS) +#endif + +#ifdef TEMPLATES +#define STR_VTEMPLATES (STR_OPEN9X + OFS_VTEMPLATES) +#endif + +#ifdef HELI +#define STR_VSWASHTYPE (STR_OPEN9X + OFS_VSWASHTYPE) +#endif + +#define STR_VKEYS (STR_OPEN9X + OFS_VKEYS) +#define STR_VSWITCHES (STR_OPEN9X + OFS_VSWITCHES) +#define STR_VSRCRAW (STR_OPEN9X + OFS_VSRCRAW) +#define STR_VTMRMODES (STR_OPEN9X + OFS_VTMRMODES) + +#if defined(ROTARY_ENCODERS) + #define STR_VRENAVIG (STR_OPEN9X + OFS_VRENAVIG) +#endif + +#if defined(ROTARY_ENCODER_NAVIGATION) + #define STR_VRENCODERS (STR_OPEN9X + OFS_VRENCODERS) +#endif + +#if defined(CPUM2560) || defined(CPUARM) +#define STR_DATETIME (STR_OPEN9X + OFS_DATETIME) +#endif + +#if defined(CPUARM) + #define STR_VLCD (STR_OPEN9X + OFS_VLCD) + #define STR_VUNITSSYSTEM (STR_OPEN9X + OFS_VUNITSSYSTEM) + #define STR_VBEEPCOUNTDOWN (STR_OPEN9X + OFS_VBEEPCOUNTDOWN) +#endif + +#if defined(PXX) + #define STR_COUNTRYCODES (STR_OPEN9X + OFS_COUNTRYCODES) + #define STR_VFAILSAFE (STR_OPEN9X + OFS_VFAILSAFE) +#endif + +#if defined(PCBTARANIS) + #define STR_VTRAINERMODES (STR_OPEN9X + OFS_VTRAINERMODES) +#endif + +#if defined(MAVLINK) + #define STR_MAVLINK_BAUDS (STR_OPEN9X + OFS_MAVLINK_BAUDS) + #define STR_MAVLINK_AC_MODES (STR_OPEN9X + OFS_MAVLINK_AC_MODES) + #define STR_MAVLINK_AP_MODES (STR_OPEN9X + OFS_MAVLINK_AP_MODES) +#endif + +// The 0-terminated-strings +#define NO_INDENT(x) (x)+LEN_INDENT + +extern const pm_char STR_POPUPS[]; +#define STR_EXIT (STR_POPUPS + OFS_EXIT) +extern const pm_char STR_MENUWHENDONE[]; +extern const pm_char STR_FREE[]; +#define LEN_FREE PSIZE(TR_FREE) +extern const pm_char STR_DELETEMODEL[]; +extern const pm_char STR_COPYINGMODEL[]; +extern const pm_char STR_MOVINGMODEL[]; +extern const pm_char STR_LOADINGMODEL[]; +extern const pm_char STR_NAME[]; +extern const pm_char STR_BITMAP[]; +extern const pm_char STR_TIMER[]; +extern const pm_char STR_ELIMITS[]; +extern const pm_char STR_ETRIMS[]; +extern const pm_char STR_TRIMINC[]; +extern const pm_char STR_TTRACE[]; +extern const pm_char STR_TTRIM[]; +extern const pm_char STR_BEEPCTR[]; +extern const pm_char STR_PROTO[]; +extern const pm_char STR_PPMFRAME[]; +extern const pm_char STR_MS[]; +extern const pm_char STR_SWITCH[]; +extern const pm_char STR_TRIMS[]; +extern const pm_char STR_FADEIN[]; +extern const pm_char STR_FADEOUT[]; +extern const pm_char STR_DEFAULT[]; +extern const pm_char STR_CHECKTRIMS[]; +extern const pm_char STR_SWASHTYPE[]; +extern const pm_char STR_COLLECTIVE[]; +extern const pm_char STR_SWASHRING[]; +extern const pm_char STR_ELEDIRECTION[]; +extern const pm_char STR_AILDIRECTION[]; +extern const pm_char STR_COLDIRECTION[]; +extern const pm_char STR_MODE[]; +#if defined(AUDIO) && defined(BUZZER) +extern const pm_char STR_SPEAKER[]; +extern const pm_char STR_BUZZER[]; +#else +#define STR_SPEAKER STR_MODE +#define STR_BUZZER STR_MODE +#endif +extern const pm_char STR_NOFREEEXPO[]; +extern const pm_char STR_NOFREEMIXER[]; +extern const pm_char STR_INSERTMIX[]; +extern const pm_char STR_EDITMIX[]; +extern const pm_char STR_SOURCE[]; +extern const pm_char STR_WEIGHT[]; +extern const pm_char STR_EXPO[]; +extern const pm_char STR_SIDE[]; +extern const pm_char STR_DIFFERENTIAL[]; +extern const pm_char STR_OFFSET[]; +extern const pm_char STR_TRIM[]; +extern const pm_char STR_DREX[]; +extern const pm_char STR_CURVE[]; +extern const pm_char STR_FLMODE[]; +extern const pm_char STR_MIXWARNING[]; +extern const pm_char STR_OFF[]; +extern const pm_char STR_MULTPX[]; +extern const pm_char STR_DELAYDOWN[]; +extern const pm_char STR_DELAYUP[]; +extern const pm_char STR_SLOWDOWN[]; +extern const pm_char STR_SLOWUP[]; +extern const pm_char STR_MIXER[]; +extern const pm_char STR_CV[]; +extern const pm_char STR_GV[]; +extern const pm_char STR_ACHANNEL[]; +extern const pm_char STR_RANGE[]; +extern const pm_char STR_BAR[]; +extern const pm_char STR_ALARM[]; +extern const pm_char STR_USRDATA[]; +extern const pm_char STR_BLADES[]; +extern const pm_char STR_SCREEN[]; +extern const pm_char STR_SOUND_LABEL[]; +extern const pm_char STR_LENGTH[]; +extern const pm_char STR_SPKRPITCH[]; +extern const pm_char STR_HAPTIC_LABEL[]; +extern const pm_char STR_HAPTICSTRENGTH[]; +extern const pm_char STR_CONTRAST[]; +extern const pm_char STR_ALARMS_LABEL[]; +#if defined(BATTGRAPH) || defined(PCBTARANIS) +extern const pm_char STR_BATTERY_RANGE[]; +#endif +extern const pm_char STR_BATTERYWARNING[]; +extern const pm_char STR_INACTIVITYALARM[]; +extern const pm_char STR_MEMORYWARNING[]; +extern const pm_char STR_ALARMWARNING[]; +extern const pm_char STR_RENAVIG[]; +extern const pm_char STR_THROTTLEREVERSE[]; +extern const pm_char STR_MINUTEBEEP[]; +extern const pm_char STR_BEEPCOUNTDOWN[]; +extern const pm_char STR_PERSISTENT[]; +extern const pm_char STR_BACKLIGHT_LABEL[]; +extern const pm_char STR_BLDELAY[]; +#if defined(PWM_BACKLIGHT) +extern const pm_char STR_BLONBRIGHTNESS[]; +extern const pm_char STR_BLOFFBRIGHTNESS[]; +#endif +extern const pm_char STR_SPLASHSCREEN[]; +extern const pm_char STR_THROTTLEWARNING[]; +extern const pm_char STR_SWITCHWARNING[]; +extern const pm_char STR_TIMEZONE[]; +extern const pm_char STR_GPSCOORD[]; +extern const pm_char STR_VARIO[]; +extern const pm_char STR_RXCHANNELORD[]; +extern const pm_char STR_SLAVE[]; +extern const pm_char STR_MODESRC[]; +extern const pm_char STR_MULTIPLIER[]; +#define LEN_MULTIPLIER PSIZE(TR_MULTIPLIER) +extern const pm_char STR_CAL[]; +extern const pm_char STR_VTRIM[]; +extern const pm_char STR_BG[]; +extern const pm_char STR_MENUTOSTART[]; +extern const pm_char STR_SETMIDPOINT[]; +extern const pm_char STR_MOVESTICKSPOTS[]; +extern const pm_char STR_RXBATT[]; +extern const pm_char STR_TX[]; +#define STR_RX (STR_TX+OFS_RX) +extern const pm_char STR_ACCEL[]; +extern const pm_char STR_NODATA[]; +extern const pm_char STR_TM1TM2[]; +extern const pm_char STR_THRTHP[]; +extern const pm_char STR_TOT[]; +extern const pm_char STR_TMR1LATMAXUS[]; +extern const pm_char STR_TMR1LATMINUS[]; +extern const pm_char STR_TMR1JITTERUS[]; +extern const pm_char STR_TMIXMAXMS[]; +extern const pm_char STR_FREESTACKMINB[]; +extern const pm_char STR_MENUTORESET[]; +extern const pm_char STR_PPM[]; +extern const pm_char STR_CH[]; +extern const pm_char STR_MODEL[]; +extern const pm_char STR_FP[]; +#if defined(CPUARM) +extern const pm_char STR_MIX[]; +#endif +extern const pm_char STR_EEPROMLOWMEM[]; +extern const pm_char STR_ALERT[]; +extern const pm_char STR_PRESSANYKEYTOSKIP[]; +extern const pm_char STR_THROTTLENOTIDLE[]; +extern const pm_char STR_ALARMSDISABLED[]; +extern const pm_char STR_PRESSANYKEY[]; +#define LEN_PRESSANYKEY PSIZE(TR_PRESSANYKEY) +extern const pm_char STR_BADEEPROMDATA[]; +extern const pm_char STR_EEPROMFORMATTING[]; +extern const pm_char STR_EEPROMOVERFLOW[]; +extern const pm_char STR_TRIMS2OFFSETS[]; +extern const pm_char STR_MENURADIOSETUP[]; +extern const pm_char STR_MENUDATEANDTIME[]; +extern const pm_char STR_MENUTRAINER[]; +extern const pm_char STR_MENUVERSION[]; +extern const pm_char STR_MENUDIAG[]; +extern const pm_char STR_MENUANA[]; +extern const pm_char STR_MENUCALIBRATION[]; +extern const pm_char STR_MENUMODELSEL[]; +extern const pm_char STR_MENUSETUP[]; +extern const pm_char STR_MENUFLIGHTPHASE[]; +extern const pm_char STR_MENUFLIGHTPHASES[]; +extern const pm_char STR_MENUHELISETUP[]; +extern const pm_char STR_MENUINPUTS[]; +extern const pm_char STR_MENULIMITS[]; +extern const pm_char STR_MENUCURVES[]; +extern const pm_char STR_MENUCURVE[]; +extern const pm_char STR_MENUCUSTOMSWITCH[]; +extern const pm_char STR_MENUCUSTOMSWITCHES[]; +extern const pm_char STR_MENUCUSTOMFUNC[]; +extern const pm_char STR_MENUCUSTOMSCRIPTS[]; +extern const pm_char STR_MENUCUSTOMSCRIPT[]; +extern const pm_char STR_MENUTELEMETRY[]; +extern const pm_char STR_MENUTEMPLATES[]; +extern const pm_char STR_MENUSTAT[]; +extern const pm_char STR_MENUDEBUG[]; +extern const pm_char STR_MENUGLOBALVARS[]; +extern const pm_char STR_INVERT_THR[]; +extern const pm_char STR_AND_SWITCH[]; +extern const pm_char STR_CF[]; + +#if defined(DSM2) || defined(PXX) +extern const pm_char STR_RXNUM[]; +#endif + +#if defined(PXX) +extern const pm_char STR_SYNCMENU[]; +extern const pm_char STR_INTERNALRF[]; +extern const pm_char STR_EXTERNALRF[]; +extern const pm_char STR_FAILSAFE[]; +extern const pm_char STR_FAILSAFESET[]; +extern const pm_char STR_COUNTRYCODE[]; +#endif + +#if defined(FRSKY) +extern const pm_char STR_LIMIT[]; +#endif + +#ifdef FRSKY_HUB +extern const pm_char STR_MINRSSI[]; +extern const pm_char STR_LATITUDE[]; +extern const pm_char STR_LONGITUDE[]; +#endif + +#if defined(CPUARM) || defined(CPUM2560) +extern const pm_char STR_SHUTDOWN[]; +#endif + +extern const pm_char STR_BATT_CALIB[]; + +#if defined(CPUARM) || defined(FRSKY) +extern const pm_char STR_VOLTAGE[]; +extern const pm_char STR_CURRENT[]; +#endif + +#if defined(CPUARM) + extern const pm_char STR_CURRENT_CALIB[]; + #define LEN_CALIB_FIELDS (PSIZE(TR_BATT_CALIB) > PSIZE(TR_CURRENT_CALIB) ? PSIZE(TR_BATT_CALIB) : PSIZE(TR_CURRENT_CALIB)) + extern const pm_char STR_UNITSSYSTEM[]; + extern const pm_char STR_VOICELANG[]; + extern const pm_char STR_MODELIDUSED[]; + extern const pm_char STR_BEEP_VOLUME[]; + extern const pm_char STR_WAV_VOLUME[]; + extern const pm_char STR_VARIO_VOLUME[]; + extern const pm_char STR_BG_VOLUME[]; + extern const pm_char STR_PERSISTENT_MAH[]; +#else + #define LEN_CALIB_FIELDS PSIZE(TR_BATT_CALIB) +#endif + +#if defined(NAVIGATION_MENUS) + extern const pm_char STR_SELECT_MODEL[]; + extern const pm_char STR_CREATE_MODEL[]; + extern const pm_char STR_COPY_MODEL[]; + extern const pm_char STR_MOVE_MODEL[]; + extern const pm_char STR_DELETE_MODEL[]; + extern const pm_char STR_EDIT[]; + extern const pm_char STR_INSERT_BEFORE[]; + extern const pm_char STR_INSERT_AFTER[]; + extern const pm_char STR_COPY[]; + extern const pm_char STR_MOVE[]; + extern const pm_char STR_PASTE[]; + extern const pm_char STR_INSERT[]; + extern const pm_char STR_DELETE[]; + extern const pm_char STR_RESET_FLIGHT[]; + extern const pm_char STR_RESET_TIMER1[]; + extern const pm_char STR_RESET_TIMER2[]; + extern const pm_char STR_RESET_TELEMETRY[]; + extern const pm_char STR_STATISTICS[]; + extern const pm_char STR_ABOUT_US[]; +#endif + +extern const pm_char STR_RESET_BTN[]; + +#if defined(SDCARD) + extern const pm_char STR_BACKUP_MODEL[]; + extern const pm_char STR_RESTORE_MODEL[]; + extern const pm_char STR_SDCARD_ERROR[]; + extern const pm_char STR_NO_SDCARD[]; + extern const pm_char STR_INCOMPATIBLE[]; + extern const pm_char STR_LOGS_PATH[]; + extern const pm_char STR_LOGS_EXT[]; + extern const pm_char STR_MODELS_PATH[]; + extern const pm_char STR_MODELS_EXT[]; + #define STR_UPDATE_LIST STR_DELAYDOWN +#endif + +extern const pm_uchar font_5x7[]; +extern const pm_uchar font_10x14[]; + +#if defined(CPUARM) +extern const pm_uchar font_3x5[]; +extern const pm_uchar font_4x6[]; +extern const pm_uchar font_8x10[]; +extern const pm_uchar font_5x7_extra[]; +extern const pm_uchar font_10x14_extra[]; +extern const pm_uchar font_4x6_extra[]; +#endif + +extern const pm_char STR_WARNING[]; +extern const pm_char STR_EEPROMWARN[]; +extern const pm_char STR_THROTTLEWARN[]; +extern const pm_char STR_ALARMSWARN[]; +extern const pm_char STR_SWITCHWARN[]; + +extern const pm_char STR_SPEAKER_VOLUME[]; +extern const pm_char STR_LCD[]; +extern const pm_char STR_BRIGHTNESS[]; +extern const pm_char STR_CPU_TEMP[]; +extern const pm_char STR_CPU_CURRENT[]; +extern const pm_char STR_CPU_MAH[]; +extern const pm_char STR_COPROC[]; +extern const pm_char STR_COPROC_TEMP[]; +extern const pm_char STR_CAPAWARNING[]; +extern const pm_char STR_TEMPWARNING[]; +extern const pm_char STR_FUNC[]; +extern const pm_char STR_V1[]; +extern const pm_char STR_V2[]; +extern const pm_char STR_DURATION[]; +extern const pm_char STR_DELAY[]; +extern const pm_char STR_SD_CARD[]; +extern const pm_char STR_SDHC_CARD[]; +extern const pm_char STR_NO_SOUNDS_ON_SD[]; +extern const pm_char STR_NO_MODELS_ON_SD[]; +extern const pm_char STR_NO_BITMAPS_ON_SD[]; +extern const pm_char STR_NO_SCRIPTS_ON_SD[]; +extern const pm_char STR_PLAY_FILE[]; +extern const pm_char STR_ASSIGN_BITMAP[]; +extern const pm_char STR_EXECUTE_FILE[]; +extern const pm_char STR_DELETE_FILE[]; +extern const pm_char STR_COPY_FILE[]; +extern const pm_char STR_RENAME_FILE[]; +extern const pm_char STR_REMOVED[]; +extern const pm_char STR_SD_INFO[]; +extern const pm_char STR_SD_FORMAT[]; +extern const pm_char STR_NA[]; +extern const pm_char STR_HARDWARE[]; +extern const pm_char STR_FORMATTING[]; +extern const pm_char STR_TEMP_CALIB[]; +extern const pm_char STR_TIME[]; +extern const pm_char STR_BAUDRATE[]; +extern const pm_char STR_SD_INFO_TITLE[]; +extern const pm_char STR_SD_TYPE[]; +extern const pm_char STR_SD_SPEED[]; +extern const pm_char STR_SD_SECTORS[]; +extern const pm_char STR_SD_SIZE[]; +extern const pm_char STR_TYPE[]; +extern const pm_char STR_GLOBAL_VARS[]; +extern const pm_char STR_GLOBAL_VAR[]; +extern const pm_char STR_OWN[]; +extern const pm_char STR_ROTARY_ENCODER[]; +extern const pm_char STR_DATE[]; +extern const pm_char STR_CHANNELS_MONITOR[]; +extern const pm_char STR_PATH_TOO_LONG[]; +extern const pm_char STR_VIEW_TEXT[]; + +#if defined(VOICE) && defined(CPUARM) + struct LanguagePack { + const char *id; + const char *name; + void (*playNumber)(getvalue_t number, uint8_t unit, uint8_t att, uint8_t id); + void (*playDuration)(int16_t seconds, uint8_t id); + }; + extern LanguagePack * languagePacks[]; + extern LanguagePack * currentLanguagePack; + extern uint8_t currentLanguagePackIdx; + #define LANGUAGE_PACK_DECLARE(lng, name) LanguagePack lng ## LanguagePack = { #lng, name, lng ## _ ## playNumber, lng ## _ ## playDuration } + #define LANGUAGE_PACK_DECLARE_DEFAULT(lng, name) LANGUAGE_PACK_DECLARE(lng, name); LanguagePack * currentLanguagePack = & lng ## LanguagePack; uint8_t currentLanguagePackIdx + inline PLAY_FUNCTION(playNumber, getvalue_t number, uint8_t unit, uint8_t att) { currentLanguagePack->playNumber(number, unit, att, id); } + inline PLAY_FUNCTION(playDuration, int16_t seconds) { currentLanguagePack->playDuration(seconds, id); } +#elif defined(VOICE) + PLAY_FUNCTION(playNumber, getvalue_t number, uint8_t unit, uint8_t att); + PLAY_FUNCTION(playDuration, int16_t seconds); + #define LANGUAGE_PACK_DECLARE(lng, name) + #define LANGUAGE_PACK_DECLARE_DEFAULT(lng, name) +#else + #define LANGUAGE_PACK_DECLARE(lng, name) + #define LANGUAGE_PACK_DECLARE_DEFAULT(lng, name) +#endif + +#if LCD_W >= 212 + extern const pm_char STR_MODELNAME[]; + extern const pm_char STR_PHASENAME[]; + extern const pm_char STR_MIXNAME[]; + extern const pm_char STR_EXPONAME[]; +#else + #define STR_MODELNAME STR_NAME + #define STR_PHASENAME STR_NAME + #define STR_MIXNAME STR_NAME + #define STR_EXPONAME STR_NAME +#endif + +#if LCD_W >= 212 + extern const char * STR_PHASES_HEADERS[]; + extern const char * STR_LIMITS_HEADERS[]; + extern const char * STR_CSW_HEADERS[]; +#endif + +#if defined(PCBTARANIS) + extern const pm_char STR_BYTES[]; + extern const pm_char STR_MODULE_BIND[]; + extern const pm_char STR_SET[]; + extern const pm_char STR_TRAINER[]; + extern const pm_char STR_ANTENNAPROBLEM[]; + extern const pm_char STR_MODULE[]; + extern const pm_char STR_CHANNELRANGE[]; + extern const pm_char STR_LOWALARM[]; + extern const pm_char STR_CRITICALALARM[]; + extern const pm_char STR_ENABLE_POPUP[]; + extern const pm_char STR_DISABLE_POPUP[]; + extern const pm_char STR_CURVE_PRESET[]; + extern const pm_char STR_PRESET[]; + extern const pm_char STR_MIRROR[]; + extern const pm_char STR_CLEAR[]; + extern const pm_char STR_RESET[]; + extern const pm_char STR_COPY_TRIMS_TO_OFFSET[]; + extern const pm_char STR_TOP_BAR[]; + extern const pm_char STR_ALTITUDE[]; + extern const pm_char STR_SCALE[]; + extern const pm_char STR_VIEW_CHANNELS[]; + extern const pm_char STR_VIEW_NOTES[]; +#endif + +#if MENUS_LOCK == 1 + extern const pm_char STR_UNLOCKED[]; + extern const pm_char STR_MODS_FORBIDDEN[]; +#endif + +#if defined(PCBTARANIS) || defined(DSM2) + extern const pm_char STR_MODULE_RANGE[]; +#endif + +#if defined(MAVLINK) + extern const pm_char STR_MAVLINK_RC_RSSI_SCALE_LABEL[]; + extern const pm_char STR_MAVLINK_PC_RSSI_EN_LABEL[]; + extern const pm_char STR_MAVMENUSETUP_TITLE[]; + extern const pm_char STR_MAVLINK_BAUD_LABEL[]; + extern const pm_char STR_MAVLINK_INFOS[]; + extern const pm_char STR_MAVLINK_MODE[]; + extern const pm_char STR_MAVLINK_CUR_MODE[]; + extern const pm_char STR_MAVLINK_ARMED[]; + extern const pm_char STR_MAVLINK_BAT_MENU_TITLE[]; + extern const pm_char STR_MAVLINK_BATTERY_LABEL[]; + extern const pm_char STR_MAVLINK_RC_RSSI_LABEL[]; + extern const pm_char STR_MAVLINK_PC_RSSI_LABEL[]; + extern const pm_char STR_MAVLINK_NAV_MENU_TITLE[]; + extern const pm_char STR_MAVLINK_COURSE[]; + extern const pm_char STR_MAVLINK_HEADING[]; + extern const pm_char STR_MAVLINK_BEARING[]; + extern const pm_char STR_MAVLINK_ALTITUDE[]; + extern const pm_char STR_MAVLINK_GPS[]; + extern const pm_char STR_MAVLINK_NO_FIX[]; + extern const pm_char STR_MAVLINK_SAT[]; + extern const pm_char STR_MAVLINK_HDOP[]; + extern const pm_char STR_MAVLINK_LAT[]; + extern const pm_char STR_MAVLINK_LON[]; +#endif + +#if !defined(CPUM64) + extern const pm_char STR_ABOUTUS[]; + extern const pm_char STR_ABOUT_OPENTX_1[]; + extern const pm_char STR_ABOUT_OPENTX_2[]; + extern const pm_char STR_ABOUT_OPENTX_3[]; + extern const pm_char STR_ABOUT_OPENTX_4[]; + extern const pm_char STR_ABOUT_OPENTX_5[]; + + extern const pm_char STR_ABOUT_BERTRAND_1[]; + extern const pm_char STR_ABOUT_BERTRAND_2[]; + extern const pm_char STR_ABOUT_BERTRAND_3[]; + + extern const pm_char STR_ABOUT_MIKE_1[]; + extern const pm_char STR_ABOUT_MIKE_2[]; + extern const pm_char STR_ABOUT_MIKE_3[]; + extern const pm_char STR_ABOUT_MIKE_4[]; + + extern const pm_char STR_ABOUT_ROMOLO_1[]; + extern const pm_char STR_ABOUT_ROMOLO_2[]; + extern const pm_char STR_ABOUT_ROMOLO_3[]; + + extern const pm_char STR_ABOUT_ANDRE_1[]; + extern const pm_char STR_ABOUT_ANDRE_2[]; + extern const pm_char STR_ABOUT_ANDRE_3[]; + + extern const pm_char STR_ABOUT_ROB_1[]; + extern const pm_char STR_ABOUT_ROB_2[]; + + extern const pm_char STR_ABOUT_MARTIN_1[]; + extern const pm_char STR_ABOUT_MARTIN_2[]; + + extern const pm_char STR_ABOUT_HARDWARE_1[]; + extern const pm_char STR_ABOUT_HARDWARE_2[]; + extern const pm_char STR_ABOUT_HARDWARE_3[]; + + extern const pm_char STR_ABOUT_PARENTS_1[]; + extern const pm_char STR_ABOUT_PARENTS_2[]; + extern const pm_char STR_ABOUT_PARENTS_3[]; + extern const pm_char STR_ABOUT_PARENTS_4[]; +#endif + +#define CHR_SHORT TR_CHR_SHORT +#define CHR_LONG TR_CHR_LONG +#define CHR_TOGGLE TR_CHR_TOGGLE +#define CHR_HOUR TR_CHR_HOUR + +#endif diff --git a/radio/src/translations/en.h b/radio/src/translations/en.h index 62876daf9..86c4f48cf 100644 --- a/radio/src/translations/en.h +++ b/radio/src/translations/en.h @@ -447,6 +447,8 @@ #define TR_ALARM INDENT"Alarm" #define TR_USRDATA "UsrData" #define TR_BLADES INDENT"Blades" +#define TR_SPURGEAR INDENT"Spur Gear" // T.Foley +#define TR_PINIONGEAR INDENT"Pinion Gear" // T.Foley #define TR_SCREEN "Screen " #define TR_SOUND_LABEL "Sound" #define TR_LENGTH INDENT"Length" diff --git a/radio/src/translations/en.h~ b/radio/src/translations/en.h~ new file mode 100644 index 000000000..62876daf9 --- /dev/null +++ b/radio/src/translations/en.h~ @@ -0,0 +1,765 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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. + * + */ + +// NON ZERO TERMINATED STRINGS +#define LEN_OFFON "\003" +#define TR_OFFON "OFF""ON\0" + +#define LEN_MMMINV "\003" +#define TR_MMMINV "---""INV" + +#define LEN_NCHANNELS "\004" +#define TR_NCHANNELS "\0014CH\0016CH\0018CH10CH12CH14CH16CH" + +#define LEN_VBEEPMODE "\005" +#define TR_VBEEPMODE "Quiet""Alarm""NoKey""All " + +#define LEN_VBEEPLEN "\005" +#define TR_VBEEPLEN "0====""=0===""==0==""===0=""====0" + +#define LEN_VRENAVIG "\003" +#define TR_VRENAVIG "No REaREb" + +#define LEN_VBLMODE TR("\004", "\010") +#define TR_VBLMODE TR("OFF\0""Keys""Ctrl""Both""ON\0 ", "OFF\0 ""Keys\0 ""Controls""Both\0 ""ON\0 ") + +#define LEN_TRNMODE "\003" +#define TR_TRNMODE "OFF"" +="" :=" + +#define LEN_TRNCHN "\003" +#define TR_TRNCHN "CH1CH2CH3CH4" + +#define LEN_DATETIME "\005" +#define TR_DATETIME "DATE:""TIME:" + +#define LEN_VLCD "\006" +#define TR_VLCD "NormalOptrex" + +#define LEN_COUNTRYCODES TR("\002", "\007") +#define TR_COUNTRYCODES TR("US""JP""EU", "America""Japan\0 ""Europe\0") + +#define LEN_VTRIMINC TR("\006","\013") +#define TR_VTRIMINC TR("Expo ""ExFine""Fine ""Medium""Coarse","Exponential""Extra Fine ""Fine ""Medium ""Coarse ") + +#define LEN_VBEEPCOUNTDOWN "\006" +#define TR_VBEEPCOUNTDOWN "SilentBeeps\0Voice\0" + +#define LEN_RETA123 "\001" + +#if defined(PCBGRUVIN9X) + #if ROTARY_ENCODERS > 2 + #define TR_RETA123 "RETA123abcd" + #else + #define TR_RETA123 "RETA123ab" + #endif +#elif defined(PCBTARANIS) + #define TR_RETA123 "RETA12LR" +#else + #define TR_RETA123 "RETA123" +#endif + +#define LEN_VPROTOS "\006" + +#if defined(PXX) + #define TR_PXX "PXX\0 " +#elif defined(DSM2) || defined(IRPROTOS) + #define TR_PXX "[PXX]\0" +#else + #define TR_PXX +#endif + +#if defined(DSM2) + #define TR_DSM2 "LP45\0 ""DSM2\0 ""DSMX\0 " +#elif defined(IRPROTOS) + #define TR_DSM2 "[LP45]""[DSM2]""[DSMX]" +#else + #define TR_DSM2 +#endif + +#if defined(IRPROTOS) + #define TR_IRPROTOS "SILV TRAC09PICZ SWIFT\0" +#else + #define TR_IRPROTOS +#endif + +#if defined(CPUARM) + #define TR_XPPM +#else + #define TR_XPPM "PPM16\0""PPMsim" +#endif + +#define TR_VPROTOS "PPM\0 " TR_XPPM TR_PXX TR_DSM2 TR_IRPROTOS + +#define LEN_POSNEG "\003" +#define TR_POSNEG "POS""NEG" + +#define LEN_VCURVEFUNC "\003" +#define TR_VCURVEFUNC "---""x>0""x<0""|x|""f>0""f<0""|f|" + +#define LEN_VMLTPX "\010" +#define TR_VMLTPX "Add\0 ""Multiply""Replace\0" + +#define LEN_VMLTPX2 "\002" +#define TR_VMLTPX2 "+=""*="":=" + +#define LEN_VMIXTRIMS "\003" +#define TR_VMIXTRIMS "OFF""ON\0""Rud""Ele""Thr""Ail" + +#define LEN_VCSWFUNC "\005" +#define TR_VCSWFUNC "---\0 ""a{x\0 ""a>x\0 ""ax""|a|b\0 ""a" + +#define LEN_VTELPROTO "\007" +#define TR_VTELPROTO "None\0 ""Hub\0 ""WSHHigh" + +#define LEN_VOLTSRC "\003" +#define TR_VOLTSRC "---""A1\0""A2\0""FAS""Cel" + +#define LEN_VARIOSRC "\005" +#if defined(FRSKY_SPORT) + #define TR_VARIOSRC "Vario""A1\0 ""A2\0" +#else + #define TR_VARIOSRC "Alti\0""Alti+""Vario""A1\0 ""A2\0" +#endif + +#define LEN_VSCREEN "\004" +#define TR_VSCREEN "Nums""Bars" + +#define LEN_GPSFORMAT "\004" +#define TR_GPSFORMAT "HMS NMEA" + +#define LEN2_VTEMPLATES 13 +#define LEN_VTEMPLATES "\015" +#define TR_VTEMPLATES "Clear Mixes\0\0""Simple 4-CH \0""Sticky-T-Cut\0""V-Tail \0""Elevon\\Delta\0""eCCPM \0""Heli Setup \0""Servo Test \0" + +#define LEN_VSWASHTYPE "\004" +#define TR_VSWASHTYPE "---\0""120\0""120X""140\0""90\0" + +#define LEN_VKEYS "\005" +#define TR_VKEYS TR(" Menu"" Exit"" Down"" Up""Right"" Left", " Menu"" Exit""Enter"" Page"" Plus""Minus") + +#define LEN_VRENCODERS "\003" +#define TR_VRENCODERS "REa""REb" + +#define LEN_VSWITCHES "\003" +#define LEN_VSRCRAW "\004" + +#if defined(PCBTARANIS) + #define TR_POTS_VSRCRAW "S1\0 ""S2\0 ""LS\0 ""RS\0 " + #define TR_SW_VSRCRAW "SA\0 ""SB\0 ""SC\0 ""SD\0 ""SE\0 ""SF\0 ""SG\0 ""SH\0 " +#elif defined(EXTRA_3POS) + #define TR_POTS_VSRCRAW "P1\0 ""P2\0 " + #define TR_SW_VSRCRAW "3P1\0""3P2\0" + #define TR_9X_3POS_SWITCHES "ID0""ID1""ID2""ID3""ID4""ID5" +#else + #define TR_POTS_VSRCRAW "P1\0 ""P2\0 ""P3\0 " + #define TR_SW_VSRCRAW "3POS" + #define TR_9X_3POS_SWITCHES "ID0""ID1""ID2" +#endif + +#if defined(CPUARM) + #define TR_CUSTOMSW "CS1""CS2""CS3""CS4""CS5""CS6""CS7""CS8""CS9""CSA""CSB""CSC""CSD""CSE""CSF""CSG""CSH""CSI""CSJ""CSK""CSL""CSM""CSN""CSO""CSP""CSQ""CSR""CSS""CST""CSU""CSV""CSW" +#elif defined(PCBGRUVIN9X) || defined(CPUM2561) || defined(CPUM128) + #define TR_CUSTOMSW "CS1""CS2""CS3""CS4""CS5""CS6""CS7""CS8""CS9""CSA""CSB""CSC""CSD""CSE""CSF" +#else + #define TR_CUSTOMSW "CS1""CS2""CS3""CS4""CS5""CS6""CS7""CS8""CS9""CSA""CSB""CSC" +#endif + +#if defined(PCBTARANIS) + #define TR_VSWITCHES "SA\300""SA-""SA\301""SB\300""SB-""SB\301""SC\300""SC-""SC\301""SD\300""SD-""SD\301""SE\300""SE-""SE\301""SF\300""SF\301""SG\300""SG-""SG\301""SH\300""SH\301" TR_CUSTOMSW "One" +#else + #define TR_VSWITCHES TR_9X_3POS_SWITCHES "THR""RUD""ELE""AIL""GEA""TRN" TR_CUSTOMSW "One" +#endif + +#if defined(PCBSKY9X) + #define TR_ROTARY_ENCODERS_VSRCRAW "REnc" +#elif defined(PCBGRUVIN9X) && ROTARY_ENCODERS > 2 + #define TR_ROTARY_ENCODERS_VSRCRAW "REa ""REb ""REc ""REd " +#elif defined(PCBGRUVIN9X) && ROTARY_ENCODERS <= 2 + #define TR_ROTARY_ENCODERS_VSRCRAW "REa ""REb " +#else + #define TR_ROTARY_ENCODERS_VSRCRAW +#endif + +#if defined(HELI) + #define TR_CYC_VSRCRAW "CYC1""CYC2""CYC3" +#else + #define TR_CYC_VSRCRAW "[C1]""[C2]""[C3]" +#endif + +#define TR_VSRCRAW "---\0""Rud\0""Ele\0""Thr\0""Ail\0" TR_POTS_VSRCRAW TR_ROTARY_ENCODERS_VSRCRAW "MAX\0" TR_CYC_VSRCRAW "TrmR" "TrmE" "TrmT" "TrmA" TR_SW_VSRCRAW + +#define LEN_VTMRMODES "\003" +#define TR_VTMRMODES "OFF""ABS""THs""TH%""THt" + +#define LEN_VTRAINERMODES "\006" +#define TR_VTRAINERMODES "Master""Slave\0" + +#define LEN_VFAILSAFE "\011" +#define TR_VFAILSAFE "Hold\0 ""Custom\0 ""No pulses" + +#if defined(MAVLINK) + #define LEN_MAVLINK_BAUDS "\006" + #define TR_MAVLINK_BAUDS "4800 ""9600 ""14400 ""19200 ""38400 ""57600 ""76800 ""115200" + #define LEN_MAVLINK_AC_MODES "\011" + #define TR_MAVLINK_AC_MODES "Stabilize""Acro ""Alt Hold ""Auto ""Guided ""Loiter ""RTL ""Circle ""Pos Hold ""Land ""OF Loiter""Toy A ""Toy M ""INVALID " + #define LEN_MAVLINK_AP_MODES "\015" + #define TR_MAVLINK_AP_MODES "Manual ""Circle ""Stabilize ""Training ""Fly by Wire A""Fly by Wire A""Auto ""RTL ""Loiter ""Guided ""Initialising ""INVALID " +#endif + +// ZERO TERMINATED STRINGS +#define INDENT "\001" +#define LEN_INDENT 1 +#define INDENT_WIDTH (FW/2) + +#if defined(PCBTARANIS) + #define TR_ENTER "[ENTER]" +#else + #define TR_ENTER "[MENU]" +#endif + +#define TR_POPUPS TR_ENTER "\010[EXIT]" +#define OFS_EXIT sizeof(TR_ENTER) + +#define TR_MENUWHENDONE CENTER "\006" TR_ENTER " WHEN DONE" +#define TR_FREE "free" +#define TR_DELETEMODEL "DELETE MODEL" +#define TR_COPYINGMODEL "Copying model..." +#define TR_MOVINGMODEL "Moving model..." +#define TR_LOADINGMODEL "Loading model..." +#define TR_NAME "Name" +#define TR_MODELNAME "Model Name" +#define TR_PHASENAME "Mode Name" +#define TR_MIXNAME "Mix Name" +#if defined(PCBTARANIS) + #define TR_EXPONAME "Line Name" +#else + #define TR_EXPONAME "Expo Name" +#endif +#define TR_BITMAP "Model Image" +#define TR_TIMER TR("Timer","Timer ") +#define TR_ELIMITS TR("E.Limits","Extended Limits") +#define TR_ETRIMS TR("E.Trims","Extended Trims") +#define TR_TRIMINC "Trim Step" +#define TR_TTRACE TR("T-Source","Throttle Source") +#define TR_TTRIM TR("T-Trim","Throttle Trim") +#define TR_BEEPCTR TR("Ctr Beep","Center Beep") +#define TR_PROTO TR(INDENT "Proto", INDENT "Protocol") +#define TR_PPMFRAME TR("PPM frame", INDENT "PPM frame") +#define TR_MS "ms" +#define TR_SWITCH "Switch" +#define TR_TRIMS "Trims" +#define TR_FADEIN "Fade In" +#define TR_FADEOUT "Fade Out" +#define TR_DEFAULT "(default)" +#define TR_CHECKTRIMS CENTER "\006Check\012Trims" +#define OFS_CHECKTRIMS CENTER_OFS+(9*FW) +#define TR_SWASHTYPE "Swash Type" +#define TR_COLLECTIVE TR("Collective","Collective source") +#define TR_SWASHRING "Swash Ring" +#define TR_ELEDIRECTION TR("ELE Direction","Long. cyc. direction") +#define TR_AILDIRECTION TR("AIL Direction","Lateral cyc. direction") +#define TR_COLDIRECTION TR("PIT Direction","Coll. pitch direction") +#define TR_MODE INDENT"Mode" +#define TR_NOFREEEXPO "No free expo!" +#define TR_NOFREEMIXER "No free mixer!" +#define TR_INSERTMIX "INSERT MIX " +#define TR_EDITMIX "EDIT MIX " +#define TR_SOURCE INDENT"Source" +#define TR_WEIGHT "Weight" +#define TR_EXPO TR("Expo","Exponential") +#define TR_SIDE "Side" +#define TR_DIFFERENTIAL "Differ" +#define TR_OFFSET INDENT"Offset" +#define TR_TRIM "Trim" +#define TR_DREX "DRex" +#define TR_CURVE "Curve" +#define TR_FLMODE TR("Mode","Modes") +#define TR_MIXWARNING "Warning" +#define TR_OFF "OFF" +#define TR_MULTPX "Multpx" +#define TR_DELAYDOWN "Delay Dn" +#define TR_DELAYUP "Delay Up" +#define TR_SLOWDOWN "Slow Dn" +#define TR_SLOWUP "Slow Up" +#define TR_MIXER "MIXER" +#define TR_CV "CV" +#define TR_GV "GV" +#define TR_ACHANNEL "A\004channel" +#define TR_RANGE INDENT"Range" +#define TR_BAR "Bar" +#define TR_ALARM INDENT"Alarm" +#define TR_USRDATA "UsrData" +#define TR_BLADES INDENT"Blades" +#define TR_SCREEN "Screen " +#define TR_SOUND_LABEL "Sound" +#define TR_LENGTH INDENT"Length" +#define TR_SPKRPITCH INDENT"Pitch" +#define TR_HAPTIC_LABEL "Haptic" +#define TR_HAPTICSTRENGTH INDENT"Strength" +#define TR_CONTRAST "Contrast" +#define TR_ALARMS_LABEL "Alarms" +#define TR_BATTERY_RANGE TR("Battery range","Battery meter range") +#define TR_BATTERYWARNING INDENT"Battery Low" +#define TR_INACTIVITYALARM INDENT"Inactivity" +#define TR_MEMORYWARNING INDENT"Memory Low" +#define TR_ALARMWARNING INDENT"Sound Off" +#define TR_RENAVIG "RotEnc Navig" +#define TR_THROTTLEREVERSE TR("T-Reverse", "Throttle reverse") +#define TR_MINUTEBEEP TR(INDENT"Minute",INDENT"Minute call") +#define TR_BEEPCOUNTDOWN INDENT"Countdown" +#define TR_PERSISTENT TR(INDENT"Persist.",INDENT"Persistent") +#define TR_BACKLIGHT_LABEL "Backlight" +#define TR_BLDELAY INDENT"Duration" +#define TR_BLONBRIGHTNESS INDENT"ON Brightness" +#define TR_BLOFFBRIGHTNESS INDENT"OFF Brightness" +#define TR_SPLASHSCREEN "Splash screen" +#define TR_THROTTLEWARNING TR("T-Warning","Throttle Warning") +#define TR_SWITCHWARNING TR("S-Warning","Switch Warning") +#define TR_TIMEZONE TR("Time Zone","GPS Time zone") +#define TR_RXCHANNELORD TR("Rx Channel Ord","Default channel order") +#define TR_SLAVE "Slave" +#define TR_MODESRC "Mode\006% Source" +#define TR_MULTIPLIER "Multiplier" +#define TR_CAL "Cal" +#define TR_VTRIM "Trim- +" +#define TR_BG "BG:" +#define TR_MENUTOSTART CENTER "\006" TR_ENTER " TO START" +#define TR_SETMIDPOINT TR(CENTER "\003SET STICKS MIDPOINT",CENTER "\003CENTER STICKS/SLIDERS") +#define TR_MOVESTICKSPOTS CENTER "\006MOVE STICKS/POTS" +#define TR_RXBATT "Rx Batt:" +#define TR_TXnRX "Tx:\0Rx:" +#define OFS_RX 4 +#define TR_ACCEL "Acc:" +#define TR_NODATA CENTER "NO DATA" +#define TR_TM1TM2 "TM1\032TM2" +#define TR_THRTHP "THR\032TH%" +#define TR_TOT "TOT" +#define TR_TMR1LATMAXUS "Tmr1Lat max\006us" +#define STR_US (STR_TMR1LATMAXUS+12) +#define TR_TMR1LATMINUS "Tmr1Lat min\006us" +#define TR_TMR1JITTERUS "Tmr1 Jitter\006us" + +#if defined(CPUARM) + #define TR_TMIXMAXMS "Tmix max" + #define TR_FREESTACKMINB "Free Stack" +#else + #define TR_TMIXMAXMS "Tmix max\014ms" + #define TR_FREESTACKMINB "Free Stack\010b" +#endif + +#define TR_MENUTORESET CENTER TR_ENTER " to reset" +#define TR_PPM "PPM" +#define TR_CH "CH" +#define TR_MODEL "MODEL" +#define TR_FP "FM" +#define TR_MIX "MIX" +#define TR_EEPROMLOWMEM "EEPROM low mem" +#define TR_ALERT "\016ALERT" +#define TR_PRESSANYKEYTOSKIP "Press any key to skip" +#define TR_THROTTLENOTIDLE "Throttle not idle" +#define TR_ALARMSDISABLED "Alarms Disabled" +#define TR_PRESSANYKEY TR("\010Press any Key", "Press any Key") +#define TR_BADEEPROMDATA "Bad EEprom Data" +#define TR_EEPROMFORMATTING "Formatting EEPROM" +#define TR_EEPROMOVERFLOW "EEPROM overflow" +#define TR_MENURADIOSETUP "RADIO SETUP" +#define TR_MENUDATEANDTIME "DATE AND TIME" +#define TR_MENUTRAINER "TRAINER" +#define TR_MENUVERSION "VERSION" +#define TR_MENUDIAG TR("SWITCHES","SWITCH TEST") +#define TR_MENUANA TR("ANAS","ANALOG INPUTS") +#define TR_MENUCALIBRATION "CALIBRATION" +#define TR_TRIMS2OFFSETS "\006Trims => Offsets" +#define TR_MENUMODELSEL TR("MODELSEL","MODEL SELECTION") +#define TR_MENUSETUP TR("SETUP","MODEL SETUP") +#define TR_MENUFLIGHTPHASE "FLIGHT MODE" +#define TR_MENUFLIGHTPHASES "FLIGHT MODES" +#define TR_MENUHELISETUP "HELI SETUP" + +#if defined(PCBTARANIS) + #define TR_MENUINPUTS "INPUTS" + #define TR_MENULIMITS "SERVOS" +#elif defined(PPM_CENTER_ADJUSTABLE) || defined(PPM_LIMITS_SYMETRICAL) // The right menu titles for the gurus ... + #define TR_MENUINPUTS "STICKS" + #define TR_MENULIMITS "SERVOS" +#else + #define TR_MENUINPUTS "DR/EXPO" + #define TR_MENULIMITS "LIMITS" +#endif + +#define TR_MENUCURVES "CURVES" +#define TR_MENUCURVE "CURVE" +#define TR_MENUCUSTOMSWITCH "CUSTOM SWITCH" +#define TR_MENUCUSTOMSWITCHES "CUSTOM SWITCHES" +#define TR_MENUCUSTOMFUNC "CUSTOM FUNCTIONS" +#define TR_MENUTELEMETRY "TELEMETRY" +#define TR_MENUTEMPLATES "TEMPLATES" +#define TR_MENUSTAT "STATS" +#define TR_MENUDEBUG "DEBUG" +#define TR_RXNUM TR("RxNum", INDENT"Receiver No.") +#define TR_SYNCMENU "[Sync]" +#define TR_LIMIT INDENT"Limit" +#define TR_MINRSSI "Min Rssi" +#define TR_LATITUDE "Latitude" +#define TR_LONGITUDE "Longitude" +#define TR_GPSCOORD TR("GPS Coords", "GPS coordinate format") +#define TR_VARIO TR("Vario", "Variometer") +#define TR_SHUTDOWN "SHUTTING DOWN" +#define TR_BATT_CALIB "Battery Calib" +#define TR_CURRENT_CALIB "Current Calib" +#define TR_VOLTAGE INDENT"Voltage" +#define TR_CURRENT INDENT"Current" +#define TR_SELECT_MODEL "Select Model" +#define TR_CREATE_MODEL "Create Model" +#define TR_COPY_MODEL "Copy Model" +#define TR_MOVE_MODEL "Move Model" +#define TR_BACKUP_MODEL "Backup Model" +#define TR_DELETE_MODEL "Delete Model" +#define TR_RESTORE_MODEL "Restore Model" +#define TR_SDCARD_ERROR "SDCARD Error" +#define TR_NO_SDCARD "No SDCARD" +#define TR_INCOMPATIBLE "Incompatible" +#define TR_WARNING "WARNING" +#define TR_EEPROMWARN "EEPROM" +#define TR_THROTTLEWARN "THROTTLE" +#define TR_ALARMSWARN "ALARMS" +#define TR_SWITCHWARN "SWITCH" +#define TR_INVERT_THR TR("Invert Thr?","Invert Throttle?") +#define TR_SPEAKER_VOLUME INDENT "Volume" +#define TR_LCD "LCD" +#define TR_BRIGHTNESS INDENT "Brightness" +#define TR_CPU_TEMP "CPU Temp.\016>" +#define TR_CPU_CURRENT "Current\022>" +#define TR_CPU_MAH "Consumpt." +#define TR_COPROC "CoProc." +#define TR_COPROC_TEMP "MB Temp. \016>" +#define TR_CAPAWARNING INDENT "Capacity Low" +#define TR_TEMPWARNING INDENT "Overheat" +#define TR_FUNC "Func" +#define TR_V1 "V1" +#define TR_V2 "V2" +#define TR_DURATION "Duration" +#define TR_DELAY "Delay" +#define TR_SD_CARD "SD CARD" +#define TR_SDHC_CARD "SD-HC CARD" +#define TR_NO_SOUNDS_ON_SD "No Sounds on SD" +#define TR_NO_MODELS_ON_SD "No Models on SD" +#define TR_NO_BITMAPS_ON_SD "No Bitmaps on SD" +#define TR_PLAY_FILE "Play" +#define TR_DELETE_FILE "Delete" +#define TR_COPY_FILE "Copy" +#define TR_RENAME_FILE "Rename" +#define TR_ASSIGN_BITMAP "Assign Bitmap" +#define TR_REMOVED " removed" +#define TR_SD_INFO "Information" +#define TR_SD_FORMAT "Format" +#define TR_NA "N/A" +#define TR_HARDWARE "HARDWARE" +#define TR_FORMATTING "Formatting..." +#define TR_TEMP_CALIB "Temp. Calib" +#define TR_TIME "Time" +#define TR_BAUDRATE "BT Baudrate" +#define TR_SD_INFO_TITLE "SD INFO" +#define TR_SD_TYPE "Type:" +#define TR_SD_SPEED "Speed:" +#define TR_SD_SECTORS "Sectors:" +#define TR_SD_SIZE "Size:" +#define TR_TYPE "Type" +#define TR_GLOBAL_VARS "Global Variables" +#define TR_GLOBAL_VAR "Global Variable" +#define TR_MENUGLOBALVARS "GLOBAL VARIABLES" +#define TR_OWN "Own" +#define TR_DATE "Date" +#define TR_ROTARY_ENCODER "R.Encs" +#define TR_CHANNELS_MONITOR "CHANNEL MONITOR" +#define TR_INTERNALRF "Internal RF" +#define TR_EXTERNALRF "External RF" +#define TR_FAILSAFE INDENT "Failsafe mode" +#define TR_FAILSAFESET "FAILSAFE SETTINGS" +#define TR_COUNTRYCODE "Country Code" +#define TR_VOICELANG "Voice Language" +#define TR_UNITSSYSTEM "Units" +#define TR_EDIT "Edit" +#define TR_INSERT_BEFORE "Insert Before" +#define TR_INSERT_AFTER "Insert After" +#define TR_COPY "Copy" +#define TR_MOVE "Move" +#define TR_PASTE "Paste" +#define TR_DELETE "Delete" +#define TR_INSERT "Insert" +#define TR_RESET_FLIGHT "Reset Flight" +#define TR_RESET_TIMER1 "Reset Timer1" +#define TR_RESET_TIMER2 "Reset Timer2" +#define TR_RESET_TELEMETRY "Reset Telemetry" +#define TR_STATISTICS "Statistics" +#define TR_ABOUT_US "About" +#define TR_AND_SWITCH "AND Switch" +#define TR_CF "CF" +#define TR_SPEAKER INDENT"Speaker" +#define TR_BUZZER INDENT"Buzzer" +#define TR_BYTES "bytes" +#define TR_MODULE_BIND "[Bind]" +#define TR_MODULE_RANGE "[Range]" +#define TR_RESET_BTN "[Reset]" +#define TR_SET "[Set]" +#define TR_TRAINER "Trainer" +#define TR_ANTENNAPROBLEM CENTER "TX Antenna problem!" +#define TR_MODELIDUSED TR("ID already used","Model ID already used") +#define TR_MODULE INDENT "Module" +#define TR_CHANNELRANGE INDENT "Channel Range" +#define TR_LOWALARM INDENT "Low Alarm" +#define TR_CRITICALALARM INDENT "Critical Alarm" +#define TR_PERSISTENT_MAH INDENT "Store mAh" + +#if defined(MAVLINK) + #define TR_MAVLINK_RC_RSSI_SCALE_LABEL "Max RSSI" + #define TR_MAVLINK_PC_RSSI_EN_LABEL "PC RSSI EN" + #define TR_MAVMENUSETUP_TITLE "Mavlink Setup" + #define TR_MAVLINK_BAUD_LABEL "Baudrate" + #define TR_MAVLINK_INFOS "INFOS" + #define TR_MAVLINK_MODE "MODE" + #define TR_MAVLINK_CUR_MODE "Current Mode" + #define TR_MAVLINK_ARMED "Armed" + #define TR_MAVLINK_BAT_MENU_TITLE "BAT RSSI" + #define TR_MAVLINK_BATTERY_LABEL "Flight Battery status" + #define TR_MAVLINK_RC_RSSI_LABEL "RC RSSI" + #define TR_MAVLINK_PC_RSSI_LABEL "PC RSSI" + #define TR_MAVLINK_NAV_MENU_TITLE "NAV" + #define TR_MAVLINK_COURSE "Course" + #define TR_MAVLINK_HEADING "Heading" + #define TR_MAVLINK_BEARING "Bearing" + #define TR_MAVLINK_ALTITUDE "Altitude" + #define TR_MAVLINK_GPS "GPS" + #define TR_MAVLINK_NO_FIX "NO Fix" + #define TR_MAVLINK_SAT "SAT" + #define TR_MAVLINK_HDOP "HDOP" + #define TR_MAVLINK_LAT "LAT" + #define TR_MAVLINK_LON "LON" +#endif + + + +// Taranis column headers +#define TR_PHASES_HEADERS { " Name ", " Switch ", " Trims ", " Fade In ", " Fade Out " } +#define TR_LIMITS_HEADERS { " Name ", " Subtrim ", " Min ", " Max ", " Direction ", " Curve ", " PPM Center ", " Subtrim mode " } +#define TR_CSW_HEADERS { " Function ", " V1 ", " V2 ", " AND Switch ", " Duration ", " Delay " } + +// About screen +#define TR_ABOUTUS TR(" ABOUT ", "ABOUT") + +#define TR_ABOUT_OPENTX_1 TR("OpenTX\001is\001open\001source,\001non", "OpenTX is open source, non-") +#define TR_ABOUT_OPENTX_2 TR("commercial,\001wo\001warranties.", "commercial and comes with no") +#define TR_ABOUT_OPENTX_3 TR("It\001was\001developed\001for\001free.", "warranties. It was developed") +#define TR_ABOUT_OPENTX_4 TR("Support through donations", "for free. Support through") +#define TR_ABOUT_OPENTX_5 TR("is welcome!", "donations is welcome!") + +#define TR_ABOUT_BERTRAND_1 "Bertrand Songis" +#define TR_ABOUT_BERTRAND_2 "OpenTX main author" +#define TR_ABOUT_BERTRAND_3 "Companion9x co-author" + +#define TR_ABOUT_MIKE_1 "Mike Blandford" +#define TR_ABOUT_MIKE_2 "Code and drivers guru" +#define TR_ABOUT_MIKE_3 TR("Arguably,\001one\001of\001the\001best", "Arguably, one of the best") +#define TR_ABOUT_MIKE_4 "Inspirational" + +#define TR_ABOUT_ROMOLO_1 "Romolo Manfredini" +#define TR_ABOUT_ROMOLO_2 "Companion9x co-author" +#define TR_ABOUT_ROMOLO_3 "" + +#define TR_ABOUT_ANDRE_1 "Andre Bernet" +#define TR_ABOUT_ANDRE_2 "Functionality, usability," +#define TR_ABOUT_ANDRE_3 "debugging, documentation" + +#define TR_ABOUT_ROB_1 "Rob Thomson" +#define TR_ABOUT_ROB_2 "openrcforums webmaster" + +#define TR_ABOUT_MARTIN_1 "Martin Hotar" +#define TR_ABOUT_MARTIN_2 "Graphics designer" + +#if defined(PCBTARANIS) + #define TR_ABOUT_HARDWARE_1 "FrSky" + #define TR_ABOUT_HARDWARE_2 "Hardware designer/producer" + #define TR_ABOUT_HARDWARE_3 "Firmware contributor" +#else + #define TR_ABOUT_HARDWARE_1 "Brent Nelson" + #define TR_ABOUT_HARDWARE_2 "Sky9x designer/producer" + #define TR_ABOUT_HARDWARE_3 "" +#endif + +#define TR_ABOUT_PARENTS_1 "Parent projects" +#define TR_ABOUT_PARENTS_2 TR("Ersky9x (Mike Blandford)", "Ersky9x (Mike Blandford)") +#define TR_ABOUT_PARENTS_3 "ER9X (Erez Raviv)" +#define TR_ABOUT_PARENTS_4 "TH9X (Thomas Husterer)" + +#define TR_CHR_SHORT 's' +#define TR_CHR_LONG 'l' +#define TR_CHR_TOGGLE 't' +#define TR_CHR_HOUR 'h' + +#define TR_BEEP_VOLUME "Beep Volume" +#define TR_WAV_VOLUME "Wav Volume" +#define TR_VARIO_VOLUME "Vario Volume" +#define TR_BG_VOLUME "Bg Volume" + +#define TR_TOP_BAR "Top Bar" +#define TR_ALTITUDE INDENT "Altitude" +#define TR_MODS_FORBIDDEN "Modifications forbidden!" +#define TR_UNLOCKED "Unlocked" +