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
13 #if defined(PLATFORM_ESP32)
15 extern WiFiMode_t wifiMode
;
18 // OLED specific header files.
21 #ifdef TARGET_TX_GHOST
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
)
33 u8g2
->drawXBMP(x
, y
, size
, size
, image
);
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
++)
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
);
51 u8g2
->drawXBMP((26 + i
), 0, 32, 32, ghost
);
52 u8g2
->drawXBMP((-31 + (i
* 4)), 0, 32, 32, elrs32
);
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
);
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
);
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
);
97 if (state
== SCREEN_BACKLIGHT_OFF
)
100 u8g2
->setPowerSave(true);
104 u8g2
->setPowerSave(false);
108 void OLEDDisplay::printScreenshot()
110 u8g2
->writeBufferXBM(*TxBackpack
);
113 void OLEDDisplay::displaySplashScreen()
116 #ifdef TARGET_TX_GHOST
119 #ifdef USE_OLED_SPI_SMALL
120 if (OPT_USE_OLED_SPI_SMALL
)
122 auto constexpr sz
= 128 * 32 / 8;
124 if (spi_flash_read(logo_image
, image
, sz
) == ESP_OK
)
126 u8g2
->drawXBM(0, 0, 128, 32, image
);
132 auto constexpr sz
= 128 * 64 / 8;
134 if (spi_flash_read(logo_image
, image
, sz
) == ESP_OK
)
136 u8g2
->drawXBM(0, 0, 128, 64, image
);
140 snprintf(buffer
, sizeof(buffer
), "ELRS-%.6s", version
);
141 u8g2
->setFont(u8g2_font_profont10_mr
);
142 drawCentered(60, buffer
);
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
)
151 String power
= getValue(STATE_POWER
, running_power_index
);
152 if (dynamic
|| power_index
!= running_power_index
)
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
);
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
);
189 void OLEDDisplay::displayMainMenu(menu_item_t menu
)
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]);
200 u8g2
->drawStr(0,20, main_menu_strings
[menu
][0]);
201 u8g2
->drawStr(0,50, main_menu_strings
[menu
][1]);
203 helperDrawImage(menu
);
207 void OLEDDisplay::displayValue(menu_item_t menu
, uint8_t value_index
)
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");
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
);
231 void OLEDDisplay::displayBLEConfirm()
233 // TODO: Put wifi image?
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");
245 u8g2
->drawStr(0,29, "PRESS TO START");
246 u8g2
->drawStr(0,59, "BLE JOYSTICK");
251 void OLEDDisplay::displayBLEStatus()
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");
266 u8g2
->drawStr(0,13, "BLUETOOTH");
267 u8g2
->drawStr(0,33, "GAMEPAD");
268 u8g2
->drawStr(0,63, "RUNNING");
273 void OLEDDisplay::displayWiFiConfirm()
275 // TODO: Put wifi image?
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");
287 u8g2
->drawStr(0,29, "PRESS TO ENTER");
288 u8g2
->drawStr(0,59, "WIFI UPDATE");
293 void OLEDDisplay::displayWiFiStatus()
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");
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");
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
);
324 u8g2
->drawStr(0,13, wifi_ap_ssid
);
325 u8g2
->drawStr(0,33, wifi_ap_password
);
326 u8g2
->drawStr(0,63, wifi_ap_address
);
333 void OLEDDisplay::displayBindConfirm()
335 // TODO: Put bind image?
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");
346 u8g2
->drawStr(0,29, "PRESS TO SEND");
347 u8g2
->drawStr(0,59, "BIND REQUEST");
352 void OLEDDisplay::displayBindStatus()
354 // TODO: Put bind image?
356 u8g2
->setFont(u8g2_font_t0_17_mr
);
357 if (OPT_USE_OLED_SPI_SMALL
)
359 drawCentered(15, "BINDING...");
363 drawCentered(29, "BINDING...");
368 void OLEDDisplay::displayRunning()
370 // TODO: Put wifi image?
372 u8g2
->setFont(u8g2_font_t0_17_mr
);
373 if (OPT_USE_OLED_SPI_SMALL
)
375 drawCentered(15, "RUNNING...");
379 drawCentered(29, "RUNNING...");
384 void OLEDDisplay::displaySending()
386 // TODO: Put wifi image?
388 u8g2
->setFont(u8g2_font_t0_17_mr
);
389 if (OPT_USE_OLED_SPI_SMALL
)
391 drawCentered(15, "SENDING...");
395 drawCentered(29, "SENDING...");
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;
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)
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
);
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
);
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
467 u8g2
->drawXBM(x_pos
, y_pos
, 32, 22, rate_img32
);
470 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, switch_img32
);
473 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, antenna_img32
);
476 case STATE_POWER_MAX
:
477 case STATE_POWER_DYNAMIC
:
478 u8g2
->drawXBM(x_pos
, y_pos
, 25, 25, power_img32
);
480 case STATE_TELEMETRY
:
481 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, ratio_img32
);
483 case STATE_POWERSAVE
:
484 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, powersaving_img32
);
487 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, fan_img32
);
490 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, joystick_img32
);
494 case STATE_VTX_CHANNEL
:
495 case STATE_VTX_POWER
:
496 case STATE_VTX_PITMODE
:
498 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, vtx_img32
);
501 u8g2
->drawXBM(x_pos
, y_pos
, 24, 22, wifi_img32
);
504 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, bind_img32
);
508 u8g2
->drawXBM(x_pos
, y_pos
, 24, 22, wifi_img32
);
511 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, rxwifi_img32
);
513 case STATE_WIFI_BACKPACK
:
514 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, backpack_img32
);
517 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, vrxwifi_img32
);
526 // Adjust these to move them around on the screen
532 u8g2
->drawXBM(x_pos
, y_pos
, 64, 44, rate_img64
);
535 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, switch_img64
);
538 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, antenna_img64
);
541 case STATE_POWER_MAX
:
542 case STATE_POWER_DYNAMIC
:
543 u8g2
->drawXBM(x_pos
, y_pos
, 50, 50, power_img64
);
545 case STATE_TELEMETRY
:
546 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, ratio_img64
);
548 case STATE_POWERSAVE
:
549 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, powersaving_img64
);
552 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, fan_img64
);
555 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64-5, joystick_img64
);
559 case STATE_VTX_CHANNEL
:
560 case STATE_VTX_POWER
:
561 case STATE_VTX_PITMODE
:
563 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, vtx_img64
);
566 u8g2
->drawXBM(x_pos
, y_pos
, 48, 44, wifi_img64
);
569 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, bind_img64
);
573 u8g2
->drawXBM(x_pos
, y_pos
, 48, 44, wifi_img64
);
576 u8g2
->drawXBM(x_pos
, y_pos
-5, 64, 64, rxwifi_img64
);
578 case STATE_WIFI_BACKPACK
:
579 u8g2
->drawXBM(x_pos
, y_pos
-5, 64, 64, backpack_img64
);
582 u8g2
->drawXBM(x_pos
, y_pos
-5, 64, 64, vrxwifi_img64
);