make “reset” menu consistent with X9D (long enter press)
[opentx.git] / radio / src / gui / 128x64 / view_telemetry.cpp
blob8340d300987930155e3f28f188760b604fe886f2
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "opentx.h"
23 #define STATUS_BAR_Y (7*FH+1)
24 #define TELEM_2ND_COLUMN (10*FW)
26 #if defined(FRSKY_HUB) && defined(GAUGES)
27 bar_threshold_t barsThresholds[THLD_MAX];
28 #endif
30 uint8_t s_frsky_view = 0;
32 #define BAR_LEFT 25
33 #define BAR_WIDTH 100
35 void displayRssiLine()
37 if (TELEMETRY_STREAMING()) {
38 lcdDrawSolidHorizontalLine(0, 55, 128, 0); // separator
39 uint8_t rssi;
40 #if !defined(CPUARM)
41 rssi = min((uint8_t)99, telemetryData.rssi[1].value);
42 lcdDrawTextAlignedLeft(STATUS_BAR_Y, STR_TX); lcdDrawNumber(4*FW+1, STATUS_BAR_Y, rssi, LEADING0, 2);
43 lcdDrawRect(BAR_LEFT+1, 57, 38, 7);
44 lcdDrawFilledRect(BAR_LEFT+1, 58, 4*rssi/11, 5, (rssi < getRssiAlarmValue(0)) ? DOTTED : SOLID);
45 #endif
46 rssi = min((uint8_t)99, TELEMETRY_RSSI());
47 lcdDrawText(104, STATUS_BAR_Y, STR_RX); lcdDrawNumber(105+4*FW, STATUS_BAR_Y, rssi, LEADING0, 2);
48 lcdDrawRect(65, 57, 38, 7);
49 uint8_t v = 4*rssi/11;
50 lcdDrawFilledRect(66+36-v, 58, v, 5, (rssi < getRssiAlarmValue(0)) ? DOTTED : SOLID);
52 else {
53 lcdDrawText(7*FW, STATUS_BAR_Y, STR_NODATA, BLINK);
54 lcdInvertLastLine();
58 #if defined(TELEMETRY_FRSKY) && defined(FRSKY_HUB) && defined(GPS) && !defined(CPUARM)
59 void displayGpsTime()
61 uint8_t att = (TELEMETRY_STREAMING() ? LEFT|LEADING0 : LEFT|LEADING0|BLINK);
62 lcdDrawNumber(CENTER_OFS+6*FW+7, STATUS_BAR_Y, telemetryData.hub.hour, att, 2);
63 lcdDrawChar(CENTER_OFS+8*FW+4, STATUS_BAR_Y, ':', att);
64 lcdDrawNumber(CENTER_OFS+9*FW+2, STATUS_BAR_Y, telemetryData.hub.min, att, 2);
65 lcdDrawChar(CENTER_OFS+11*FW-1, STATUS_BAR_Y, ':', att);
66 lcdDrawNumber(CENTER_OFS+12*FW-3, STATUS_BAR_Y, telemetryData.hub.sec, att, 2);
67 lcdInvertLastLine();
70 void drawGPSCoord(uint8_t y, char direction, int16_t bp, int16_t ap)
72 if (telemetryData.hub.gpsFix >= 0) {
73 if (!direction) direction = '-';
74 lcdDrawNumber(TELEM_2ND_COLUMN, y, bp / 100, LEFT); // ddd before '.'
75 lcdDrawChar(lcdLastPos, y, '@');
76 uint8_t mn = bp % 100; // TODO div_t
77 if (g_eeGeneral.gpsFormat == 0) {
78 lcdDrawChar(lcdLastPos+FWNUM, y, direction);
79 lcdDrawNumber(lcdLastPos+FW+FW+1, y, mn, LEFT|LEADING0, 2); // mm before '.'
80 lcdDrawSolidVerticalLine(lcdLastPos, y, 2);
81 uint16_t ss = ap * 6;
82 lcdDrawNumber(lcdLastPos+3, y, ss / 1000, LEFT|LEADING0, 2); // ''
83 lcdDrawPoint(lcdLastPos, y+FH-2, 0); // small decimal point
84 lcdDrawNumber(lcdLastPos+2, y, ss % 1000, LEFT|LEADING0, 3); // ''
85 lcdDrawSolidVerticalLine(lcdLastPos, y, 2);
86 lcdDrawSolidVerticalLine(lcdLastPos+2, y, 2);
88 else {
89 lcdDrawNumber(lcdLastPos+FW, y, mn, LEFT|LEADING0, 2); // mm before '.'
90 lcdDrawPoint(lcdLastPos, y+FH-2, 0); // small decimal point
91 lcdDrawNumber(lcdLastPos+2, y, ap, LEFT|UNSIGN|LEADING0, 4); // after '.'
92 lcdDrawChar(lcdLastPos+1, y, direction);
95 else {
96 // no fix
97 lcdDrawText(TELEM_2ND_COLUMN, y, STR_VCSWFUNC+1/*----*/);
100 #elif !defined(CPUARM)
101 #define displayGpsTime()
102 #define drawGPSCoord(...)
103 #endif
105 #if !defined(CPUARM)
106 void displayVoltageScreenLine(uint8_t y, uint8_t index)
108 drawStringWithIndex(0, y, STR_A, index+1, 0);
109 if (TELEMETRY_STREAMING()) {
110 drawTelemetryValue(3*FW+6*FW+4, y-FH, index+TELEM_A1-1, telemetryData.analog[index].value, DBLSIZE);
111 lcdDrawChar(12*FW-1, y-FH, '<'); drawTelemetryValue(17*FW, y-FH, index+TELEM_A1-1, telemetryData.analog[index].min, NO_UNIT);
112 lcdDrawChar(12*FW, y, '>'); drawTelemetryValue(17*FW, y, index+TELEM_A1-1, telemetryData.analog[index].max, NO_UNIT);
115 #endif
117 uint8_t barCoord(int16_t value, int16_t min, int16_t max)
119 #if defined(CPUARM)
120 if (value <= min)
121 return 0;
122 else if (value >= max)
123 return BAR_WIDTH-1;
124 else
125 return ((int32_t)(BAR_WIDTH-1) * (value - min)) / (max - min);
126 #else
127 return limit((uint8_t)0, (uint8_t)(((int32_t)(BAR_WIDTH-1) * (value - min)) / (max - min)), (uint8_t)BAR_WIDTH);
128 #endif
131 #if !defined(CPUARM)
132 void displayVoltagesScreen()
134 // Volts / Amps / Watts / mAh
135 uint8_t analog = 0;
136 lcdDrawTextAtIndex(0, 2*FH, STR_AMPSRC, g_model.frsky.voltsSource+1, 0);
137 switch (g_model.frsky.voltsSource) {
138 case FRSKY_VOLTS_SOURCE_A1:
139 case FRSKY_VOLTS_SOURCE_A2:
140 displayVoltageScreenLine(2*FH, g_model.frsky.voltsSource);
141 analog = 1+g_model.frsky.voltsSource;
142 break;
143 #if defined(FRSKY_HUB)
144 case FRSKY_VOLTS_SOURCE_FAS:
145 drawTelemetryValue(3*FW+6*FW+4, FH, TELEM_VFAS-1, telemetryData.hub.vfas, DBLSIZE);
146 break;
147 case FRSKY_VOLTS_SOURCE_CELLS:
148 drawTelemetryValue(3*FW+6*FW+4, FH, TELEM_CELLS_SUM-1, telemetryData.hub.cellsSum, DBLSIZE);
149 break;
150 #endif
153 if (g_model.frsky.currentSource) {
154 lcdDrawTextAtIndex(0, 4*FH, STR_AMPSRC, g_model.frsky.currentSource, 0);
155 switch(g_model.frsky.currentSource) {
156 case FRSKY_CURRENT_SOURCE_A1:
157 case FRSKY_CURRENT_SOURCE_A2:
158 displayVoltageScreenLine(4*FH, g_model.frsky.currentSource-1);
159 break;
160 #if defined(FRSKY_HUB)
161 case FRSKY_CURRENT_SOURCE_FAS:
162 drawTelemetryValue(3*FW+6*FW+4, 3*FH, TELEM_CURRENT-1, telemetryData.hub.current, DBLSIZE);
163 break;
164 #endif
167 drawTelemetryValue(4, 5*FH, TELEM_POWER-1, telemetryData.hub.power, LEFT|DBLSIZE);
168 drawTelemetryValue(3*FW+4+4*FW+6*FW+FW, 5*FH, TELEM_CONSUMPTION-1, telemetryData.hub.currentConsumption, DBLSIZE);
170 else {
171 displayVoltageScreenLine(analog > 0 ? 5*FH : 4*FH, analog ? 2-analog : 0);
172 if (analog == 0) displayVoltageScreenLine(6*FH, 1);
175 #if defined(FRSKY_HUB)
176 // Cells voltage
177 if (telemetryData.hub.cellsCount > 0) {
178 uint8_t y = 1*FH;
179 for (uint8_t k=0; k<telemetryData.hub.cellsCount && k<6; k++) {
180 #if defined(GAUGES)
181 uint8_t attr = (barsThresholds[THLD_CELL] && telemetryData.hub.cellVolts[k] < barsThresholds[THLD_CELL]) ? BLINK|PREC2 : PREC2;
182 #else
183 uint8_t attr = PREC2;
184 #endif
185 lcdDrawNumber(LCD_W, y, TELEMETRY_CELL_VOLTAGE(k), attr, 4);
186 y += 1*FH;
188 lcdDrawSolidVerticalLine(LCD_W-3*FW-2, 8, 47);
190 #endif
192 displayRssiLine();
195 void displayAfterFlightScreen()
197 uint8_t line=1*FH+1;
198 if (IS_GPS_AVAILABLE()) {
199 // Latitude
200 lcdDrawTextAlignedLeft(line, STR_LATITUDE);
201 drawGPSCoord(line, telemetryData.hub.gpsLatitudeNS, telemetryData.hub.gpsLatitude_bp, telemetryData.hub.gpsLatitude_ap);
202 // Longitude
203 line+=1*FH+1;
204 lcdDrawTextAlignedLeft(line, STR_LONGITUDE);
205 drawGPSCoord(line, telemetryData.hub.gpsLongitudeEW, telemetryData.hub.gpsLongitude_bp, telemetryData.hub.gpsLongitude_ap);
206 displayGpsTime();
207 line+=1*FH+1;
209 // Rssi
210 lcdDrawTextAlignedLeft(line, STR_MINRSSI);
211 lcdDrawText(TELEM_2ND_COLUMN, line, STR_TX);
212 lcdDrawNumber(TELEM_2ND_COLUMN+3*FW, line, telemetryData.rssi[1].min, LEFT|LEADING0, 2);
213 lcdDrawText(TELEM_2ND_COLUMN+6*FW, line, STR_RX);
214 lcdDrawNumber(TELEM_2ND_COLUMN+9*FW, line, telemetryData.rssi[0].min, LEFT|LEADING0, 2);
216 #endif
218 bool displayGaugesTelemetryScreen(FrSkyScreenData & screen)
220 // Custom Screen with gauges
221 uint8_t barHeight = 5;
222 for (int8_t i=3; i>=0; i--) {
223 FrSkyBarData & bar = screen.bars[i];
224 source_t source = bar.source;
225 #if defined(CPUARM)
226 getvalue_t barMin = bar.barMin;
227 getvalue_t barMax = bar.barMax;
228 if (source <= MIXSRC_LAST_CH) {
229 barMin = calc100toRESX(barMin);
230 barMax = calc100toRESX(barMax);
232 #else
233 getvalue_t barMin = convertBarTelemValue(source, bar.barMin);
234 getvalue_t barMax = convertBarTelemValue(source, 255-bar.barMax);
235 #endif
236 if (source && barMax > barMin) {
237 uint8_t y = barHeight+6+i*(barHeight+6);
238 #if defined(CPUARM)
239 drawSource(0, y+barHeight-5, source, 0);
240 #else
241 lcdDrawTextAtIndex(0, y+barHeight-5, STR_VTELEMCHNS, source, 0);
242 #endif
243 lcdDrawRect(BAR_LEFT, y, BAR_WIDTH+1, barHeight+2);
244 #if defined(CPUARM)
245 getvalue_t value = getValue(source);
246 #else
247 getvalue_t value = getValue(MIXSRC_FIRST_TELEM+source-1);
248 #endif
250 uint8_t thresholdX = 0;
252 #if !defined(CPUARM)
253 getvalue_t threshold = 0;
254 if (source <= TELEM_TIMER_MAX)
255 threshold = 0;
256 else if (source <= TELEM_RSSI_RX)
257 threshold = getRssiAlarmValue(source-TELEM_RSSI_TX);
258 else if (source <= TELEM_A2)
259 threshold = g_model.frsky.channels[source-TELEM_A1].alarms_value[0];
260 #if defined(FRSKY_HUB)
261 else {
262 #if defined(GAUGES)
263 threshold = convertBarTelemValue(source, barsThresholds[source-TELEM_ALT]);
264 #endif
266 #endif
268 if (threshold) {
269 thresholdX = barCoord(threshold, barMin, barMax);
270 if (thresholdX == 100)
271 thresholdX = 0;
273 #endif
275 uint8_t width = barCoord(value, barMin, barMax);
277 #if defined(CPUARM)
278 uint8_t barShade = SOLID;
279 #else
280 // reversed barshade for T1/T2
281 uint8_t barShade = ((threshold > value) ? DOTTED : SOLID);
282 if (source == TELEM_T1 || source == TELEM_T2) {
283 barShade = -barShade;
285 #endif
287 lcdDrawFilledRect(BAR_LEFT+1, y+1, width, barHeight, barShade);
289 for (uint8_t j=24; j<99; j+=25) {
290 if (j>thresholdX || j>width) {
291 lcdDrawSolidVerticalLine(j*BAR_WIDTH/100+BAR_LEFT+1, y+1, barHeight);
295 if (thresholdX) {
296 lcdDrawVerticalLine(BAR_LEFT+1+thresholdX, y-2, barHeight+3, DOTTED);
297 lcdDrawSolidHorizontalLine(BAR_LEFT+thresholdX, y-2, 3);
300 else {
301 barHeight += 2;
304 displayRssiLine();
305 return barHeight < 13;
308 #if defined(CPUARM)
309 bool displayNumbersTelemetryScreen(FrSkyScreenData & screen)
311 // Custom Screen with numbers
312 uint8_t fields_count = 0;
313 lcdDrawSolidVerticalLine(63, 8, 48);
314 for (uint8_t i=0; i<4; i++) {
315 for (uint8_t j=0; j<NUM_LINE_ITEMS; j++) {
316 source_t field = screen.lines[i].sources[j];
317 if (field > 0) {
318 fields_count++;
320 if (i==3) {
321 if (!TELEMETRY_STREAMING()) {
322 displayRssiLine();
323 return fields_count;
326 if (field) {
327 LcdFlags att = (i==3 ? RIGHT|NO_UNIT : RIGHT|DBLSIZE|NO_UNIT);
328 coord_t pos[] = {0, 65, 130};
329 if (field >= MIXSRC_FIRST_TIMER && field <= MIXSRC_LAST_TIMER && i!=3) {
330 // there is not enough space on LCD for displaying "Tmr1" or "Tmr2" and still see the - sign, we write "T1" or "T2" instead
331 drawStringWithIndex(pos[j], 1+FH+2*FH*i, "T", field-MIXSRC_FIRST_TIMER+1, 0);
333 else if (field >= MIXSRC_FIRST_TELEM && isGPSSensor(1+(field-MIXSRC_FIRST_TELEM)/3) && telemetryItems[(field-MIXSRC_FIRST_TELEM)/3].isAvailable()) {
334 // we don't display GPS name, no space for it
336 else {
337 drawSource(pos[j], 1+FH+2*FH*i, field, 0);
340 if (field >= MIXSRC_FIRST_TELEM) {
341 TelemetryItem & telemetryItem = telemetryItems[(field-MIXSRC_FIRST_TELEM)/3]; // TODO macro to convert a source to a telemetry index
342 if (!telemetryItem.isAvailable()) {
343 continue;
345 else if (telemetryItem.isOld()) {
346 att |= INVERS|BLINK;
349 drawSourceValue(pos[j+1]-2, (i==3 ? 1+FH+2*FH*i:FH+2*FH*i), field, att);
353 lcdInvertLastLine();
354 return fields_count;
356 #else
357 bool displayNumbersTelemetryScreen(FrSkyScreenData & screen)
359 // Custom Screen with numbers
360 uint8_t fields_count = 0;
361 for (uint8_t i=0; i<4; i++) {
362 for (uint8_t j=0; j<NUM_LINE_ITEMS; j++) {
363 uint8_t field = screen.lines[i].sources[j];
364 if (field > 0) {
365 fields_count++;
367 if (i==3) {
368 lcdDrawSolidVerticalLine(63, 8, 48);
369 if (TELEMETRY_STREAMING()) {
370 #if defined(FRSKY_HUB)
371 if (field == TELEM_ACC) {
372 lcdDrawTextAlignedLeft(STATUS_BAR_Y, STR_ACCEL);
373 lcdDrawNumber(4*FW, STATUS_BAR_Y, telemetryData.hub.accelX, LEFT|PREC2);
374 lcdDrawNumber(10*FW, STATUS_BAR_Y, telemetryData.hub.accelY, LEFT|PREC2);
375 lcdDrawNumber(16*FW, STATUS_BAR_Y, telemetryData.hub.accelZ, LEFT|PREC2);
376 break;
378 #endif
379 #if defined(FRSKY_HUB) && defined(GPS)
380 else if (field == TELEM_GPS_TIME) {
381 displayGpsTime();
382 break;
384 #endif
386 else {
387 displayRssiLine();
388 return fields_count;
391 if (field) {
392 getvalue_t value = getValue(MIXSRC_FIRST_TELEM+field-1);
393 uint8_t att = (i==3 ? NO_UNIT : DBLSIZE|NO_UNIT);
394 coord_t pos[] = {0, 65, 130};
395 drawTelemetryValue(pos[j+1]-2, FH+2*FH*i, field-1, value, att);
397 if (field >= TELEM_TIMER1 && field <= TELEM_TIMER_MAX && i!=3) {
398 // there is not enough space on LCD for displaying "Tmr1" or "Tmr2" and still see the - sign, we write "T1" or "T2" instead
399 field = field-TELEM_TIMER1+TELEM_T1;
402 lcdDrawTextAtIndex(pos[j], 1+FH+2*FH*i, STR_VTELEMCHNS, field, 0);
406 lcdInvertLastLine();
407 return fields_count;
409 #endif
411 #if defined(CPUARM)
412 bool displayCustomTelemetryScreen(uint8_t index)
414 FrSkyScreenData & screen = g_model.frsky.screens[index];
416 #if defined(GAUGES)
417 if (IS_BARS_SCREEN(s_frsky_view)) {
418 return displayGaugesTelemetryScreen(screen);
420 #endif
422 displayNumbersTelemetryScreen(screen);
424 return true;
426 #else
427 bool displayCustomTelemetryScreen(uint8_t index)
429 FrSkyScreenData & screen = g_model.frsky.screens[index];
431 #if defined(GAUGES)
432 if (IS_BARS_SCREEN(s_frsky_view)) {
433 return displayGaugesTelemetryScreen(screen);
435 #endif
437 return displayNumbersTelemetryScreen(screen);
439 #endif
441 bool displayTelemetryScreen()
443 #if defined(LUA)
444 if (TELEMETRY_SCREEN_TYPE(s_frsky_view) == TELEMETRY_SCREEN_TYPE_SCRIPT) {
445 uint8_t state = isTelemetryScriptAvailable(s_frsky_view);
446 switch (state) {
447 case SCRIPT_OK:
448 return true; // contents will be drawed by Lua Task
449 case SCRIPT_NOFILE:
450 return false; // requested lua telemetry screen not available
451 case SCRIPT_SYNTAX_ERROR:
452 case SCRIPT_PANIC:
453 case SCRIPT_KILLED:
454 luaError(lsScripts, state, false);
455 return true;
457 return false;
459 #endif
461 #if defined(CPUARM)
462 if (TELEMETRY_SCREEN_TYPE(s_frsky_view) == TELEMETRY_SCREEN_TYPE_NONE) {
463 return false;
465 #endif
467 drawTelemetryTopBar();
469 if (s_frsky_view < MAX_TELEMETRY_SCREENS) {
470 return displayCustomTelemetryScreen(s_frsky_view);
473 #if !defined(CPUARM)
474 if (s_frsky_view == TELEMETRY_VOLTAGES_SCREEN) {
475 displayVoltagesScreen();
477 #endif
479 #if !defined(CPUARM) && defined(FRSKY_HUB)
480 else {
481 displayAfterFlightScreen();
483 #endif
485 return true;
488 #if defined(CPUARM)
489 enum NavigationDirection {
490 none,
492 down
494 #define decrTelemetryScreen() direction = up
495 #define incrTelemetryScreen() direction = down
496 #else
497 void decrTelemetryScreen()
499 if (s_frsky_view-- == 0)
500 s_frsky_view = TELEMETRY_VIEW_MAX;
502 void incrTelemetryScreen()
504 if (s_frsky_view++ == TELEMETRY_VIEW_MAX)
505 s_frsky_view = 0;
507 #endif
509 #if defined(PCBX7)
510 #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_LONG(KEY_PAGE)
511 #define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGE)
512 #else
513 #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_FIRST(KEY_UP)
514 #define EVT_KEY_NEXT_VIEW EVT_KEY_FIRST(KEY_DOWN)
515 #endif
517 void menuViewTelemetryFrsky(event_t event)
519 #if defined(CPUARM)
520 enum NavigationDirection direction = none;
521 #endif
523 switch (event) {
524 case EVT_KEY_FIRST(KEY_EXIT):
525 #if defined(LUA)
526 case EVT_KEY_LONG(KEY_EXIT):
527 #endif
528 killEvents(event);
529 chainMenu(menuMainView);
530 break;
532 case EVT_KEY_PREVIOUS_VIEW:
533 if (IS_KEY_LONG(EVT_KEY_PREVIOUS_VIEW)) {
534 killEvents(event);
536 decrTelemetryScreen();
537 break;
539 case EVT_KEY_NEXT_VIEW:
540 incrTelemetryScreen();
541 break;
543 #if defined(CPUARM)
544 case EVT_KEY_LONG(KEY_ENTER):
545 killEvents(event);
546 POPUP_MENU_ADD_ITEM(STR_RESET_TELEMETRY);
547 POPUP_MENU_ADD_ITEM(STR_RESET_FLIGHT);
548 POPUP_MENU_START(onMainViewMenu);
549 #else
550 case EVT_KEY_FIRST(KEY_ENTER):
551 telemetryReset();
552 #endif
553 break;
556 #if defined(CPUARM)
557 for (int i=0; i<=TELEMETRY_SCREEN_TYPE_MAX; i++) {
558 if (direction == up) {
559 if (s_frsky_view-- == 0)
560 s_frsky_view = TELEMETRY_VIEW_MAX;
562 else if (direction == down) {
563 if (s_frsky_view++ == TELEMETRY_VIEW_MAX)
564 s_frsky_view = 0;
566 else {
567 direction = down;
569 if (displayTelemetryScreen()) {
570 return;
574 drawTelemetryTopBar();
575 lcdDrawText(2*FW, 3*FH, "No Telemetry Screens");
576 displayRssiLine();
577 #else
578 if (!displayTelemetryScreen()) {
579 putEvent(event == EVT_KEY_FIRST(KEY_UP) ? event : EVT_KEY_FIRST(KEY_DOWN));
581 #endif
584 #undef EVT_KEY_PREVIOUS_VIEW
585 #undef EVT_KEY_NEXT_VIEW