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.
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
];
30 uint8_t s_frsky_view
= 0;
35 void displayRssiLine()
37 if (TELEMETRY_STREAMING()) {
38 lcdDrawSolidHorizontalLine(0, 55, 128, 0); // separator
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
);
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
);
53 lcdDrawText(7*FW
, STATUS_BAR_Y
, STR_NODATA
, BLINK
);
58 #if defined(TELEMETRY_FRSKY) && defined(FRSKY_HUB) && defined(GPS) && !defined(CPUARM)
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);
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);
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);
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
);
97 lcdDrawText(TELEM_2ND_COLUMN
, y
, STR_VCSWFUNC
+1/*----*/);
100 #elif !defined(CPUARM)
101 #define displayGpsTime()
102 #define drawGPSCoord(...)
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
);
117 uint8_t barCoord(int16_t value
, int16_t min
, int16_t max
)
122 else if (value
>= max
)
125 return ((int32_t)(BAR_WIDTH
-1) * (value
- min
)) / (max
- min
);
127 return limit((uint8_t)0, (uint8_t)(((int32_t)(BAR_WIDTH
-1) * (value
- min
)) / (max
- min
)), (uint8_t)BAR_WIDTH
);
132 void displayVoltagesScreen()
134 // Volts / Amps / Watts / mAh
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
;
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
);
147 case FRSKY_VOLTS_SOURCE_CELLS
:
148 drawTelemetryValue(3*FW
+6*FW
+4, FH
, TELEM_CELLS_SUM
-1, telemetryData
.hub
.cellsSum
, DBLSIZE
);
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);
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
);
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
);
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)
177 if (telemetryData
.hub
.cellsCount
> 0) {
179 for (uint8_t k
=0; k
<telemetryData
.hub
.cellsCount
&& k
<6; k
++) {
181 uint8_t attr
= (barsThresholds
[THLD_CELL
] && telemetryData
.hub
.cellVolts
[k
] < barsThresholds
[THLD_CELL
]) ? BLINK
|PREC2
: PREC2
;
183 uint8_t attr
= PREC2
;
185 lcdDrawNumber(LCD_W
, y
, TELEMETRY_CELL_VOLTAGE(k
), attr
, 4);
188 lcdDrawSolidVerticalLine(LCD_W
-3*FW
-2, 8, 47);
195 void displayAfterFlightScreen()
198 if (IS_GPS_AVAILABLE()) {
200 lcdDrawTextAlignedLeft(line
, STR_LATITUDE
);
201 drawGPSCoord(line
, telemetryData
.hub
.gpsLatitudeNS
, telemetryData
.hub
.gpsLatitude_bp
, telemetryData
.hub
.gpsLatitude_ap
);
204 lcdDrawTextAlignedLeft(line
, STR_LONGITUDE
);
205 drawGPSCoord(line
, telemetryData
.hub
.gpsLongitudeEW
, telemetryData
.hub
.gpsLongitude_bp
, telemetryData
.hub
.gpsLongitude_ap
);
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);
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
;
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
);
233 getvalue_t barMin
= convertBarTelemValue(source
, bar
.barMin
);
234 getvalue_t barMax
= convertBarTelemValue(source
, 255-bar
.barMax
);
236 if (source
&& barMax
> barMin
) {
237 uint8_t y
= barHeight
+6+i
*(barHeight
+6);
239 drawSource(0, y
+barHeight
-5, source
, 0);
241 lcdDrawTextAtIndex(0, y
+barHeight
-5, STR_VTELEMCHNS
, source
, 0);
243 lcdDrawRect(BAR_LEFT
, y
, BAR_WIDTH
+1, barHeight
+2);
245 getvalue_t value
= getValue(source
);
247 getvalue_t value
= getValue(MIXSRC_FIRST_TELEM
+source
-1);
250 uint8_t thresholdX
= 0;
253 getvalue_t threshold
= 0;
254 if (source
<= TELEM_TIMER_MAX
)
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)
263 threshold
= convertBarTelemValue(source
, barsThresholds
[source
-TELEM_ALT
]);
269 thresholdX
= barCoord(threshold
, barMin
, barMax
);
270 if (thresholdX
== 100)
275 uint8_t width
= barCoord(value
, barMin
, barMax
);
278 uint8_t barShade
= SOLID
;
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
;
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
);
296 lcdDrawVerticalLine(BAR_LEFT
+1+thresholdX
, y
-2, barHeight
+3, DOTTED
);
297 lcdDrawSolidHorizontalLine(BAR_LEFT
+thresholdX
, y
-2, 3);
305 return barHeight
< 13;
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
];
321 if (!TELEMETRY_STREAMING()) {
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
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()) {
345 else if (telemetryItem
.isOld()) {
349 drawSourceValue(pos
[j
+1]-2, (i
==3 ? 1+FH
+2*FH
*i
:FH
+2*FH
*i
), field
, att
);
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
];
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
);
379 #if defined(FRSKY_HUB) && defined(GPS)
380 else if (field
== TELEM_GPS_TIME
) {
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);
412 bool displayCustomTelemetryScreen(uint8_t index
)
414 FrSkyScreenData
& screen
= g_model
.frsky
.screens
[index
];
417 if (IS_BARS_SCREEN(s_frsky_view
)) {
418 return displayGaugesTelemetryScreen(screen
);
422 displayNumbersTelemetryScreen(screen
);
427 bool displayCustomTelemetryScreen(uint8_t index
)
429 FrSkyScreenData
& screen
= g_model
.frsky
.screens
[index
];
432 if (IS_BARS_SCREEN(s_frsky_view
)) {
433 return displayGaugesTelemetryScreen(screen
);
437 return displayNumbersTelemetryScreen(screen
);
441 bool displayTelemetryScreen()
444 if (TELEMETRY_SCREEN_TYPE(s_frsky_view
) == TELEMETRY_SCREEN_TYPE_SCRIPT
) {
445 uint8_t state
= isTelemetryScriptAvailable(s_frsky_view
);
448 return true; // contents will be drawed by Lua Task
450 return false; // requested lua telemetry screen not available
451 case SCRIPT_SYNTAX_ERROR
:
454 luaError(lsScripts
, state
, false);
462 if (TELEMETRY_SCREEN_TYPE(s_frsky_view
) == TELEMETRY_SCREEN_TYPE_NONE
) {
467 drawTelemetryTopBar();
469 if (s_frsky_view
< MAX_TELEMETRY_SCREENS
) {
470 return displayCustomTelemetryScreen(s_frsky_view
);
474 if (s_frsky_view
== TELEMETRY_VOLTAGES_SCREEN
) {
475 displayVoltagesScreen();
479 #if !defined(CPUARM) && defined(FRSKY_HUB)
481 displayAfterFlightScreen();
489 enum NavigationDirection
{
494 #define decrTelemetryScreen() direction = up
495 #define incrTelemetryScreen() direction = down
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
)
510 #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_LONG(KEY_PAGE)
511 #define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGE)
513 #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_FIRST(KEY_UP)
514 #define EVT_KEY_NEXT_VIEW EVT_KEY_FIRST(KEY_DOWN)
517 void menuViewTelemetryFrsky(event_t event
)
520 enum NavigationDirection direction
= none
;
524 case EVT_KEY_FIRST(KEY_EXIT
):
526 case EVT_KEY_LONG(KEY_EXIT
):
529 chainMenu(menuMainView
);
532 case EVT_KEY_PREVIOUS_VIEW
:
533 if (IS_KEY_LONG(EVT_KEY_PREVIOUS_VIEW
)) {
536 decrTelemetryScreen();
539 case EVT_KEY_NEXT_VIEW
:
540 incrTelemetryScreen();
544 case EVT_KEY_LONG(KEY_ENTER
):
546 POPUP_MENU_ADD_ITEM(STR_RESET_TELEMETRY
);
547 POPUP_MENU_ADD_ITEM(STR_RESET_FLIGHT
);
548 POPUP_MENU_START(onMainViewMenu
);
550 case EVT_KEY_FIRST(KEY_ENTER
):
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
)
569 if (displayTelemetryScreen()) {
574 drawTelemetryTopBar();
575 lcdDrawText(2*FW
, 3*FH
, "No Telemetry Screens");
578 if (!displayTelemetryScreen()) {
579 putEvent(event
== EVT_KEY_FIRST(KEY_UP
) ? event
: EVT_KEY_FIRST(KEY_DOWN
));
584 #undef EVT_KEY_PREVIOUS_VIEW
585 #undef EVT_KEY_NEXT_VIEW