/* * 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_LogicalSwitches, 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 menuModelLogicalSwitches(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 menuModelLogicalSwitches, 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_LONG(KEY_ENTER) && s_editMode) { s_noHi = NO_HI_LEN; 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 && !s_noHi) { 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 uint8_t mode = 0; if (active) { if (s_editMode <= 0) mode = INVERS+FIXEDWIDTH; else mode = FIXEDWIDTH; } lcd_putsnAtt(x, y, name, size, ZCHAR | mode); 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+FIXEDWIDTH); } 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, #if defined(PCBTARANIS) ITEM_MODEL_CHECKLIST_DISPLAY, #endif ITEM_MODEL_SWITCHES_WARNING, CASE_PCBTARANIS(ITEM_MODEL_POT_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) #define POT_WARN_ITEMS() ((g_model.nPotsToWarn >> 6) ? (uint8_t)NUM_POTS : (uint8_t)0) 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, CASE_PCBTARANIS(0) 7, POT_WARN_ITEMS(), 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, 5, 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, 6, NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2, CASE_PCBSKY9X(1) CASE_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); putsTimerMode(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, SWSRC_FIRST, TMR_VAROFS+SWSRC_LAST-1/*--- which is OFF*/); 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 && !s_noHi ? attr : 0); if (attr && m_posHorz>0) { s_editMode = 0; if (event==EVT_KEY_LONG(KEY_ENTER)) { s_noHi = NO_HI_LEN; 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; #if defined(PCBTARANIS) case ITEM_MODEL_CHECKLIST_DISPLAY: g_model.displayText = onoffMenuItem(g_model.displayText, MODEL_SETUP_2ND_COLUMN, y, STR_CHECKLIST, attr, event); break; #endif case ITEM_MODEL_SWITCHES_WARNING: { lcd_putsLeft(y, STR_SWITCHWARNING); swstate_t states = g_model.switchWarningStates; char c; if (attr) { s_editMode = 0; if (!READ_ONLY()) { switch(event) { CASE_EVT_ROTARY_BREAK case EVT_KEY_BREAK(KEY_ENTER): #if defined(CPUM64) g_model.nSwToWarn ^= (1 << m_posHorz); eeDirty(EE_MODEL); #else if (m_posHorz < NUM_SWITCHES-1) { g_model.nSwToWarn ^= (1 << m_posHorz); eeDirty(EE_MODEL); } #endif break; case EVT_KEY_LONG(KEY_ENTER): #if defined(CPUM64) getMovedSwitch(); g_model.switchWarningStates = switches_states; AUDIO_WARNING1(); eeDirty(EE_MODEL); #else if (m_posHorz == NUM_SWITCHES-1) { s_noHi = NO_HI_LEN; getMovedSwitch(); g_model.switchWarningStates = switches_states; AUDIO_WARNING1(); eeDirty(EE_MODEL); } #endif killEvents(event); break; } } } LcdFlags line = attr; for (uint8_t i=0; i>= 2; #else attr = 0; if (IS_3POS(i)) { c = '0'+(states & 0x03); states >>= 2; } else { if ((states & 0x01) && swactive) attr = INVERS; c = pgm_read_byte(STR_VSWITCHES - 2 + 9 + (3*i)); states >>= 1; } if (line && (m_posHorz == i)) { attr = BLINK; if (swactive) attr |= INVERS; } lcd_putcAtt(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive || (attr & BLINK)) ? c : '-', attr); #if !defined(CPUM64) lcd_putsAtt(MODEL_SETUP_2ND_COLUMN+(NUM_SWITCHES*FW), y, PSTR("<]"), (m_posHorz == NUM_SWITCHES-1 && !s_noHi) ? line : 0); #endif #endif } break; } #if defined(PCBTARANIS) case ITEM_MODEL_POT_WARNING: { lcd_putsLeft(y, STR_POTWARNING); uint8_t potMode = g_model.nPotsToWarn >> 6; if(attr) { if (m_posHorz) s_editMode = 0; if (!READ_ONLY() && m_posHorz) { switch(event) { case EVT_KEY_LONG(KEY_ENTER): killEvents(event); if(potMode == 1) { g_model.potPosition[m_posHorz-1] = getValue(MIXSRC_FIRST_POT+m_posHorz-1) >> 3; AUDIO_WARNING1(); eeDirty(EE_MODEL); } break; case EVT_KEY_BREAK(KEY_ENTER): g_model.nPotsToWarn ^= (1 << (m_posHorz-1)); eeDirty(EE_MODEL); break; } } } lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, PSTR("\004""Off\0""Man\0""Auto"), potMode, attr & ((m_posHorz == 0) ? attr : !INVERS)); if (potMode) 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< 0) { posHorz += 5; } 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-1, y, p->name, sizeof(p->name), event, attr); break; case ITEM_PHASES_SWITCH: if (k == 0) { lcd_puts((5+LEN_FP_NAME)*FW, y, STR_DEFAULT); } else { putsSwitches((5+LEN_FP_NAME)*FW+FW/2, y, p->swtch, attr); if (active) CHECK_INCDEC_MODELSWITCH(event, p->swtch, SWSRC_FIRST, SWSRC_LAST); } break; case ITEM_PHASES_TRIM_RUD: case ITEM_PHASES_TRIM_ELE: case ITEM_PHASES_TRIM_THR: case ITEM_PHASES_TRIM_AIL: if (k != 0) { uint8_t t = j-ITEM_PHASES_TRIM_RUD; putsTrimMode((4+LEN_FP_NAME)*FW+j*(5*FW/2), y, k, t, attr); if (active) { trim_t & v = p->trim[t]; v.mode = checkIncDec(event, v.mode==TRIM_MODE_NONE ? -1 : v.mode, -1, 2*MAX_PHASES-1, EE_MODEL, isTrimModeAvailable); } } break; case ITEM_PHASES_FADE_IN: lcd_outdezAtt(32*FW-2, 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(35*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 // PCBTARANIS 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_MAX, 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, STR_INPUTNAME, 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); 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, GV_RANGELARGE_WEIGHT_NEG, GV_RANGELARGE_WEIGHT, 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_OFFSET_NEG, GV_RANGELARGE_OFFSET, 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+3 #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(lcdNextPos, 0, g_model.limitData[ch-1].name, len, ZCHAR); lcd_putc(lcdNextPos, 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, 0); gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, attr | (isMixActive(i) ? BOLD : 0), 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 #elif defined(CPUARM) #define MIN_MAX_DISPLAY(x) (x) #else #define MIN_MAX_DISPLAY(x) ((int8_t)(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 200); /*2 seconds*/ if (first2seconds) { menuTitle = STR_GLOBAL_V; for (int i=0; i 6 for (uint8_t l=0; l0) ? BLINK|INVERS : INVERS) : 0); xcoord_t x = GVARS_FM_COLUMN(j-1); #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 LogicalSwitchFields { LS_FIELD_FUNCTION, LS_FIELD_V1, LS_FIELD_V2, #if defined(PCBTARANIS) LS_FIELD_V3, #endif LS_FIELD_ANDSW, #if defined(CPUARM) LS_FIELD_DURATION, LS_FIELD_DELAY, #endif LS_FIELD_COUNT, LS_FIELD_LAST = LS_FIELD_COUNT-1 }; #if LCD_W >= 212 #define CSW_1ST_COLUMN (4*FW-3) #define CSW_2ND_COLUMN (8*FW+2+FW/2) #define CSW_3RD_COLUMN (14*FW+1+FW/2) #define CSW_4TH_COLUMN (22*FW) #define CSW_5TH_COLUMN (26*FW+3) #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 menuModelLogicalSwitchOne(uint8_t event) { TITLE(STR_MENUCUSTOMSWITCH); LogicalSwitchData * cs = cswAddress(s_currIdx); uint8_t sw = SWSRC_SW1+s_currIdx; putsSwitches(14*FW, 0, sw, (getSwitch(sw) ? BOLD : 0)); SUBMENU_NOTITLE(LS_FIELD_COUNT, {0, 0, 1, 0 /*, 0...*/}); int8_t sub = m_posVert; for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); uint8_t cstate = cswFamily(cs->func); switch(i) { case LS_FIELD_FUNCTION: lcd_putsLeft(y, STR_FUNC); lcd_putsiAtt(CSWONE_2ND_COLUMN, y, STR_VCSWFUNC, cs->func, attr); if (attr) { cs->func = checkIncDec(event, cs->func, 0, LS_FUNC_MAX, EE_MODEL, isLogicalSwitchFunctionAvailable); uint8_t new_cstate = cswFamily(cs->func); if (cstate != new_cstate) { if (new_cstate == LS_FAMILY_TIMER) { cs->v1 = cs->v2 = -119; } else if (new_cstate == LS_FAMILY_STAY) { cs->v1 = 0; cs->v2 = -129; cs->v3 = 0; } else { cs->v1 = cs->v2 = 0; } } } break; case LS_FIELD_V1: { lcd_putsLeft(y, STR_V1); int8_t v1_min=0, v1_max=MIXSRC_LAST_TELEM; if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY || cstate == LS_FAMILY_STAY) { putsSwitches(CSWONE_2ND_COLUMN, y, cs->v1, attr); v1_min = SWSRC_OFF+1; v1_max = SWSRC_ON-1; } else if (cstate == LS_FAMILY_TIMER) { 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 LS_FIELD_V2: { lcd_putsLeft(y, STR_V2); int16_t v2_min=0, v2_max=MIXSRC_LAST_TELEM; if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { putsSwitches(CSWONE_2ND_COLUMN, y, cs->v2, attr); v2_min = SWSRC_OFF+1; v2_max = SWSRC_ON-1; } else if (cstate == LS_FAMILY_TIMER) { lcd_outdezAtt(CSWONE_2ND_COLUMN, y, cs->v2+1, LEFT|attr); v2_max = 99; } else if (cstate == LS_FAMILY_STAY) { lcd_putc(CSWONE_2ND_COLUMN-4, y, '['); lcd_outdezAtt(CSWONE_2ND_COLUMN, y, cswTimerValue(cs->v2), LEFT|PREC1|(m_posHorz==0 ? attr : 0)); lcd_putc(lcdLastPos, y, ':'); if (cs->v3 == 0) lcd_putsAtt(lcdLastPos+3, y, "--", (m_posHorz==1 ? attr : 0)); else lcd_outdezAtt(lcdLastPos+3, y, cswTimerValue(cs->v2+cs->v3), LEFT|PREC1|(m_posHorz==1 ? attr : 0)); lcd_putc(lcdLastPos, y, ']'); if (s_editMode <= 0) continue; if (attr && m_posHorz==1) { CHECK_INCDEC_MODELVAR(event, cs->v3, 0, 222 - cs->v2); break; } v2_min=-129; v2_max = 122; } else if (cstate == LS_FAMILY_COMP) { 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 == LS_FAMILY_OFS) { 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 LS_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_LS_ANDSW, MAX_LS_ANDSW); break; case LS_FIELD_DURATION: lcd_putsLeft(y, STR_DURATION); if (cs->duration > 0) lcd_outdezAtt(CSWONE_2ND_COLUMN, y, 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_LS_DURATION); break; case LS_FIELD_DELAY: lcd_putsLeft(y, STR_DELAY); if (cs->delay > 0) lcd_outdezAtt(CSWONE_2ND_COLUMN, y, 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_LS_DELAY); break; } } } void menuModelLogicalSwitches(uint8_t event) { SIMPLE_MENU(STR_MENUCUSTOMSWITCHES, menuTabModel, e_LogicalSwitches, 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(menuModelLogicalSwitchOne); } 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 == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { putsSwitches(CSW_2ND_COLUMN, y, cs->v1, 0); putsSwitches(CSW_3RD_COLUMN, y, cs->v2, 0); } else if (cstate == LS_FAMILY_COMP) { putsMixerSource(CSW_2ND_COLUMN, y, cs->v1, 0); putsMixerSource(CSW_3RD_COLUMN, y, cs->v2, 0); } else if (cstate == LS_FAMILY_STAY) { putsSwitches(CSW_2ND_COLUMN, y, cs->v1, 0); lcd_putc(CSW_3RD_COLUMN-4, y, '['); lcd_outdezAtt(CSW_3RD_COLUMN, y, cswTimerValue(cs->v2), LEFT|PREC1); lcd_putc(lcdLastPos, y, ':'); if (cs->v3 == 0) lcd_puts(lcdLastPos+3, y, "--"); else lcd_outdezAtt(lcdLastPos+3, y, cswTimerValue(cs->v2+cs->v3), LEFT|PREC1); lcd_putc(lcdLastPos-1, y, ']'); } else if (cstate == LS_FAMILY_TIMER) { 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 { LogicalSwitchData csw; CustomFnData cfn; } data; }; Clipboard clipboard; void onLogicalSwitchesMenu(const char *result) { int8_t sub = m_posVert-1; LogicalSwitchData * 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(LogicalSwitchData)); eeDirty(EE_MODEL); } } #endif void menuModelLogicalSwitches(uint8_t event) { INCDEC_DECLARE_VARS(); MENU(STR_MENUCUSTOMSWITCHES, menuTabModel, e_LogicalSwitches, NUM_CSW+1, {0, NAVIGATION_LINE_BY_LINE|LS_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); LogicalSwitchData * 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 = onLogicalSwitchesMenu; } #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); LogicalSwitchData * 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_val=cs->v1, v1_min=0, v1_max=MIXSRC_LAST_TELEM, v2_min=0, v2_max=MIXSRC_LAST_TELEM; int16_t v3_min=0, v3_max=100; #else int8_t v1_min=0, v1_max=MIXSRC_LAST_TELEM, v2_min=0, v2_max=MIXSRC_LAST_TELEM; #define v1_val cs->v1 #endif if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { 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(isSwitchAvailableInLogicalSwitches); } #if defined(CPUARM) else if (cstate == LS_FAMILY_STAY) { putsSwitches(CSW_2ND_COLUMN, y, cs->v1, attr1); lcd_putc(CSW_3RD_COLUMN-4, y, '['); lcd_outdezAtt(CSW_3RD_COLUMN, y, cswTimerValue(cs->v2), LEFT|PREC1|attr2); lcd_putc(lcdLastPos, y, ':'); if (cs->v3 == 0) lcd_putsAtt(lcdLastPos+3, y, "--", (horz==LS_FIELD_V3 ? attr : 0)); else lcd_outdezAtt(lcdLastPos+3, y, cswTimerValue(cs->v2+cs->v3), LEFT|PREC1|(horz==LS_FIELD_V3 ? attr : 0)); lcd_putc(lcdLastPos, y, ']'); v1_min = SWSRC_OFF+1; v1_max = SWSRC_ON-1; v2_min=-129; v2_max = 122; v3_max = 222 - cs->v2; if (horz == 1) { INCDEC_SET_FLAG(INCDEC_SWITCH); INCDEC_ENABLE_CHECK(isSwitchAvailable); } else { INCDEC_SET_FLAG(0); INCDEC_ENABLE_CHECK(NULL); } } #endif else if (cstate == LS_FAMILY_COMP) { putsMixerSource(CSW_2ND_COLUMN, y, v1_val, attr1); putsMixerSource(CSW_3RD_COLUMN, y, cs->v2, attr2); INCDEC_SET_FLAG(INCDEC_SOURCE); INCDEC_ENABLE_CHECK(isSourceAvailable); } else if (cstate == LS_FAMILY_TIMER) { 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 { #if defined(CPUARM) v1_val = (uint8_t)cs->v1; #endif putsMixerSource(CSW_2ND_COLUMN, y, v1_val, 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 (v1_val >= MIXSRC_FIRST_TELEM) { putsTelemetryChannel(CSW_3RD_COLUMN, y, v1_val - MIXSRC_FIRST_TELEM, convertCswTelemValue(cs), LEFT|attr2); v2_max = maxTelemValue(v1_val - MIXSRC_FIRST_TELEM + 1); #if defined(CPUARM) v2_min = minTelemValue(v1_val - MIXSRC_FIRST_TELEM + 1); if (cs->v2 < v2_min || cs->v2 > v2_max) { cs->v2 = 0; eeDirty(EE_MODEL); } #else if (cstate == LS_FAMILY_OFS) { 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 (v1_val >= MIXSRC_FIRST_TELEM) { putsTelemetryChannel(CSW_3RD_COLUMN, y, v1_val - 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 #if defined(CPUARM) putsSwitches(CSW_4TH_COLUMN, y, cs->andsw, horz==LS_FIELD_ANDSW ? attr : 0); #else uint8_t andsw = cs->andsw; if (andsw > SWSRC_LAST_SWITCH) { andsw += SWSRC_SW1-SWSRC_LAST_SWITCH-1; } putsSwitches(CSW_4TH_COLUMN, y, andsw, horz==LS_FIELD_ANDSW ? attr : 0); #endif #if defined(CPUARM) // CSW duration if (cs->duration > 0) lcd_outdezAtt(CSW_5TH_COLUMN, y, cs->duration, (horz==LS_FIELD_DURATION ? attr : 0)|PREC1|LEFT); else lcd_putsiAtt(CSW_5TH_COLUMN, y, STR_MMMINV, 0, horz==LS_FIELD_DURATION ? attr : 0); // CSW delay if (cstate == LS_FAMILY_STAY) { lcd_puts(CSW_6TH_COLUMN, y, "N/A"); if (attr && horz == LS_FIELD_DELAY) { REPEAT_LAST_CURSOR_MOVE(); } } else if (cs->delay > 0) { lcd_outdezAtt(CSW_6TH_COLUMN, y, cs->delay, (horz==LS_FIELD_DELAY ? attr : 0)|PREC1|LEFT); } else { lcd_putsiAtt(CSW_6TH_COLUMN, y, STR_MMMINV, 0, horz==LS_FIELD_DELAY ? attr : 0); } if (attr && horz == LS_FIELD_V3 && cstate != LS_FAMILY_STAY) { REPEAT_LAST_CURSOR_MOVE(); } #endif if ((s_editMode>0 || p1valdiff) && attr) { switch (horz) { case LS_FIELD_FUNCTION: { #if defined(CPUARM) cs->func = checkIncDec(event, cs->func, 0, LS_FUNC_MAX, EE_MODEL, isLogicalSwitchFunctionAvailable); #else CHECK_INCDEC_MODELVAR_ZERO(event, cs->func, LS_FUNC_MAX); #endif uint8_t new_cstate = cswFamily(cs->func); if (cstate != new_cstate) { #if defined(CPUARM) if (new_cstate == LS_FAMILY_TIMER) { cs->v1 = cs->v2 = -119; } else if (new_cstate == LS_FAMILY_STAY) { cs->v1 = 0; cs->v2 = -129; cs->v3 = 0; } else { cs->v1 = cs->v2 = 0; } #else cs->v1 = cs->v2 = (new_cstate==LS_FAMILY_TIMER ? -119/*1.0*/ : 0); #endif } break; } case LS_FIELD_V1: cs->v1 = CHECK_INCDEC_PARAM(event, v1_val, v1_min, v1_max); break; case LS_FIELD_V2: cs->v2 = CHECK_INCDEC_PARAM(event, cs->v2, v2_min, v2_max); #if defined(PCBTARANIS) if (cstate==LS_FAMILY_OFS && cs->v1!=0 && event==EVT_KEY_LONG(KEY_ENTER)) { killEvents(event); getvalue_t x = getValue(v1_val); if (v1_val < MIXSRC_GVAR1) cs->v2 = calcRESXto100(x); else if (v1_val - MIXSRC_FIRST_TELEM + 1 == TELEM_ALT) cs->v2 *= 100; eeDirty(EE_MODEL); } #endif break; #if defined(CPUARM) case LS_FIELD_V3: cs->v3 = CHECK_INCDEC_PARAM(event, cs->v3, v3_min, v3_max); break; #endif case LS_FIELD_ANDSW: #if defined(CPUARM) INCDEC_SET_FLAG(INCDEC_SWITCH); INCDEC_ENABLE_CHECK(isSwitchAvailableInLogicalSwitches); cs->andsw = CHECK_INCDEC_PARAM(event, cs->andsw, -MAX_LS_ANDSW, MAX_LS_ANDSW); #else CHECK_INCDEC_MODELVAR_ZERO(event, cs->andsw, MAX_LS_ANDSW); #endif break; #if defined(CPUARM) case LS_FIELD_DURATION: CHECK_INCDEC_MODELVAR_ZERO(event, cs->duration, MAX_LS_DURATION); break; case LS_FIELD_DELAY: CHECK_INCDEC_MODELVAR_ZERO(event, cs->delay, MAX_LS_DELAY); break; #endif } } } } #endif #if LCD_W >= 212 #define MODEL_CUSTOM_FUNC_1ST_COLUMN (5+3*FW) #define MODEL_CUSTOM_FUNC_2ND_COLUMN (8*FW) #define MODEL_CUSTOM_FUNC_3RD_COLUMN (20*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].play.name), 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].play.name, result, sizeof(g_model.funcSw[sub].play.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|4/*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]; uint8_t func = CFN_FUNC(sd); for (uint8_t j=0; j<5; 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, CFN_SWITCH(sd), attr | ((activeFnSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0)); if (active || AUTOSWITCH_ENTER_LONG()) CHECK_INCDEC_MODELSWITCH(event, CFN_SWITCH(sd), SWSRC_FIRST, SWSRC_LAST); break; case 1: if (CFN_SWITCH(sd)) { lcd_putsiAtt(MODEL_CUSTOM_FUNC_2ND_COLUMN, y, STR_VFSWFUNC, func, attr); if (active) { #if defined(CPUARM) CFN_FUNC(sd) = checkIncDec(event, CFN_FUNC(sd), 0, FUNC_MAX-1, EE_MODEL, isAssignableFunctionAvailable); #else CHECK_INCDEC_MODELVAR_ZERO(event, CFN_FUNC(sd), FUNC_MAX-1); #endif if (checkIncDec_Ret) CFN_RESET(sd); } } else { j = 4; // skip other fields if (sub==k && m_posHorz > 0) { REPEAT_LAST_CURSOR_MOVE(); } } break; case 2: { int8_t maxParam = NUM_CHNOUT-1; if (func == FUNC_SAFETY_CHANNEL) { putsChn(lcdNextPos, y, CFN_CH_INDEX(sd)+1, attr); } else if (func == FUNC_TRAINER) { maxParam = 4; #if defined(CPUARM) putsMixerSource(lcdNextPos, y, CFN_CH_INDEX(sd)==0 ? 0 : MIXSRC_Rud+CFN_CH_INDEX(sd)-1, attr); #else putsMixerSource(lcdNextPos, y, MIXSRC_Rud+CFN_CH_INDEX(sd)-1, attr); #endif } #if defined(GVARS) else if (func == FUNC_ADJUST_GVAR) { maxParam = MAX_GVARS-1; putsStrIdx(lcdNextPos, y, STR_GV, CFN_GVAR_INDEX(sd)+1, attr); if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_GVAR_INDEX(sd), maxParam); break; } #endif #if defined(CPUARM) else if (func == FUNC_SET_TIMER) { maxParam = 1; putsStrIdx(lcdNextPos, y, STR_TIMER, CFN_TIMER_INDEX(sd)+1, attr); if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_TIMER_INDEX(sd), maxParam); break; } #endif else if (attr) { REPEAT_LAST_CURSOR_MOVE(); } if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_CH_INDEX(sd), maxParam); break; } case 3: { INCDEC_DECLARE_VARS(); int16_t val_displayed = CFN_PARAM(sd); int8_t val_min = 0; #if defined(CPUARM) int16_t val_max = 255; #else uint8_t val_max = 255; #endif if (func == FUNC_SAFETY_CHANNEL) { 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(CPUARM) else if (func == FUNC_SET_TIMER) { val_max = 59*60+59; putsTime(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT, attr); } #endif #if defined(AUDIO) else if (func == FUNC_PLAY_SOUND) { val_max = AU_FRSKY_LAST-AU_FRSKY_FIRST-1; lcd_putsiAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, STR_FUNCSOUNDS, val_displayed, attr); } #endif #if defined(HAPTIC) else if (func == 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 (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC) { #if LCD_W >= 212 xcoord_t x = MODEL_CUSTOM_FUNC_3RD_COLUMN; #else xcoord_t x = (func == FUNC_PLAY_TRACK ? MODEL_CUSTOM_FUNC_2ND_COLUMN + FW + FW*strlen(TR_PLAY_TRACK) : MODEL_CUSTOM_FUNC_3RD_COLUMN); #endif if (ZEXIST(sd->play.name)) lcd_putsnAtt(x, y, sd->play.name, sizeof(sd->play.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->play.name), sd->play.name)) { menuHandler = onCustomFunctionsFileSelectionMenu; } else { POPUP_WARNING(STR_NO_SOUNDS_ON_SD); s_menu_flags = 0; } } break; } else if (func == 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 (func == 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 (func == 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 (func == 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 (func == 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 (func == 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 (func == FUNC_RESET) { val_max = FUNC_RESET_PARAM_LAST; lcd_putsiAtt(MODEL_CUSTOM_FUNC_3RD_COLUMN, y, STR_VFSWRESET, CFN_PARAM(sd), attr); } #if defined(GVARS) else if (func == FUNC_ADJUST_GVAR) { 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; #if defined(CPUARM) CFN_GVAR_MODE(sd) &= 0x03; #endif val_displayed = 0; } } #endif else if (attr) { REPEAT_LAST_CURSOR_MOVE(); } if (active) { CFN_PARAM(sd) = CHECK_INCDEC_PARAM(event, val_displayed, val_min, val_max); } break; } case 4: if (HAS_ENABLE_PARAM(func)) { 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 (HAS_REPEAT_PARAM(func)) { 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, SCRIPTS_EXT, sizeof(sd.file), NULL)) { POPUP_WARNING(STR_NO_SCRIPTS_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)) 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, SCRIPTS_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 #if defined(FAS_OFFSET) || !defined(CPUM64) #define IF_FAS_OFFSET(x) x, #else #define IF_FAS_OFFSET(x) #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_FAS_OFFSET(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, 1+g_model.frsky.blades, attr); #if defined(CPUARM) if (attr) CHECK_INCDEC_MODELVAR(event, g_model.frsky.blades, MIN_BLADES, MAX_BLADES); #else if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.blades, MAX_BLADES); #endif 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(FAS_OFFSET) || !defined(CPUM64) case ITEM_TELEMETRY_FAS_OFFSET: lcd_putsLeft(y, STR_FAS_OFFSET); lcd_outdezAtt(TELEM_COL2, y, g_model.frsky.fasOffset, attr|LEFT|PREC1); lcd_outdezAtt(TELEM_COL2+6*FW, y, frskyData.hub.current, LEFT|PREC1); lcd_putc(TELEM_COL2+8*FW, y, 'A'); if (attr) g_model.frsky.fasOffset = checkIncDec(event, g_model.frsky.fasOffset, -15, 15, EE_MODEL); break; #endif #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); 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 != (bool)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 != (bool)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 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, convertBarTelemValue(barSource, bar.barMin), (m_posHorz==1 ? attr : 0) | LEFT); putsTelemetryChannel(TELEM_BARS_COLMAX, y, barSource-1, convertBarTelemValue(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 = CHECK_INCDEC_MODELVAR_ZERO_CHECK(event, barSource, TELEM_DISPLAY_MAX, isTelemetrySourceAvailable); if (checkIncDec_Ret) { bar.barMin = 0; bar.barMax = 255 - maxBarTelemValue(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, maxBarTelemValue(barSource), EE_MODEL|NO_INCDEC_MARKS); break; } } } else #endif { for (uint8_t c=0; c0 || p1valdiff)) { CHECK_INCDEC_MODELVAR_ZERO_CHECK(event, value, (lineIndex==3 && c==0) ? TELEM_STATUS_MAX : TELEM_DISPLAY_MAX, isTelemetrySourceAvailable); } } 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