1 #if defined(PLATFORM_ESP32)
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
14 extern WiFiMode_t wifiMode
;
16 // OLED specific header files.
19 static void helperDrawImage(menu_item_t menu
);
20 static void drawCentered(u8g2_int_t y
, const char *str
)
22 u8g2_int_t x
= (u8g2
->getDisplayWidth() - u8g2
->getStrWidth(str
)) / 2;
23 u8g2
->drawStr(x
, y
, str
);
26 void OLEDDisplay::init()
28 if (OPT_HAS_OLED_SPI_SMALL
)
29 u8g2
= new U8G2_SSD1306_128X32_UNIVISION_F_4W_SW_SPI(OPT_SCREEN_REVERSED
? U8G2_R2
: U8G2_R0
, GPIO_PIN_SCREEN_SCK
, GPIO_PIN_SCREEN_MOSI
, GPIO_PIN_SCREEN_CS
, GPIO_PIN_SCREEN_DC
, GPIO_PIN_SCREEN_RST
);
30 else if (OPT_HAS_OLED_SPI
)
31 u8g2
= new U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI(OPT_SCREEN_REVERSED
? U8G2_R2
: U8G2_R0
, GPIO_PIN_SCREEN_SCK
, GPIO_PIN_SCREEN_MOSI
, GPIO_PIN_SCREEN_CS
, GPIO_PIN_SCREEN_DC
, GPIO_PIN_SCREEN_RST
);
32 else if (OPT_HAS_OLED_I2C
)
33 u8g2
= new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(OPT_SCREEN_REVERSED
? U8G2_R2
: U8G2_R0
, GPIO_PIN_SCREEN_RST
, GPIO_PIN_SCREEN_SCK
, GPIO_PIN_SCREEN_SDA
);
39 void OLEDDisplay::doScreenBackLight(screen_backlight_t state
)
41 if (GPIO_PIN_SCREEN_BL
!= UNDEF_PIN
)
43 digitalWrite(GPIO_PIN_SCREEN_BL
, state
);
45 if (state
== SCREEN_BACKLIGHT_OFF
)
48 u8g2
->setPowerSave(true);
52 u8g2
->setPowerSave(false);
56 void OLEDDisplay::printScreenshot()
58 u8g2
->writeBufferXBM(*TxBackpack
);
61 void OLEDDisplay::displaySplashScreen()
64 if (OPT_HAS_OLED_SPI_SMALL
)
66 auto constexpr sz
= 128 * 32 / 8;
68 if (spi_flash_read(logo_image
, image
, sz
) == ESP_OK
)
70 u8g2
->drawXBM(0, 0, 128, 32, image
);
75 auto constexpr sz
= 128 * 64 / 8;
77 if (spi_flash_read(logo_image
, image
, sz
) == ESP_OK
)
79 u8g2
->drawXBM(0, 0, 128, 64, image
);
83 snprintf(buffer
, sizeof(buffer
), "ELRS-%.6s", version
);
84 u8g2
->setFont(u8g2_font_profont10_mr
);
85 drawCentered(60, buffer
);
90 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
)
93 String power
= getValue(STATE_POWER
, running_power_index
);
94 if (dynamic
|| power_index
!= running_power_index
)
99 u8g2
->setFont(u8g2_font_t0_15_mr
);
100 if (connectionState
== radioFailed
)
102 drawCentered(15, "BAD");
103 drawCentered(32, "RADIO");
105 else if (connectionState
== noCrossfire
)
107 drawCentered(15, "NO");
108 drawCentered(32, "HANDSET");
110 else if (OPT_HAS_OLED_SPI_SMALL
)
112 u8g2
->drawStr(0, 15, getValue(STATE_PACKET
, rate_index
));
113 u8g2
->drawStr(70, 15, getValue(STATE_TELEMETRY_CURR
, ratio_index
));
114 u8g2
->drawStr(0, 32, power
.c_str());
115 u8g2
->drawStr(70, 32, version
);
119 u8g2
->drawStr(0, 13, message_string
[message_index
]);
120 u8g2
->drawStr(0, 45, getValue(STATE_PACKET
, rate_index
));
121 u8g2
->drawStr(70, 45, getValue(STATE_TELEMETRY_CURR
, ratio_index
));
122 u8g2
->drawStr(0, 60, power
.c_str());
123 u8g2
->setFont(u8g2_font_profont10_mr
);
124 u8g2
->drawStr(70, 56, "TLM");
125 u8g2
->drawStr(0, 27, "Ver: ");
126 u8g2
->drawStr(38, 27, version
);
131 void OLEDDisplay::displayMainMenu(menu_item_t menu
)
134 u8g2
->setFont(u8g2_font_t0_17_mr
);
135 if (OPT_HAS_OLED_SPI_SMALL
)
137 u8g2
->drawStr(0,15, main_menu_strings
[menu
][0]);
138 u8g2
->drawStr(0,32, main_menu_strings
[menu
][1]);
142 u8g2
->drawStr(0,20, main_menu_strings
[menu
][0]);
143 u8g2
->drawStr(0,50, main_menu_strings
[menu
][1]);
145 helperDrawImage(menu
);
149 void OLEDDisplay::displayValue(menu_item_t menu
, uint8_t value_index
)
152 u8g2
->setFont(u8g2_font_9x15_t_symbols
);
153 String val
= String(getValue(menu
, value_index
));
154 val
.replace("!+", "\u2191");
155 val
.replace("!-", "\u2193");
156 if (OPT_HAS_OLED_SPI_SMALL
)
158 u8g2
->drawStr(0,15, val
.c_str());
159 u8g2
->setFont(u8g2_font_profont10_mr
);
160 u8g2
->drawStr(0,60, "PRESS TO CONFIRM");
164 u8g2
->drawUTF8(0,20, val
.c_str());
165 u8g2
->setFont(u8g2_font_profont10_mr
);
166 u8g2
->drawStr(0,44, "PRESS TO");
167 u8g2
->drawStr(0,56, "CONFIRM");
169 helperDrawImage(menu
);
173 void OLEDDisplay::displayBLEConfirm()
175 // TODO: Put wifi image?
178 u8g2
->setFont(u8g2_font_t0_17_mr
);
179 if (OPT_HAS_OLED_SPI_SMALL
)
181 u8g2
->drawStr(0,15, "PRESS TO");
182 u8g2
->drawStr(70,15, "START BLUETOOTH");
183 u8g2
->drawStr(0,32, "JOYSTICK");
187 u8g2
->drawStr(0,29, "PRESS TO START");
188 u8g2
->drawStr(0,59, "BLE JOYSTICK");
193 void OLEDDisplay::displayBLEStatus()
197 // TODO: Add a fancy joystick symbol like the cool TFT peeps
199 u8g2
->setFont(u8g2_font_t0_17_mr
);
200 if (OPT_HAS_OLED_SPI_SMALL
)
202 u8g2
->drawStr(0,15, "BLUETOOTH");
203 u8g2
->drawStr(70,15, "GAMEPAD");
204 u8g2
->drawStr(0,32, "RUNNING");
208 u8g2
->drawStr(0,13, "BLUETOOTH");
209 u8g2
->drawStr(0,33, "GAMEPAD");
210 u8g2
->drawStr(0,63, "RUNNING");
215 void OLEDDisplay::displayWiFiConfirm()
217 // TODO: Put wifi image?
220 u8g2
->setFont(u8g2_font_t0_17_mr
);
221 if (OPT_HAS_OLED_SPI_SMALL
)
223 u8g2
->drawStr(0,15, "PRESS TO");
224 u8g2
->drawStr(70,15, "ENTER WIFI");
225 u8g2
->drawStr(0,32, "UPDATE");
229 u8g2
->drawStr(0,29, "PRESS TO ENTER");
230 u8g2
->drawStr(0,59, "WIFI UPDATE");
235 void OLEDDisplay::displayWiFiStatus()
238 // TODO: Add a fancy wifi symbol like the cool TFT peeps
240 u8g2
->setFont(u8g2_font_t0_17_mr
);
241 if (wifiMode
== WIFI_STA
) {
242 if (OPT_HAS_OLED_SPI_SMALL
)
244 u8g2
->drawStr(0,15, "open http://");
245 u8g2
->drawStr(70,15, (String(wifi_hostname
)+".local").c_str());
246 u8g2
->drawStr(0,32, "by browser");
250 u8g2
->drawStr(0,13, "open http://");
251 u8g2
->drawStr(0,33, (String(wifi_hostname
)+".local").c_str());
252 u8g2
->drawStr(0,63, "by browser");
257 if (OPT_HAS_OLED_SPI_SMALL
)
259 u8g2
->drawStr(0,15, wifi_ap_ssid
);
260 u8g2
->drawStr(70,15, wifi_ap_password
);
261 u8g2
->drawStr(0,32, wifi_ap_address
);
265 u8g2
->drawStr(0,13, wifi_ap_ssid
);
266 u8g2
->drawStr(0,33, wifi_ap_password
);
267 u8g2
->drawStr(0,63, wifi_ap_address
);
273 void OLEDDisplay::displayBindConfirm()
275 // TODO: Put bind image?
277 u8g2
->setFont(u8g2_font_t0_17_mr
);
278 if (OPT_HAS_OLED_SPI_SMALL
)
280 u8g2
->drawStr(0,15, "PRESS TO");
281 u8g2
->drawStr(70,15 , "SEND BIND");
282 u8g2
->drawStr(0,32, "REQUEST");
286 u8g2
->drawStr(0,29, "PRESS TO SEND");
287 u8g2
->drawStr(0,59, "BIND REQUEST");
292 void OLEDDisplay::displayBindStatus()
294 // TODO: Put bind image?
296 u8g2
->setFont(u8g2_font_t0_17_mr
);
297 if (OPT_HAS_OLED_SPI_SMALL
)
299 drawCentered(15, "BINDING...");
303 drawCentered(29, "BINDING...");
308 void OLEDDisplay::displayRunning()
310 // TODO: Put wifi image?
312 u8g2
->setFont(u8g2_font_t0_17_mr
);
313 if (OPT_HAS_OLED_SPI_SMALL
)
315 drawCentered(15, "RUNNING...");
319 drawCentered(29, "RUNNING...");
324 void OLEDDisplay::displaySending()
326 // TODO: Put wifi image?
328 u8g2
->setFont(u8g2_font_t0_17_mr
);
329 if (OPT_HAS_OLED_SPI_SMALL
)
331 drawCentered(15, "SENDING...");
335 drawCentered(29, "SENDING...");
340 void OLEDDisplay::displayLinkstats()
342 constexpr int16_t LINKSTATS_COL_FIRST
= 0;
343 constexpr int16_t LINKSTATS_COL_SECOND
= 32;
344 constexpr int16_t LINKSTATS_COL_THIRD
= 85;
346 constexpr int16_t LINKSTATS_ROW_FIRST
= 10;
347 constexpr int16_t LINKSTATS_ROW_SECOND
= 20;
348 constexpr int16_t LINKSTATS_ROW_THIRD
= 30;
349 constexpr int16_t LINKSTATS_ROW_FOURTH
= 40;
350 constexpr int16_t LINKSTATS_ROW_FIFTH
= 50;
353 u8g2
->setFont(u8g2_font_profont10_mr
);
355 u8g2
->drawStr(LINKSTATS_COL_FIRST
, LINKSTATS_ROW_SECOND
, "LQ");
356 u8g2
->drawStr(LINKSTATS_COL_FIRST
, LINKSTATS_ROW_THIRD
, "RSSI");
357 u8g2
->drawStr(LINKSTATS_COL_FIRST
, LINKSTATS_ROW_FOURTH
, "SNR");
358 u8g2
->drawStr(LINKSTATS_COL_FIRST
, LINKSTATS_ROW_FIFTH
, "Ant");
360 u8g2
->drawStr(LINKSTATS_COL_SECOND
, LINKSTATS_ROW_FIRST
, "Uplink");
361 u8g2
->setCursor(LINKSTATS_COL_SECOND
, LINKSTATS_ROW_SECOND
);
362 u8g2
->print(CRSF::LinkStatistics
.uplink_Link_quality
);
363 u8g2
->setCursor(LINKSTATS_COL_SECOND
, LINKSTATS_ROW_THIRD
);
364 u8g2
->print((int8_t)CRSF::LinkStatistics
.uplink_RSSI_1
);
365 if (CRSF::LinkStatistics
.uplink_RSSI_2
!= 0)
368 u8g2
->print((int8_t)CRSF::LinkStatistics
.uplink_RSSI_2
);
371 u8g2
->drawStr(LINKSTATS_COL_THIRD
, LINKSTATS_ROW_FIRST
, "Downlink");
372 u8g2
->setCursor(LINKSTATS_COL_THIRD
, LINKSTATS_ROW_SECOND
);
373 u8g2
->print(CRSF::LinkStatistics
.downlink_Link_quality
);
374 u8g2
->setCursor(LINKSTATS_COL_THIRD
, LINKSTATS_ROW_THIRD
);
375 u8g2
->print((int8_t)CRSF::LinkStatistics
.downlink_RSSI_1
);
379 u8g2
->print((int8_t)CRSF::LinkStatistics
.downlink_RSSI_2
);
382 if (!OPT_HAS_OLED_SPI_SMALL
)
384 u8g2
->setCursor(LINKSTATS_COL_SECOND
, LINKSTATS_ROW_FOURTH
);
385 u8g2
->print((int8_t)CRSF::LinkStatistics
.uplink_SNR
);
386 u8g2
->setCursor(LINKSTATS_COL_THIRD
, LINKSTATS_ROW_FOURTH
);
387 u8g2
->print((int8_t)CRSF::LinkStatistics
.downlink_SNR
);
388 u8g2
->setCursor(LINKSTATS_COL_SECOND
, LINKSTATS_ROW_FIFTH
);
389 u8g2
->print(CRSF::LinkStatistics
.active_antenna
);
396 static void helperDrawImage(menu_item_t menu
)
398 if (OPT_HAS_OLED_SPI_SMALL
)
401 // Adjust these to move them around on the screen
407 u8g2
->drawXBM(x_pos
, y_pos
, 32, 22, rate_img32
);
410 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, switch_img32
);
413 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, antenna_img32
);
416 case STATE_POWER_MAX
:
417 case STATE_POWER_DYNAMIC
:
418 u8g2
->drawXBM(x_pos
, y_pos
, 25, 25, power_img32
);
420 case STATE_TELEMETRY
:
421 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, ratio_img32
);
423 case STATE_POWERSAVE
:
424 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, powersaving_img32
);
427 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, fan_img32
);
430 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, joystick_img32
);
434 case STATE_VTX_CHANNEL
:
435 case STATE_VTX_POWER
:
436 case STATE_VTX_PITMODE
:
438 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, vtx_img32
);
441 u8g2
->drawXBM(x_pos
, y_pos
, 24, 22, wifi_img32
);
444 u8g2
->drawXBM(x_pos
, y_pos
, 32, 32, bind_img32
);
448 u8g2
->drawXBM(x_pos
, y_pos
, 24, 22, wifi_img32
);
451 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, rxwifi_img32
);
453 case STATE_WIFI_BACKPACK
:
454 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, backpack_img32
);
457 u8g2
->drawXBM(x_pos
, y_pos
-5, 32, 32, vrxwifi_img32
);
466 // Adjust these to move them around on the screen
472 u8g2
->drawXBM(x_pos
, y_pos
, 64, 44, rate_img64
);
475 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, switch_img64
);
478 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, antenna_img64
);
481 case STATE_POWER_MAX
:
482 case STATE_POWER_DYNAMIC
:
483 u8g2
->drawXBM(x_pos
, y_pos
, 50, 50, power_img64
);
485 case STATE_TELEMETRY
:
486 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, ratio_img64
);
488 case STATE_POWERSAVE
:
489 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, powersaving_img64
);
492 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, fan_img64
);
495 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64-5, joystick_img64
);
499 case STATE_VTX_CHANNEL
:
500 case STATE_VTX_POWER
:
501 case STATE_VTX_PITMODE
:
503 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, vtx_img64
);
506 u8g2
->drawXBM(x_pos
, y_pos
, 48, 44, wifi_img64
);
509 u8g2
->drawXBM(x_pos
, y_pos
, 64, 64, bind_img64
);
513 u8g2
->drawXBM(x_pos
, y_pos
, 48, 44, wifi_img64
);
516 u8g2
->drawXBM(x_pos
, y_pos
-5, 64, 64, rxwifi_img64
);
518 case STATE_WIFI_BACKPACK
:
519 u8g2
->drawXBM(x_pos
, y_pos
-5, 64, 64, backpack_img64
);
522 u8g2
->drawXBM(x_pos
, y_pos
-5, 64, 64, vrxwifi_img64
);