9 TFT_eSPI tft
= TFT_eSPI();
11 bool is_confirmed
= false;
13 const uint16_t *main_menu_icons
[] = {
23 #define COLOR_ELRS_BANNER_BACKGROUND 0x9E2D
25 #define SCREEN_X TFT_HEIGHT
26 #define SCREEN_Y TFT_WIDTH
28 //see #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
29 #define SCREEN_SMALL_FONT_SIZE 8
30 #define SCREEN_SMALL_FONT 1
32 //see #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
33 #define SCREEN_NORMAL_FONT_SIZE 16
34 #define SCREEN_NORMAL_FONT 2
36 //see #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
37 #define SCREEN_LARGE_FONT_SIZE 26
38 #define SCREEN_LARGE_FONT 4
40 //ICON SIZE Definition
41 #define SCREEN_LARGE_ICON_SIZE 60
42 #define SCREEN_NORAML_ICON_SIZE 20
43 #define SCREEN_NARROW_ICON_WEIGHT 14
46 #define SCREEN_CONTENT_GAP 10
47 #define SCREEN_FONT_GAP 5
49 //INIT LOGO PAGE Definition
50 #define INIT_PAGE_LOGO_X SCREEN_X
51 #define INIT_PAGE_LOGO_Y 53
52 #define INIT_PAGE_FONT_PADDING 3
53 #define INIT_PAGE_FONT_START_X SCREEN_FONT_GAP
54 #define INIT_PAGE_FONT_START_Y INIT_PAGE_LOGO_Y + (SCREEN_Y - INIT_PAGE_LOGO_Y - SCREEN_NORMAL_FONT_SIZE)/2
56 //IDLE PAGE Definition
57 #define IDLE_PAGE_START_X SCREEN_CONTENT_GAP
58 #define IDLE_PAGE_START_Y 0
60 #define IDLE_PAGE_STAT_START_X SCREEN_X/2 + SCREEN_CONTENT_GAP
61 #define IDLE_PAGE_STAT_Y_GAP (SCREEN_Y - SCREEN_NORMAL_FONT_SIZE * 3)/4
63 #define IDLE_PAGE_RATE_START_Y IDLE_PAGE_STAT_Y_GAP
64 #define IDLE_PAGE_POWER_START_Y IDLE_PAGE_RATE_START_Y + SCREEN_NORMAL_FONT_SIZE + IDLE_PAGE_STAT_Y_GAP
65 #define IDLE_PAGE_RATIO_START_Y IDLE_PAGE_POWER_START_Y + SCREEN_NORMAL_FONT_SIZE + IDLE_PAGE_STAT_Y_GAP
67 //MAIN PAGE Definition
68 #define MAIN_PAGE_ICON_START_X SCREEN_CONTENT_GAP
69 #define MAIN_PAGE_ICON_START_Y (SCREEN_Y - SCREEN_LARGE_ICON_SIZE)/2
71 #define MAIN_PAGE_WORD_START_X MAIN_PAGE_ICON_START_X + SCREEN_LARGE_ICON_SIZE + SCREEN_CONTENT_GAP
72 #define MAIN_PAGE_WORD_START_Y (SCREEN_Y - SCREEN_NORMAL_FONT_SIZE)/2
73 #define MAIN_PAGE_WORD_START_Y1 (SCREEN_Y - SCREEN_NORMAL_FONT_SIZE*2 - SCREEN_FONT_GAP)/2
74 #define MAIN_PAGE_WORD_START_Y2 MAIN_PAGE_WORD_START_Y1 + SCREEN_NORMAL_FONT_SIZE + SCREEN_FONT_GAP
76 //Sub Function Definiton
77 #define SUB_PAGE_VALUE_START_X SCREEN_CONTENT_GAP
78 #define SUB_PAGE_VALUE_START_Y (SCREEN_Y - SCREEN_LARGE_FONT_SIZE - SCREEN_NORMAL_FONT_SIZE - SCREEN_CONTENT_GAP)/2
80 #define SUB_PAGE_TIPS_START_X SCREEN_CONTENT_GAP
81 #define SUB_PAGE_TIPS_START_Y SCREEN_Y - SCREEN_NORMAL_FONT_SIZE - SCREEN_CONTENT_GAP
83 //Sub WIFI Mode & Bind Confirm Definiton
84 #define SUB_PAGE_ICON_START_X 0
85 #define SUB_PAGE_ICON_START_Y (SCREEN_Y - SCREEN_LARGE_ICON_SIZE)/2
87 #define SUB_PAGE_WORD_START_X SUB_PAGE_ICON_START_X + SCREEN_LARGE_ICON_SIZE
88 #define SUB_PAGE_WORD_START_Y1 (SCREEN_Y - SCREEN_NORMAL_FONT_SIZE*3 - SCREEN_FONT_GAP*2)/2
89 #define SUB_PAGE_WORD_START_Y2 SUB_PAGE_WORD_START_Y1 + SCREEN_NORMAL_FONT_SIZE + SCREEN_FONT_GAP
90 #define SUB_PAGE_WORD_START_Y3 SUB_PAGE_WORD_START_Y2 + SCREEN_NORMAL_FONT_SIZE + SCREEN_FONT_GAP
92 //Sub Binding Definiton
93 #define SUB_PAGE_BINDING_WORD_START_X 0
94 #define SUB_PAGE_BINDING_WORD_START_Y (SCREEN_Y - SCREEN_LARGE_FONT_SIZE)/2
96 void TFTScreen::init(bool reboot
)
100 tft
.setSwapBytes(true);
103 doScreenBackLight(HIGH
);
104 tft
.fillScreen(TFT_WHITE
);
106 tft
.pushImage(0, 0, INIT_PAGE_LOGO_X
, INIT_PAGE_LOGO_Y
, vendor_logo
);
108 tft
.fillRect(SCREEN_FONT_GAP
, INIT_PAGE_FONT_START_Y
- INIT_PAGE_FONT_PADDING
,
109 SCREEN_X
- SCREEN_FONT_GAP
*2, SCREEN_NORMAL_FONT_SIZE
+ INIT_PAGE_FONT_PADDING
*2, TFT_BLACK
);
112 sprintf(buffer
, "%s ELRS-", HARDWARE_VERSION
);
113 strncat(buffer
, version
, 6);
114 displayFontCenter(INIT_PAGE_FONT_START_X
, SCREEN_X
- INIT_PAGE_FONT_START_X
, INIT_PAGE_FONT_START_Y
,
115 SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
116 String(buffer
), TFT_WHITE
, TFT_BLACK
);
119 doScreenBackLight(LOW
);
121 current_rate_index
= 0;
122 current_power_index
= 0;
123 current_ratio_index
= 0;
124 current_powersaving_index
= 0;
125 current_smartfan_index
= 0;
128 current_screen_status
= SCREEN_STATUS_INIT
;
129 current_page_index
= PAGE_MAIN_MENU_INDEX
;
130 main_menu_page_index
= MAIN_MENU_RATE_INDEX
;
133 current_screen_status
= SCREEN_STATUS_WORK
;
134 current_page_index
= PAGE_MAIN_MENU_INDEX
;
135 main_menu_page_index
= MAIN_MENU_UPDATEFW_INDEX
;
136 updateMainMenuPage();
139 system_temperature
= 25;
142 void TFTScreen::idleScreen()
144 tft
.fillRect(0, 0, SCREEN_X
/2, SCREEN_Y
, COLOR_ELRS_BANNER_BACKGROUND
);
145 tft
.fillRect(SCREEN_X
/2, 0, SCREEN_X
/2, SCREEN_Y
, TFT_WHITE
);
147 tft
.pushImage(IDLE_PAGE_START_X
, IDLE_PAGE_START_Y
, SCREEN_LARGE_ICON_SIZE
, SCREEN_LARGE_ICON_SIZE
, elrs_banner
);
150 strncpy(buffer
, version
, 6);
151 sprintf(buffer
+6, " %02d", system_temperature
);
152 displayFontCenterWithCelsius(0, SCREEN_X
/2, SCREEN_LARGE_ICON_SIZE
+ (SCREEN_Y
- SCREEN_LARGE_ICON_SIZE
- SCREEN_SMALL_FONT_SIZE
)/2,
153 SCREEN_SMALL_FONT_SIZE
, SCREEN_SMALL_FONT
,
154 String(buffer
), TFT_WHITE
, COLOR_ELRS_BANNER_BACKGROUND
);
156 displayFontCenter(IDLE_PAGE_STAT_START_X
, SCREEN_X
, IDLE_PAGE_RATE_START_Y
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
157 rate_string
[current_rate_index
], TFT_BLACK
, TFT_WHITE
);
159 displayFontCenter(IDLE_PAGE_STAT_START_X
, SCREEN_X
, IDLE_PAGE_POWER_START_Y
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
160 power_string
[current_power_index
], TFT_BLACK
, TFT_WHITE
);
162 displayFontCenter(IDLE_PAGE_STAT_START_X
, SCREEN_X
, IDLE_PAGE_RATIO_START_Y
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
163 ratio_string
[current_ratio_index
], TFT_BLACK
, TFT_WHITE
);
165 current_screen_status
= SCREEN_STATUS_IDLE
;
168 void TFTScreen::updateMainMenuPage()
170 tft
.fillScreen(TFT_WHITE
);
172 tft
.pushImage(MAIN_PAGE_ICON_START_X
, MAIN_PAGE_ICON_START_Y
, SCREEN_LARGE_ICON_SIZE
, SCREEN_LARGE_ICON_SIZE
, main_menu_icons
[main_menu_page_index
-1]);
175 displayFontCenter(MAIN_PAGE_WORD_START_X
, SCREEN_X
, MAIN_PAGE_WORD_START_Y1
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
176 main_menu_line_1
[main_menu_page_index
- 1], TFT_BLACK
, TFT_WHITE
);
178 displayFontCenter(MAIN_PAGE_WORD_START_X
, SCREEN_X
, MAIN_PAGE_WORD_START_Y2
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
179 main_menu_line_2
[main_menu_page_index
- 1], TFT_BLACK
, TFT_WHITE
);
182 void TFTScreen::updateSubFunctionPage()
184 tft
.fillScreen(TFT_WHITE
);
186 doValueSelection(USER_ACTION_NONE
);
188 displayFontCenter(SUB_PAGE_TIPS_START_X
, SCREEN_X
, SUB_PAGE_TIPS_START_Y
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
189 "PRESS TO CONFIRM", TFT_BLACK
, TFT_WHITE
);
192 void TFTScreen::updateSubWIFIModePage()
194 tft
.fillScreen(TFT_WHITE
);
196 tft
.pushImage(SUB_PAGE_ICON_START_X
, SUB_PAGE_ICON_START_Y
, SCREEN_LARGE_ICON_SIZE
, SCREEN_LARGE_ICON_SIZE
, elrs_wifimode
);
197 #if defined(HOME_WIFI_SSID) && defined(HOME_WIFI_PASSWORD)
198 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y1
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
199 "open http://", TFT_BLACK
, TFT_WHITE
);
201 String host_msg
= String(wifi_hostname
) + ".local";
202 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y2
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
203 host_msg
, TFT_BLACK
, TFT_WHITE
);
205 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y3
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
206 "by browser", TFT_BLACK
, TFT_WHITE
);
208 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y1
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
209 wifi_ap_ssid
, TFT_BLACK
, TFT_WHITE
);
211 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y2
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
212 wifi_ap_password
, TFT_BLACK
, TFT_WHITE
);
214 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y3
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
215 wifi_ap_address
, TFT_BLACK
, TFT_WHITE
);
217 updatecallback(USER_UPDATE_TYPE_WIFI
);
220 void TFTScreen::updateSubBindConfirmPage()
222 tft
.fillScreen(TFT_WHITE
);
224 tft
.pushImage(SUB_PAGE_ICON_START_X
, SUB_PAGE_ICON_START_Y
, SCREEN_LARGE_ICON_SIZE
, SCREEN_LARGE_ICON_SIZE
, elrs_bind
);
226 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y1
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
227 "PRESS TO", TFT_BLACK
, TFT_WHITE
);
229 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y2
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
230 "SEND BIND", TFT_BLACK
, TFT_WHITE
);
232 displayFontCenter(SUB_PAGE_WORD_START_X
, SCREEN_X
, SUB_PAGE_WORD_START_Y3
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
233 "REQUEST", TFT_BLACK
, TFT_WHITE
);
236 void TFTScreen::updateSubBindingPage()
238 tft
.fillScreen(TFT_WHITE
);
240 displayFontCenter(SUB_PAGE_BINDING_WORD_START_X
, SCREEN_X
, SUB_PAGE_BINDING_WORD_START_Y
, SCREEN_LARGE_FONT_SIZE
, SCREEN_LARGE_FONT
,
241 "BINDING", TFT_BLACK
, TFT_WHITE
);
243 updatecallback(USER_UPDATE_TYPE_BINDING
);
245 current_screen_status
= SCREEN_STATUS_BINDING
;
248 void TFTScreen::doRateValueSelect(int action
)
250 nextIndex(current_rate_index
, action
, RATE_MAX_NUMBER
);
251 displayFontCenter(SUB_PAGE_VALUE_START_X
, SCREEN_X
, SUB_PAGE_VALUE_START_Y
, SCREEN_LARGE_FONT_SIZE
, SCREEN_LARGE_FONT
,
252 rate_string
[current_rate_index
], TFT_BLACK
, TFT_WHITE
);
255 void TFTScreen::doPowerValueSelect(int action
)
257 nextIndex(current_power_index
, action
, MinPower
, MaxPower
+1);
258 displayFontCenter(SUB_PAGE_VALUE_START_X
, SCREEN_X
, SUB_PAGE_VALUE_START_Y
, SCREEN_LARGE_FONT_SIZE
, SCREEN_LARGE_FONT
,
259 power_string
[current_power_index
], TFT_BLACK
, TFT_WHITE
);
262 void TFTScreen::doRatioValueSelect(int action
)
264 nextIndex(current_ratio_index
, action
, RATIO_MAX_NUMBER
);
265 displayFontCenter(SUB_PAGE_VALUE_START_X
, SCREEN_X
, SUB_PAGE_VALUE_START_Y
, SCREEN_LARGE_FONT_SIZE
, SCREEN_LARGE_FONT
,
266 ratio_string
[current_ratio_index
], TFT_BLACK
, TFT_WHITE
);
270 void TFTScreen::doPowerSavingValueSelect(int action
)
272 nextIndex(current_powersaving_index
, action
, POWERSAVING_MAX_NUMBER
);
273 displayFontCenter(SUB_PAGE_VALUE_START_X
, SCREEN_X
, SUB_PAGE_VALUE_START_Y
, SCREEN_LARGE_FONT_SIZE
, SCREEN_LARGE_FONT
,
274 powersaving_string
[current_powersaving_index
], TFT_BLACK
, TFT_WHITE
);
277 void TFTScreen::doSmartFanValueSelect(int action
)
279 nextIndex(current_smartfan_index
, action
, SMARTFAN_MAX_NUMBER
);
280 displayFontCenter(SUB_PAGE_VALUE_START_X
, SCREEN_X
, SUB_PAGE_VALUE_START_Y
, SCREEN_LARGE_FONT_SIZE
, SCREEN_LARGE_FONT
,
281 smartfan_string
[current_smartfan_index
], TFT_BLACK
, TFT_WHITE
);
284 void TFTScreen::doParamUpdate(uint8_t rate_index
, uint8_t power_index
, uint8_t ratio_index
, uint8_t motion_index
, uint8_t fan_index
)
286 if(current_screen_status
== SCREEN_STATUS_IDLE
)
288 if(rate_index
!= current_rate_index
)
290 current_rate_index
= rate_index
;
291 displayFontCenter(IDLE_PAGE_STAT_START_X
, SCREEN_X
, IDLE_PAGE_RATE_START_Y
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
292 rate_string
[current_rate_index
], TFT_BLACK
, TFT_WHITE
);
295 if(power_index
!= current_power_index
)
297 current_power_index
= power_index
;
298 displayFontCenter(IDLE_PAGE_STAT_START_X
, SCREEN_X
, IDLE_PAGE_POWER_START_Y
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
299 power_string
[current_power_index
], TFT_BLACK
, TFT_WHITE
);
302 if(ratio_index
!= current_ratio_index
)
304 current_ratio_index
= ratio_index
;
305 displayFontCenter(IDLE_PAGE_STAT_START_X
, SCREEN_X
, IDLE_PAGE_RATIO_START_Y
, SCREEN_NORMAL_FONT_SIZE
, SCREEN_NORMAL_FONT
,
306 ratio_string
[current_ratio_index
], TFT_BLACK
, TFT_WHITE
);
311 current_rate_index
= rate_index
;
312 current_power_index
= power_index
;
313 current_ratio_index
= ratio_index
;
316 current_powersaving_index
= motion_index
;
317 current_smartfan_index
= fan_index
;
320 void TFTScreen::doTemperatureUpdate(uint8_t temperature
)
322 if(current_screen_status
== SCREEN_STATUS_IDLE
&& system_temperature
!= temperature
)
325 strncpy(buffer
, version
, 6);
326 sprintf(buffer
+6, " %02d", temperature
);
327 displayFontCenterWithCelsius(0, SCREEN_X
/2, SCREEN_LARGE_ICON_SIZE
+ (SCREEN_Y
- SCREEN_LARGE_ICON_SIZE
- SCREEN_SMALL_FONT_SIZE
)/2,
328 SCREEN_SMALL_FONT_SIZE
, SCREEN_SMALL_FONT
,
329 String(buffer
), TFT_WHITE
, COLOR_ELRS_BANNER_BACKGROUND
);
331 system_temperature
= temperature
;
334 void TFTScreen::displayFontCenterWithCelsius(uint32_t font_start_x
, uint32_t font_end_x
, uint32_t font_start_y
,
335 int font_size
, int font_type
, String font_string
,
336 uint16_t fgColor
, uint16_t bgColor
)
338 tft
.fillRect(font_start_x
, font_start_y
, font_end_x
- font_start_x
, font_size
, bgColor
);
340 int start_pos
= font_start_x
+ (font_end_x
- font_start_x
- tft
.textWidth(font_string
, font_type
) - font_size
)/2;
342 tft
.setCursor(start_pos
, font_start_y
, font_type
);
343 tft
.setTextColor(fgColor
, bgColor
);
344 tft
.print(font_string
);
346 int celsius_start_pos
= start_pos
+ tft
.textWidth(font_string
, font_type
);
347 if(font_size
== SCREEN_SMALL_FONT_SIZE
)
349 tft
.pushImage(celsius_start_pos
, font_start_y
, font_size
/2, font_size
, Celsius4x8
);
352 int char_c_pos
= celsius_start_pos
+ font_size
/2;
353 tft
.setCursor(char_c_pos
, font_start_y
, font_type
);
357 void TFTScreen::displayFontCenter(uint32_t font_start_x
, uint32_t font_end_x
, uint32_t font_start_y
,
358 int font_size
, int font_type
, String font_string
,
359 uint16_t fgColor
, uint16_t bgColor
)
361 tft
.fillRect(font_start_x
, font_start_y
, font_end_x
- font_start_x
, font_size
, bgColor
);
363 int start_pos
= font_start_x
+ (font_end_x
- font_start_x
- tft
.textWidth(font_string
, font_type
))/2;
364 tft
.setCursor(start_pos
, font_start_y
, font_type
);
366 tft
.setTextColor(fgColor
, bgColor
);
367 tft
.print(font_string
);
371 void TFTScreen::doScreenBackLight(int state
)
374 digitalWrite(TFT_BL
, state
);