Add linkstats to module display (#2843)
[ExpressLRS.git] / src / lib / SCREEN / OLED / oleddisplay.cpp
blobb4d4615103fdf5af2cec725787cb7a04e3617ec3
1 #if defined(USE_OLED_SPI) || defined(USE_OLED_SPI_SMALL) || defined(USE_OLED_I2C) // This code will not be used if the hardware does not have a OLED display. Maybe a better way to blacklist it in platformio.ini?
3 #include <U8g2lib.h> // Needed for the OLED drivers, this is a arduino package. It is maintained by platformIO
5 #include "oleddisplay.h"
7 #include "XBMStrings.h" // Contains all the ELRS logos and animations for the UI
8 #include "options.h"
9 #include "logging.h"
10 #include "common.h"
11 #include "CRSF.h"
13 #if defined(PLATFORM_ESP32)
14 #include "WiFi.h"
15 extern WiFiMode_t wifiMode;
16 #endif
18 // OLED specific header files.
19 U8G2 *u8g2;
21 #ifdef TARGET_TX_GHOST
22 /**
23 * helper function is used to draw xbmp on the OLED.
24 * x = x position of the image
25 * y = y position of the image
26 * size = demensions of the box size x size, this only works for square images 1:1
27 * image = XBM character string
29 #ifndef TARGET_TX_GHOST_LITE
30 static void helper(int x, int y, int size, const unsigned char *image)
32 u8g2->clearBuffer();
33 u8g2->drawXBMP(x, y, size, size, image);
34 u8g2->sendBuffer();
36 #endif
38 /**
39 * ghostChase will only be called for ghost TX hardware.
41 static void ghostChase()
43 // Using i < 16 and (i*4) to get 64 total pixels. Change to i < 32 (i*2) to slow animation.
44 for (int i = 0; i < 20; i++)
46 u8g2->clearBuffer();
47 #ifndef TARGET_TX_GHOST_LITE
48 u8g2->drawXBMP((26 + i), 16, 32, 32, ghost);
49 u8g2->drawXBMP((-31 + (i * 4)), 16, 32, 32, elrs32);
50 #else
51 u8g2->drawXBMP((26 + i), 0, 32, 32, ghost);
52 u8g2->drawXBMP((-31 + (i * 4)), 0, 32, 32, elrs32);
53 #endif
54 u8g2->sendBuffer();
56 /**
57 * Animation for the ghost logo expanding in the center of the screen.
58 * helper function just draw's the XBM strings.
60 #ifndef TARGET_TX_GHOST_LITE
61 helper(38, 12, 40, elrs40);
62 helper(36, 8, 48, elrs48);
63 helper(34, 4, 56, elrs56);
64 helper(32, 0, 64, elrs64);
65 #endif
67 #endif
69 static void helperDrawImage(menu_item_t menu);
70 static void drawCentered(u8g2_int_t y, const char *str)
72 u8g2_int_t x = (u8g2->getDisplayWidth() - u8g2->getStrWidth(str)) / 2;
73 u8g2->drawStr(x, y, str);
76 void OLEDDisplay::init()
78 if (OPT_USE_OLED_SPI_SMALL)
79 u8g2 = new U8G2_SSD1306_128X32_UNIVISION_F_4W_SW_SPI(OPT_OLED_REVERSED ? U8G2_R2 : U8G2_R0, GPIO_PIN_OLED_SCK, GPIO_PIN_OLED_MOSI, GPIO_PIN_OLED_CS, GPIO_PIN_OLED_DC, GPIO_PIN_OLED_RST);
80 else if (OPT_USE_OLED_SPI)
81 u8g2 = new U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI(OPT_OLED_REVERSED ? U8G2_R2 : U8G2_R0, GPIO_PIN_OLED_SCK, GPIO_PIN_OLED_MOSI, GPIO_PIN_OLED_CS, GPIO_PIN_OLED_DC, GPIO_PIN_OLED_RST);
82 else if (OPT_USE_OLED_I2C)
83 u8g2 = new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(OPT_OLED_REVERSED ? U8G2_R2 : U8G2_R0, GPIO_PIN_OLED_RST, GPIO_PIN_OLED_SCK, GPIO_PIN_OLED_SDA);
85 u8g2->begin();
86 u8g2->clearBuffer();
89 void OLEDDisplay::doScreenBackLight(screen_backlight_t state)
91 #ifdef GPIO_PIN_OLED_BL
92 if (GPIO_PIN_OLED_BL != UNDEF_PIN)
94 digitalWrite(GPIO_PIN_OLED_BL, state);
96 #endif
97 if (state == SCREEN_BACKLIGHT_OFF)
99 u8g2->clearDisplay();
100 u8g2->setPowerSave(true);
102 else
104 u8g2->setPowerSave(false);
108 void OLEDDisplay::printScreenshot()
110 u8g2->writeBufferXBM(*TxBackpack);
113 void OLEDDisplay::displaySplashScreen()
115 u8g2->clearBuffer();
116 #ifdef TARGET_TX_GHOST
117 ghostChase();
118 #else
119 #ifdef USE_OLED_SPI_SMALL
120 if (OPT_USE_OLED_SPI_SMALL)
122 auto constexpr sz = 128 * 32 / 8;
123 uint8_t image[sz];
124 if (spi_flash_read(logo_image, image, sz) == ESP_OK)
126 u8g2->drawXBM(0, 0, 128, 32, image);
129 else
130 #endif
132 auto constexpr sz = 128 * 64 / 8;
133 uint8_t image[sz];
134 if (spi_flash_read(logo_image, image, sz) == ESP_OK)
136 u8g2->drawXBM(0, 0, 128, 64, image);
139 char buffer[50];
140 snprintf(buffer, sizeof(buffer), "ELRS-%.6s", version);
141 u8g2->setFont(u8g2_font_profont10_mr);
142 drawCentered(60, buffer);
144 #endif
145 u8g2->sendBuffer();
148 void OLEDDisplay::displayIdleScreen(uint8_t changed, uint8_t rate_index, uint8_t power_index, uint8_t ratio_index, uint8_t motion_index, uint8_t fan_index, bool dynamic, uint8_t running_power_index, uint8_t temperature, message_index_t message_index)
150 u8g2->clearBuffer();
151 String power = getValue(STATE_POWER, running_power_index);
152 if (dynamic || power_index != running_power_index)
154 power += " *";
157 u8g2->setFont(u8g2_font_t0_15_mr);
158 if (connectionState == radioFailed)
160 drawCentered(15, "BAD");
161 drawCentered(32, "RADIO");
163 else if (connectionState == noCrossfire)
165 drawCentered(15, "NO");
166 drawCentered(32, "HANDSET");
168 else if (OPT_USE_OLED_SPI_SMALL)
170 u8g2->drawStr(0, 15, getValue(STATE_PACKET, rate_index));
171 u8g2->drawStr(70, 15, getValue(STATE_TELEMETRY_CURR, ratio_index));
172 u8g2->drawStr(0, 32, power.c_str());
173 u8g2->drawStr(70, 32, version);
175 else
177 u8g2->drawStr(0, 13, message_string[message_index]);
178 u8g2->drawStr(0, 45, getValue(STATE_PACKET, rate_index));
179 u8g2->drawStr(70, 45, getValue(STATE_TELEMETRY_CURR, ratio_index));
180 u8g2->drawStr(0, 60, power.c_str());
181 u8g2->setFont(u8g2_font_profont10_mr);
182 u8g2->drawStr(70, 56, "TLM");
183 u8g2->drawStr(0, 27, "Ver: ");
184 u8g2->drawStr(38, 27, version);
186 u8g2->sendBuffer();
189 void OLEDDisplay::displayMainMenu(menu_item_t menu)
191 u8g2->clearBuffer();
192 u8g2->setFont(u8g2_font_t0_17_mr);
193 if (OPT_USE_OLED_SPI_SMALL)
195 u8g2->drawStr(0,15, main_menu_strings[menu][0]);
196 u8g2->drawStr(0,32, main_menu_strings[menu][1]);
198 else
200 u8g2->drawStr(0,20, main_menu_strings[menu][0]);
201 u8g2->drawStr(0,50, main_menu_strings[menu][1]);
203 helperDrawImage(menu);
204 u8g2->sendBuffer();
207 void OLEDDisplay::displayValue(menu_item_t menu, uint8_t value_index)
209 u8g2->clearBuffer();
210 u8g2->setFont(u8g2_font_9x15_t_symbols);
211 String val = String(getValue(menu, value_index));
212 val.replace("!+", "\u2191");
213 val.replace("!-", "\u2193");
214 if (OPT_USE_OLED_SPI_SMALL)
216 u8g2->drawStr(0,15, val.c_str());
217 u8g2->setFont(u8g2_font_profont10_mr);
218 u8g2->drawStr(0,60, "PRESS TO CONFIRM");
220 else
222 u8g2->drawUTF8(0,20, val.c_str());
223 u8g2->setFont(u8g2_font_profont10_mr);
224 u8g2->drawStr(0,44, "PRESS TO");
225 u8g2->drawStr(0,56, "CONFIRM");
227 helperDrawImage(menu);
228 u8g2->sendBuffer();
231 void OLEDDisplay::displayBLEConfirm()
233 // TODO: Put wifi image?
234 u8g2->clearBuffer();
236 u8g2->setFont(u8g2_font_t0_17_mr);
237 if (OPT_USE_OLED_SPI_SMALL)
239 u8g2->drawStr(0,15, "PRESS TO");
240 u8g2->drawStr(70,15, "START BLUETOOTH");
241 u8g2->drawStr(0,32, "JOYSTICK");
243 else
245 u8g2->drawStr(0,29, "PRESS TO START");
246 u8g2->drawStr(0,59, "BLE JOYSTICK");
248 u8g2->sendBuffer();
251 void OLEDDisplay::displayBLEStatus()
253 u8g2->clearBuffer();
255 // TODO: Add a fancy joystick symbol like the cool TFT peeps
257 u8g2->setFont(u8g2_font_t0_17_mr);
258 if (OPT_USE_OLED_SPI_SMALL)
260 u8g2->drawStr(0,15, "BLUETOOTH");
261 u8g2->drawStr(70,15, "GAMEPAD");
262 u8g2->drawStr(0,32, "RUNNING");
264 else
266 u8g2->drawStr(0,13, "BLUETOOTH");
267 u8g2->drawStr(0,33, "GAMEPAD");
268 u8g2->drawStr(0,63, "RUNNING");
270 u8g2->sendBuffer();
273 void OLEDDisplay::displayWiFiConfirm()
275 // TODO: Put wifi image?
276 u8g2->clearBuffer();
278 u8g2->setFont(u8g2_font_t0_17_mr);
279 if (OPT_USE_OLED_SPI_SMALL)
281 u8g2->drawStr(0,15, "PRESS TO");
282 u8g2->drawStr(70,15, "ENTER WIFI");
283 u8g2->drawStr(0,32, "UPDATE");
285 else
287 u8g2->drawStr(0,29, "PRESS TO ENTER");
288 u8g2->drawStr(0,59, "WIFI UPDATE");
290 u8g2->sendBuffer();
293 void OLEDDisplay::displayWiFiStatus()
295 u8g2->clearBuffer();
296 #if defined(PLATFORM_ESP32)
297 // TODO: Add a fancy wifi symbol like the cool TFT peeps
299 u8g2->setFont(u8g2_font_t0_17_mr);
300 if (wifiMode == WIFI_STA) {
301 if (OPT_USE_OLED_SPI_SMALL)
303 u8g2->drawStr(0,15, "open http://");
304 u8g2->drawStr(70,15, (String(wifi_hostname)+".local").c_str());
305 u8g2->drawStr(0,32, "by browser");
307 else
309 u8g2->drawStr(0,13, "open http://");
310 u8g2->drawStr(0,33, (String(wifi_hostname)+".local").c_str());
311 u8g2->drawStr(0,63, "by browser");
314 else
316 if (OPT_USE_OLED_SPI_SMALL)
318 u8g2->drawStr(0,15, wifi_ap_ssid);
319 u8g2->drawStr(70,15, wifi_ap_password);
320 u8g2->drawStr(0,32, wifi_ap_address);
322 else
324 u8g2->drawStr(0,13, wifi_ap_ssid);
325 u8g2->drawStr(0,33, wifi_ap_password);
326 u8g2->drawStr(0,63, wifi_ap_address);
329 #endif
330 u8g2->sendBuffer();
333 void OLEDDisplay::displayBindConfirm()
335 // TODO: Put bind image?
336 u8g2->clearBuffer();
337 u8g2->setFont(u8g2_font_t0_17_mr);
338 if (OPT_USE_OLED_SPI_SMALL)
340 u8g2->drawStr(0,15, "PRESS TO");
341 u8g2->drawStr(70,15 , "SEND BIND");
342 u8g2->drawStr(0,32, "REQUEST");
344 else
346 u8g2->drawStr(0,29, "PRESS TO SEND");
347 u8g2->drawStr(0,59, "BIND REQUEST");
349 u8g2->sendBuffer();
352 void OLEDDisplay::displayBindStatus()
354 // TODO: Put bind image?
355 u8g2->clearBuffer();
356 u8g2->setFont(u8g2_font_t0_17_mr);
357 if (OPT_USE_OLED_SPI_SMALL)
359 drawCentered(15, "BINDING...");
361 else
363 drawCentered(29, "BINDING...");
365 u8g2->sendBuffer();
368 void OLEDDisplay::displayRunning()
370 // TODO: Put wifi image?
371 u8g2->clearBuffer();
372 u8g2->setFont(u8g2_font_t0_17_mr);
373 if (OPT_USE_OLED_SPI_SMALL)
375 drawCentered(15, "RUNNING...");
377 else
379 drawCentered(29, "RUNNING...");
381 u8g2->sendBuffer();
384 void OLEDDisplay::displaySending()
386 // TODO: Put wifi image?
387 u8g2->clearBuffer();
388 u8g2->setFont(u8g2_font_t0_17_mr);
389 if (OPT_USE_OLED_SPI_SMALL)
391 drawCentered(15, "SENDING...");
393 else
395 drawCentered(29, "SENDING...");
397 u8g2->sendBuffer();
400 void OLEDDisplay::displayLinkstats()
402 constexpr int16_t LINKSTATS_COL_FIRST = 0;
403 constexpr int16_t LINKSTATS_COL_SECOND = 32;
404 constexpr int16_t LINKSTATS_COL_THIRD = 85;
406 constexpr int16_t LINKSTATS_ROW_FIRST = 10;
407 constexpr int16_t LINKSTATS_ROW_SECOND = 20;
408 constexpr int16_t LINKSTATS_ROW_THIRD = 30;
409 constexpr int16_t LINKSTATS_ROW_FOURTH = 40;
410 constexpr int16_t LINKSTATS_ROW_FIFTH = 50;
412 u8g2->clearBuffer();
413 u8g2->setFont(u8g2_font_profont10_mr);
415 u8g2->drawStr(LINKSTATS_COL_FIRST, LINKSTATS_ROW_SECOND, "LQ");
416 u8g2->drawStr(LINKSTATS_COL_FIRST, LINKSTATS_ROW_THIRD, "RSSI");
417 u8g2->drawStr(LINKSTATS_COL_FIRST, LINKSTATS_ROW_FOURTH, "SNR");
418 u8g2->drawStr(LINKSTATS_COL_FIRST, LINKSTATS_ROW_FIFTH, "Ant");
420 u8g2->drawStr(LINKSTATS_COL_SECOND, LINKSTATS_ROW_FIRST, "Uplink");
421 u8g2->setCursor(LINKSTATS_COL_SECOND, LINKSTATS_ROW_SECOND);
422 u8g2->print(CRSF::LinkStatistics.uplink_Link_quality);
423 u8g2->setCursor(LINKSTATS_COL_SECOND, LINKSTATS_ROW_THIRD);
424 u8g2->print((int8_t)CRSF::LinkStatistics.uplink_RSSI_1);
425 if (CRSF::LinkStatistics.uplink_RSSI_2 != 0)
427 u8g2->print('/');
428 u8g2->print((int8_t)CRSF::LinkStatistics.uplink_RSSI_2);
431 u8g2->drawStr(LINKSTATS_COL_THIRD, LINKSTATS_ROW_FIRST, "Downlink");
432 u8g2->setCursor(LINKSTATS_COL_THIRD, LINKSTATS_ROW_SECOND);
433 u8g2->print(CRSF::LinkStatistics.downlink_Link_quality);
434 u8g2->setCursor(LINKSTATS_COL_THIRD, LINKSTATS_ROW_THIRD);
435 u8g2->print((int8_t)CRSF::LinkStatistics.downlink_RSSI_1);
436 if (isDualRadio())
438 u8g2->print('/');
439 u8g2->print((int8_t)CRSF::LinkStatistics.downlink_RSSI_2);
442 if (!OPT_USE_OLED_SPI_SMALL)
444 u8g2->setCursor(LINKSTATS_COL_SECOND, LINKSTATS_ROW_FOURTH);
445 u8g2->print((int8_t)CRSF::LinkStatistics.uplink_SNR);
446 u8g2->setCursor(LINKSTATS_COL_THIRD, LINKSTATS_ROW_FOURTH);
447 u8g2->print((int8_t)CRSF::LinkStatistics.downlink_SNR);
448 u8g2->setCursor(LINKSTATS_COL_SECOND, LINKSTATS_ROW_FIFTH);
449 u8g2->print(CRSF::LinkStatistics.active_antenna);
452 u8g2->sendBuffer();
455 // helpers
456 static void helperDrawImage(menu_item_t menu)
458 if (OPT_USE_OLED_SPI_SMALL)
461 // Adjust these to move them around on the screen
462 int x_pos = 65;
463 int y_pos = 5;
465 switch(menu){
466 case STATE_PACKET:
467 u8g2->drawXBM(x_pos, y_pos, 32, 22, rate_img32);
468 break;
469 case STATE_SWITCH:
470 u8g2->drawXBM(x_pos, y_pos, 32, 32, switch_img32);
471 break;
472 case STATE_ANTENNA:
473 u8g2->drawXBM(x_pos, y_pos, 32, 32, antenna_img32);
474 break;
475 case STATE_POWER:
476 case STATE_POWER_MAX:
477 case STATE_POWER_DYNAMIC:
478 u8g2->drawXBM(x_pos, y_pos, 25, 25, power_img32);
479 break;
480 case STATE_TELEMETRY:
481 u8g2->drawXBM(x_pos, y_pos, 32, 32, ratio_img32);
482 break;
483 case STATE_POWERSAVE:
484 u8g2->drawXBM(x_pos, y_pos, 32, 32, powersaving_img32);
485 break;
486 case STATE_SMARTFAN:
487 u8g2->drawXBM(x_pos, y_pos, 32, 32, fan_img32);
488 break;
489 case STATE_JOYSTICK:
490 u8g2->drawXBM(x_pos, y_pos-5, 32, 32, joystick_img32);
491 break;
492 case STATE_VTX:
493 case STATE_VTX_BAND:
494 case STATE_VTX_CHANNEL:
495 case STATE_VTX_POWER:
496 case STATE_VTX_PITMODE:
497 case STATE_VTX_SEND:
498 u8g2->drawXBM(x_pos, y_pos, 32, 32, vtx_img32);
499 break;
500 case STATE_WIFI:
501 u8g2->drawXBM(x_pos, y_pos, 24, 22, wifi_img32);
502 break;
503 case STATE_BIND:
504 u8g2->drawXBM(x_pos, y_pos, 32, 32, bind_img32);
505 break;
507 case STATE_WIFI_TX:
508 u8g2->drawXBM(x_pos, y_pos, 24, 22, wifi_img32);
509 break;
510 case STATE_WIFI_RX:
511 u8g2->drawXBM(x_pos, y_pos-5, 32, 32, rxwifi_img32);
512 break;
513 case STATE_WIFI_BACKPACK:
514 u8g2->drawXBM(x_pos, y_pos-5, 32, 32, backpack_img32);
515 break;
516 case STATE_WIFI_VRX:
517 u8g2->drawXBM(x_pos, y_pos-5, 32, 32, vrxwifi_img32);
518 break;
520 default:
521 break;
524 else
526 // Adjust these to move them around on the screen
527 int x_pos = 65;
528 int y_pos = 5;
530 switch(menu){
531 case STATE_PACKET:
532 u8g2->drawXBM(x_pos, y_pos, 64, 44, rate_img64);
533 break;
534 case STATE_SWITCH:
535 u8g2->drawXBM(x_pos, y_pos, 64, 64, switch_img64);
536 break;
537 case STATE_ANTENNA:
538 u8g2->drawXBM(x_pos, y_pos, 64, 64, antenna_img64);
539 break;
540 case STATE_POWER:
541 case STATE_POWER_MAX:
542 case STATE_POWER_DYNAMIC:
543 u8g2->drawXBM(x_pos, y_pos, 50, 50, power_img64);
544 break;
545 case STATE_TELEMETRY:
546 u8g2->drawXBM(x_pos, y_pos, 64, 64, ratio_img64);
547 break;
548 case STATE_POWERSAVE:
549 u8g2->drawXBM(x_pos, y_pos, 64, 64, powersaving_img64);
550 break;
551 case STATE_SMARTFAN:
552 u8g2->drawXBM(x_pos, y_pos, 64, 64, fan_img64);
553 break;
554 case STATE_JOYSTICK:
555 u8g2->drawXBM(x_pos, y_pos, 64, 64-5, joystick_img64);
556 break;
557 case STATE_VTX:
558 case STATE_VTX_BAND:
559 case STATE_VTX_CHANNEL:
560 case STATE_VTX_POWER:
561 case STATE_VTX_PITMODE:
562 case STATE_VTX_SEND:
563 u8g2->drawXBM(x_pos, y_pos, 64, 64, vtx_img64);
564 break;
565 case STATE_WIFI:
566 u8g2->drawXBM(x_pos, y_pos, 48, 44, wifi_img64);
567 break;
568 case STATE_BIND:
569 u8g2->drawXBM(x_pos, y_pos, 64, 64, bind_img64);
570 break;
572 case STATE_WIFI_TX:
573 u8g2->drawXBM(x_pos, y_pos, 48, 44, wifi_img64);
574 break;
575 case STATE_WIFI_RX:
576 u8g2->drawXBM(x_pos, y_pos-5, 64, 64, rxwifi_img64);
577 break;
578 case STATE_WIFI_BACKPACK:
579 u8g2->drawXBM(x_pos, y_pos-5, 64, 64, backpack_img64);
580 break;
581 case STATE_WIFI_VRX:
582 u8g2->drawXBM(x_pos, y_pos-5, 64, 64, vrxwifi_img64);
583 break;
585 default:
586 break;
591 #endif