acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
Display.cpp (25563B)
1 #include "bootScreen.h" 2 #include "Display.h" 3 #include "IRC.h" 4 #include "pins.h" 5 #include "Speaker.h" 6 #include "Storage.h" 7 #include "Utilities.h" 8 9 10 bool infoScreen = false; 11 bool configScreen = false; 12 bool screenOn = true; 13 const char* channel = "#comms"; 14 unsigned long infoScreenStartTime = 0; 15 unsigned long configScreenStartTime = 0; 16 unsigned long lastStatusUpdateTime = 0; 17 unsigned long lastActivityTime = 0; 18 String inputBuffer = ""; 19 20 std::vector<String> lines; 21 std::vector<bool> mentions; 22 std::map<String, uint32_t> nickColors; 23 24 TFT_eSPI tft = TFT_eSPI(); 25 26 27 void addLine(String senderNick, String message, String type, bool mention, uint16_t errorColor, uint16_t reasonColor) { 28 if (type != "error" && nickColors.find(senderNick) == nickColors.end()) 29 nickColors[senderNick] = generateRandomColor(); 30 31 String formattedMessage; 32 33 if (type == "join") { 34 formattedMessage = "JOIN " + senderNick + " has joined " + String(channel); 35 } else if (type == "part") { 36 formattedMessage = "PART " + senderNick + " has EMO-QUIT " + String(channel); 37 } else if (type == "quit") { 38 formattedMessage = "QUIT " + senderNick; 39 } else if (type == "nick") { 40 int arrowPos = message.indexOf(" -> "); 41 String oldNick = senderNick; 42 String newNick = message.substring(arrowPos + 4); 43 if (nickColors.find(newNick) == nickColors.end()) { 44 nickColors[newNick] = generateRandomColor(); 45 } 46 formattedMessage = "NICK " + oldNick + " -> " + newNick; 47 } else if (type == "kick") { 48 formattedMessage = "KICK " + senderNick + message; 49 } else if (type == "mode") { 50 formattedMessage = "MODE " + message; 51 tft.setTextColor(TFT_BLUE); 52 } else if (type == "action") { 53 formattedMessage = "* " + senderNick + " " + message; 54 } else if (type == "error") { 55 formattedMessage = "ERROR " + message; 56 senderNick = "ERROR"; 57 } else { 58 formattedMessage = senderNick + ": " + message; 59 } 60 61 int linesRequired = calculateLinesRequired(formattedMessage); 62 63 while (lines.size() + linesRequired > MAX_LINES) { 64 lines.erase(lines.begin()); 65 mentions.erase(mentions.begin()); 66 } 67 68 if (type == "error") { 69 lines.push_back("ERROR " + message); 70 mentions.push_back(false); 71 } else { 72 lines.push_back(formattedMessage); 73 mentions.push_back(mention); 74 } 75 76 displayLines(); 77 } 78 79 80 int calculateLinesRequired(String message) { 81 int linesRequired = 1; 82 int lineWidth = 0; 83 84 for (unsigned int i = 0; i < message.length(); i++) { 85 char c = message[i]; 86 if (c == '\x03') { 87 if (i + 1 < message.length() && isdigit(message[i + 1])) { 88 i++; 89 if (i + 1 < message.length() && isdigit(message[i + 1])) 90 i++; 91 } 92 if (i + 1 < message.length() && message[i + 1] == ',' && isdigit(message[i + 2])) { 93 i += 2; 94 if (i + 1 < message.length() && isdigit(message[i + 1])) 95 i++; 96 } 97 } else if (c != '\x02' && c != '\x0F' && c != '\x1F') { 98 lineWidth += tft.textWidth(String(c)); 99 if (lineWidth > SCREEN_WIDTH) { 100 linesRequired++; 101 lineWidth = tft.textWidth(String(c)); 102 } 103 } 104 } 105 106 return linesRequired; 107 } 108 109 110 void displayCenteredText(String text) { 111 tft.fillScreen(TFT_BLACK); 112 tft.setTextDatum(MC_DATUM); 113 tft.setTextColor(TFT_GREEN, TFT_BLACK); 114 tft.drawString(text, SCREEN_WIDTH / 2, (SCREEN_HEIGHT + STATUS_BAR_HEIGHT) / 2); 115 } 116 117 118 void displayInputLine() { 119 tft.fillRect(0, SCREEN_HEIGHT - INPUT_LINE_HEIGHT, SCREEN_WIDTH, INPUT_LINE_HEIGHT, TFT_BLACK); 120 tft.setCursor(0, SCREEN_HEIGHT - INPUT_LINE_HEIGHT); 121 tft.setTextColor(TFT_WHITE); 122 tft.setTextSize(1); 123 124 String displayInput = inputBuffer; 125 int displayWidth = tft.textWidth(displayInput); 126 int inputWidth = SCREEN_WIDTH - tft.textWidth("> "); 127 128 while (displayWidth > inputWidth) { 129 displayInput = displayInput.substring(1); 130 displayWidth = tft.textWidth(displayInput); 131 } 132 133 if (inputBuffer.length() >= 510) 134 tft.setTextColor(TFT_RED); 135 136 tft.print("> " + displayInput); 137 tft.setTextColor(TFT_WHITE); 138 } 139 140 141 void displayLines() { 142 tft.fillRect(0, STATUS_BAR_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - STATUS_BAR_HEIGHT - INPUT_LINE_HEIGHT, TFT_BLACK); 143 144 int cursorY = STATUS_BAR_HEIGHT; 145 int totalLinesHeight = 0; 146 std::vector<int> lineHeights; 147 148 for (const String& line : lines) { 149 int lineHeight = calculateLinesRequired(line) * CHAR_HEIGHT; 150 lineHeights.push_back(lineHeight); 151 totalLinesHeight += lineHeight; 152 } 153 154 while (totalLinesHeight > SCREEN_HEIGHT - STATUS_BAR_HEIGHT - INPUT_LINE_HEIGHT) { 155 totalLinesHeight -= lineHeights.front(); 156 lines.erase(lines.begin()); 157 mentions.erase(mentions.begin()); 158 lineHeights.erase(lineHeights.begin()); 159 } 160 161 for (size_t i = 0; i < lines.size(); ++i) { 162 const String& line = lines[i]; 163 bool mention = mentions[i]; 164 165 tft.setCursor(0, cursorY); 166 167 if (line.startsWith("JOIN ")) { 168 tft.setTextColor(TFT_GREEN); 169 tft.print("JOIN "); 170 int startIndex = 5; 171 int endIndex = line.indexOf(" has joined "); 172 String senderNick = line.substring(startIndex, endIndex); 173 tft.setTextColor(nickColors[senderNick]); 174 tft.print(senderNick); 175 tft.setTextColor(TFT_WHITE); 176 tft.print(" has joined "); 177 tft.setTextColor(TFT_CYAN); 178 tft.print(channel); 179 cursorY += CHAR_HEIGHT; 180 } else if (line.startsWith("PART ")) { 181 tft.setTextColor(TFT_RED); 182 tft.print("PART "); 183 int startIndex = 5; 184 int endIndex = line.indexOf(" has EMO-QUIT "); 185 String senderNick = line.substring(startIndex, endIndex); 186 tft.setTextColor(nickColors[senderNick]); 187 tft.print(senderNick); 188 tft.setTextColor(TFT_WHITE); 189 tft.print(" has EMO-QUIT "); 190 tft.setTextColor(TFT_CYAN); 191 tft.print(channel); 192 cursorY += CHAR_HEIGHT; 193 } else if (line.startsWith("QUIT ")) { 194 tft.setTextColor(TFT_RED); 195 tft.print("QUIT "); 196 String senderNick = line.substring(5); 197 tft.setTextColor(nickColors[senderNick]); 198 tft.print(senderNick); 199 cursorY += CHAR_HEIGHT; 200 } else if (line.startsWith("NICK ")) { 201 tft.setTextColor(TFT_BLUE); 202 tft.print("NICK "); 203 int startIndex = 5; 204 int endIndex = line.indexOf(" -> "); 205 String oldNick = line.substring(startIndex, endIndex); 206 String newNick = line.substring(endIndex + 4); 207 tft.setTextColor(nickColors[oldNick]); 208 tft.print(oldNick); 209 tft.setTextColor(TFT_WHITE); 210 tft.print(" -> "); 211 tft.setTextColor(nickColors[newNick]); 212 tft.print(newNick); 213 cursorY += CHAR_HEIGHT; 214 } else if (line.startsWith("KICK ")) { 215 tft.setTextColor(TFT_RED); 216 tft.print("KICK "); 217 int startIndex = 5; 218 int endIndex = line.indexOf(" by "); 219 String kickedNick = line.substring(startIndex, endIndex); 220 String kicker = line.substring(endIndex + 4); 221 tft.setTextColor(nickColors[kickedNick]); 222 tft.print(kickedNick); 223 tft.setTextColor(TFT_WHITE); 224 tft.print(" by "); 225 tft.setTextColor(nickColors[kicker]); 226 tft.print(kicker); 227 cursorY += CHAR_HEIGHT; 228 } else if (line.startsWith("MODE ")) { 229 tft.setTextColor(TFT_BLUE); 230 tft.print("MODE "); 231 String modeChange = line.substring(5); 232 tft.setTextColor(TFT_WHITE); 233 tft.print(modeChange); 234 cursorY += CHAR_HEIGHT; 235 } else if (line.startsWith("ERROR ")) { 236 tft.setTextColor(TFT_RED); 237 tft.print("ERROR "); 238 String errorReason = line.substring(6); 239 tft.setTextColor(TFT_DARKGREY); 240 tft.print(errorReason); 241 cursorY += CHAR_HEIGHT; 242 } else if (line.startsWith("* ")) { 243 tft.setTextColor(TFT_MAGENTA); 244 tft.print("* "); 245 int startIndex = 2; 246 int endIndex = line.indexOf(' ', startIndex); 247 String senderNick = line.substring(startIndex, endIndex); 248 String actionMessage = line.substring(endIndex + 1); 249 tft.setTextColor(nickColors[senderNick]); 250 tft.print(senderNick); 251 tft.setTextColor(TFT_MAGENTA); 252 tft.print(" " + actionMessage); 253 cursorY += CHAR_HEIGHT; 254 } else { 255 int colonIndex = line.indexOf(':'); 256 String senderNick = line.substring(0, colonIndex); 257 String message = line.substring(colonIndex + 1); 258 259 tft.setTextColor(nickColors[senderNick]); 260 tft.print(senderNick); 261 tft.setTextColor(TFT_WHITE); 262 cursorY = renderFormattedMessage(":" + message, cursorY, CHAR_HEIGHT, mention); 263 } 264 } 265 266 displayInputLine(); 267 } 268 269 270 void displayXBM() { 271 tft.fillScreen(TFT_BLACK); 272 273 int x = (SCREEN_WIDTH - logo_width) / 2; 274 int y = (SCREEN_HEIGHT - logo_height) / 2; 275 276 tft.drawXBitmap(x, y, logo_bits, logo_width, logo_height, TFT_GREEN); 277 278 unsigned long startTime = millis(); 279 bool wipeInitiated = false; 280 281 while (millis() - startTime < 3000) { 282 if (getKeyboardInput() == 'w') { 283 wipeNVS(); 284 tft.fillScreen(TFT_BLACK); 285 displayCenteredText("NVS WIPED"); 286 delay(2000); 287 wipeInitiated = true; 288 break; 289 } 290 } 291 } 292 293 294 uint32_t generateRandomColor() { 295 return tft.color565(random(0, 255), random(0, 255), random(0, 255)); 296 } 297 298 299 uint16_t getColorFromCode(int colorCode) { 300 switch (colorCode) { 301 case 0: return TFT_WHITE; 302 case 1: return TFT_BLACK; 303 case 2: return tft.color565(0, 0, 128); // Dark Blue (Navy) 304 case 3: return TFT_GREEN; 305 case 4: return TFT_RED; 306 case 5: return tft.color565(128, 0, 0); // Brown (Maroon) 307 case 6: return tft.color565(128, 0, 128); // Purple 308 case 7: return tft.color565(255, 165, 0); // Orange 309 case 8: return TFT_YELLOW; 310 case 9: return tft.color565(144, 238, 144); // Light Green 311 case 10: return tft.color565(0, 255, 255); // Cyan (Light Blue) 312 case 11: return tft.color565(224, 255, 255); // Light Cyan (Aqua) 313 case 12: return TFT_BLUE; 314 case 13: return tft.color565(255, 192, 203); // Pink (Light Purple) 315 case 14: return tft.color565(128, 128, 128); // Grey 316 case 15: return tft.color565(211, 211, 211); // Light Grey 317 case 16: return 0x4000; 318 case 17: return 0x4100; 319 case 18: return 0x4220; 320 case 19: return 0x3220; 321 case 20: return 0x0220; 322 case 21: return 0x0225; 323 case 22: return 0x0228; 324 case 23: return 0x0128; 325 case 24: return 0x0008; 326 case 25: return 0x2808; 327 case 26: return 0x4008; 328 case 27: return 0x4005; 329 case 28: return 0x7000; 330 case 29: return 0x71C0; 331 case 30: return 0x73A0; 332 case 31: return 0x53A0; 333 case 32: return 0x03A0; 334 case 33: return 0x03A9; 335 case 34: return 0x03AE; 336 case 35: return 0x020E; 337 case 36: return 0x000E; 338 case 37: return 0x480E; 339 case 38: return 0x700E; 340 case 39: return 0x7008; 341 case 40: return 0xB000; 342 case 41: return 0xB300; 343 case 42: return 0xB5A0; 344 case 43: return 0x7DA0; 345 case 44: return 0x05A0; 346 case 45: return 0x05AE; 347 case 46: return 0x05B6; 348 case 47: return 0x0316; 349 case 48: return 0x0016; 350 case 49: return 0x7016; 351 case 50: return 0xB016; 352 case 51: return 0xB00D; 353 case 52: return 0xF800; 354 case 53: return 0xFC60; 355 case 54: return 0xFFE0; 356 case 55: return 0xB7E0; 357 case 56: return 0x07E0; 358 case 57: return 0x07F4; 359 case 58: return 0x07FF; 360 case 59: return 0x047F; 361 case 60: return 0x001F; 362 case 61: return 0xA01F; 363 case 62: return 0xF81F; 364 case 63: return 0xF813; 365 case 64: return 0xFACB; 366 case 65: return 0xFDAB; 367 case 66: return 0xFFEE; 368 case 67: return 0xCFEC; 369 case 68: return 0x6FED; 370 case 69: return 0x67F9; 371 case 70: return 0x6FFF; 372 case 71: return 0x5DBF; 373 case 72: return 0x5ADF; 374 case 73: return 0xC2DF; 375 case 74: return 0xFB3F; 376 case 75: return 0xFAD7; 377 case 76: return 0xFCF3; 378 case 77: return 0xFE93; 379 case 78: return 0xFFF3; 380 case 79: return 0xE7F3; 381 case 80: return 0x9FF3; 382 case 81: return 0x9FFB; 383 case 82: return 0x9FFF; 384 case 83: return 0x9E9F; 385 case 84: return 0x9CFF; 386 case 85: return 0xDCFF; 387 case 86: return 0xFCFF; 388 case 87: return 0xFCBA; 389 case 88: return 0x0000; 390 case 89: return 0x1082; 391 case 90: return 0x2945; 392 case 91: return 0x31A6; 393 case 92: return 0x4A69; 394 case 93: return 0x632C; 395 case 94: return 0x8410; 396 case 95: return 0x9CF3; 397 case 96: return 0xBDF7; 398 case 97: return 0xE71C; 399 case 98: return 0xFFFF; 400 default: return TFT_WHITE; 401 } 402 } 403 404 405 uint16_t getColorFromPercentage(int percentage) { 406 if (percentage > 75) return TFT_GREEN; 407 else if (percentage > 50) return TFT_YELLOW; 408 else if (percentage > 25) return TFT_ORANGE; 409 else return TFT_RED; 410 } 411 412 413 void handleKeyboardInput(char key) { 414 lastActivityTime = millis(); 415 416 if (!screenOn) { 417 turnOnScreen(); 418 screenOn = true; 419 return; 420 } 421 422 static bool altPressed = false; 423 424 if (key == '\n' || key == '\r') { 425 if (inputBuffer.startsWith("/nick ")) { 426 String newNick = inputBuffer.substring(6); 427 sendIRC("NICK " + newNick); 428 inputBuffer = ""; 429 } else if (inputBuffer.startsWith("/config")) { 430 configScreen = true; 431 configScreenStartTime = millis(); 432 inputBuffer = ""; 433 } else if (inputBuffer.startsWith("/info")) { 434 infoScreen = true; 435 infoScreenStartTime = millis(); 436 tft.fillScreen(TFT_BLACK); 437 printDeviceInfo(); 438 inputBuffer = ""; 439 } else if (inputBuffer.startsWith("/raw ")) { 440 String rawCommand = inputBuffer.substring(5); 441 sendIRC(rawCommand); 442 } else if (inputBuffer.startsWith("/me ")) { 443 String actionMessage = inputBuffer.substring(4); 444 sendIRC("PRIVMSG " + String(channel) + " :\001ACTION " + actionMessage + "\001"); 445 addLine(irc_nickname, actionMessage, "action"); 446 inputBuffer = ""; 447 } else { 448 sendIRC("PRIVMSG " + String(channel) + " :" + inputBuffer); 449 addLine(irc_nickname, inputBuffer, "message"); 450 } 451 inputBuffer = ""; 452 displayInputLine(); 453 } else if (key == '\b') { 454 if (inputBuffer.length() > 0) { 455 inputBuffer.remove(inputBuffer.length() - 1); 456 displayInputLine(); 457 } 458 } else { 459 inputBuffer += key; 460 displayInputLine(); 461 } 462 } 463 464 465 void parseAndDisplay(String line) { 466 int firstSpace = line.indexOf(' '); 467 int secondSpace = line.indexOf(' ', firstSpace + 1); 468 469 if (firstSpace != -1 && secondSpace != -1) { 470 String command = line.substring(firstSpace + 1, secondSpace); 471 472 if (command == "PRIVMSG") { 473 int thirdSpace = line.indexOf(' ', secondSpace + 1); 474 String target = line.substring(secondSpace + 1, thirdSpace); 475 if (target == String(channel)) { 476 int colonPos = line.indexOf(':', thirdSpace); 477 String message = line.substring(colonPos + 1); 478 String senderNick = line.substring(1, line.indexOf('!')); 479 bool mention = message.indexOf(irc_nickname) != -1; 480 481 if (mention) 482 playNotificationSound(); 483 484 if (message.startsWith(String("\x01") + "ACTION ") && message.endsWith("\x01")) { 485 String actionMessage = message.substring(8, message.length() - 1); 486 addLine(senderNick, actionMessage, "action"); 487 } else { 488 addLine(senderNick, message, "message", mention); 489 } 490 } 491 } else if (command == "JOIN" && line.indexOf(channel) != -1) { 492 String senderNick = line.substring(1, line.indexOf('!')); 493 addLine(senderNick, " has joined " + String(channel), "join"); 494 } else if (command == "PART" && line.indexOf(channel) != -1) { 495 String senderNick = line.substring(1, line.indexOf('!')); 496 addLine(senderNick, " has EMO-QUIT " + String(channel), "part"); 497 } else if (command == "QUIT") { 498 String senderNick = line.substring(1, line.indexOf('!')); 499 addLine(senderNick, "", "quit"); 500 } else if (command == "NICK") { 501 String prefix = line.startsWith(":") ? line.substring(1, firstSpace) : ""; 502 String newNick = line.substring(line.lastIndexOf(':') + 1); 503 504 if (prefix.indexOf('!') == -1) { 505 addLine(irc_nickname, " -> " + newNick, "nick"); 506 irc_nickname = newNick; 507 } else { 508 String oldNick = prefix.substring(0, prefix.indexOf('!')); 509 addLine(oldNick, " -> " + newNick, "nick"); 510 if (oldNick == irc_nickname) { 511 irc_nickname = newNick; 512 } 513 } 514 } else if (command == "KICK") { 515 int thirdSpace = line.indexOf(' ', secondSpace + 1); 516 int fourthSpace = line.indexOf(' ', thirdSpace + 1); 517 String kicker = line.substring(1, line.indexOf('!')); 518 String kicked = line.substring(thirdSpace + 1, fourthSpace); 519 addLine(kicked, " by " + kicker, "kick"); 520 } else if (command == "MODE") { 521 String modeChange = line.substring(secondSpace + 1); 522 addLine("", modeChange, "mode"); 523 } else if (command == "432") { 524 addLine("ERROR", "ERR_ERRONEUSNICKNAME", "error", TFT_RED, TFT_DARKGREY); 525 } else if (command == "433") { 526 addLine("ERROR", "ERR_NICKNAMEINUSE", "error", TFT_RED, TFT_DARKGREY); 527 irc_nickname = "ACID_" + String(random(1000, 9999)); 528 sendIRC("NICK " + irc_nickname); 529 } 530 } 531 } 532 533 534 int renderFormattedMessage(String message, int cursorY, int lineHeight, bool highlightNick) { 535 uint16_t fgColor = TFT_WHITE; 536 uint16_t bgColor = TFT_BLACK; 537 bool bold = false; 538 bool underline = false; 539 bool nickHighlighted = false; 540 541 int nickPos = -1; 542 if (highlightNick) { 543 nickPos = message.indexOf(irc_nickname); 544 } 545 546 for (unsigned int i = 0; i < message.length(); i++) { 547 char c = message[i]; 548 if (c == '\x02') { 549 bold = !bold; 550 } else if (c == '\x1F') { 551 underline = !underline; 552 } else if (c == '\x03') { 553 fgColor = TFT_WHITE; 554 bgColor = TFT_BLACK; 555 556 if (i + 1 < message.length() && (isdigit(message[i + 1]) || message[i + 1] == ',')) { 557 int colorCode = -1; 558 if (isdigit(message[i + 1])) { 559 colorCode = message[++i] - '0'; 560 if (i + 1 < message.length() && isdigit(message[i + 1])) 561 colorCode = colorCode * 10 + (message[++i] - '0'); 562 } 563 564 if (colorCode != -1) 565 fgColor = getColorFromCode(colorCode); 566 567 if (i + 1 < message.length() && message[i + 1] == ',') { 568 i++; 569 int bgColorCode = -1; 570 if (isdigit(message[i + 1])) { 571 bgColorCode = message[++i] - '0'; 572 if (i + 1 < message.length() && isdigit(message[i + 1])) 573 bgColorCode = bgColorCode * 10 + (message[++i] - '0'); 574 } 575 576 if (bgColorCode != -1) 577 bgColor = getColorFromCode(bgColorCode); 578 579 } 580 581 tft.setTextColor(fgColor, bgColor); 582 } 583 } else if (c == '\x0F') { 584 fgColor = TFT_WHITE; 585 bgColor = TFT_BLACK; 586 bold = false; 587 underline = false; 588 tft.setTextColor(fgColor, bgColor); 589 tft.setTextFont(1); 590 } else { 591 if (highlightNick && !nickHighlighted && nickPos != -1 && i == nickPos) { 592 tft.setTextColor(TFT_YELLOW, bgColor); 593 for (char nc : irc_nickname) { 594 tft.print(nc); 595 i++; 596 } 597 i--; 598 tft.setTextColor(TFT_WHITE, bgColor); 599 nickHighlighted = true; 600 } else { 601 if (tft.getCursorX() + tft.textWidth(String(c)) > SCREEN_WIDTH) { 602 cursorY += lineHeight; 603 tft.setCursor(0, cursorY); 604 } 605 if (c == ' ') { 606 int spaceWidth = tft.textWidth(" "); 607 tft.fillRect(tft.getCursorX(), tft.getCursorY(), spaceWidth, lineHeight, bgColor); 608 tft.setCursor(tft.getCursorX() + spaceWidth, tft.getCursorY()); 609 } else { 610 tft.fillRect(tft.getCursorX(), tft.getCursorY(), tft.textWidth(String(c)), lineHeight, bgColor); 611 tft.setTextColor(fgColor, bgColor); 612 tft.print(c); 613 } 614 } 615 } 616 } 617 618 if (message.endsWith(" ")) { 619 int trailingSpaces = 0; 620 for (int i = message.length() - 1; i >= 0 && message[i] == ' '; i--) { 621 trailingSpaces++; 622 } 623 for (int i = 0; i < trailingSpaces; i++) { 624 int spaceWidth = tft.textWidth(" "); 625 tft.fillRect(tft.getCursorX(), tft.getCursorY(), spaceWidth, lineHeight, bgColor); 626 tft.setCursor(tft.getCursorX() + spaceWidth, tft.getCursorY()); 627 } 628 } 629 630 cursorY += lineHeight; 631 return cursorY; 632 } 633 634 635 void setupScreen() { 636 pinMode(TFT_BL, OUTPUT); 637 digitalWrite(TFT_BL, HIGH); 638 setBrightness(8); 639 tft.begin(); 640 tft.setRotation(1); 641 tft.invertDisplay(1); 642 Serial.println("TFT initialized"); 643 } 644 645 646 void turnOffScreen() { 647 Serial.println("Screen turned off"); 648 tft.writecommand(TFT_DISPOFF); 649 tft.writecommand(TFT_SLPIN); 650 digitalWrite(TFT_BL, LOW); 651 screenOn = false; 652 } 653 654 655 void turnOnScreen() { 656 Serial.println("Screen turned on"); 657 digitalWrite(TFT_BL, HIGH); 658 tft.writecommand(TFT_SLPOUT); 659 tft.writecommand(TFT_DISPON); 660 screenOn = true; 661 } 662 663 664 void updateStatusBar() { 665 Serial.println("Updating status bar..."); 666 uint16_t darkerGrey = tft.color565(25, 25, 25); 667 tft.fillRect(0, 0, SCREEN_WIDTH, STATUS_BAR_HEIGHT, darkerGrey); 668 669 struct tm timeinfo; 670 char timeStr[9]; 671 if (!getLocalTime(&timeinfo)) { 672 sprintf(timeStr, "12:00 AM"); 673 } else { 674 int hour = timeinfo.tm_hour; 675 char ampm[] = "AM"; 676 if (hour == 0) { 677 hour = 12; 678 } else if (hour >= 12) { 679 if (hour > 12) 680 hour -= 12; 681 strcpy(ampm, "PM"); 682 } 683 sprintf(timeStr, "%02d:%02d %s", hour, timeinfo.tm_min, ampm); 684 } 685 tft.setTextDatum(ML_DATUM); 686 tft.setTextColor(TFT_WHITE, darkerGrey); 687 tft.drawString(timeStr, 0, STATUS_BAR_HEIGHT / 2); 688 689 char wifiStr[15]; 690 int wifiSignal = 0; 691 if (WiFi.status() != WL_CONNECTED) { 692 sprintf(wifiStr, "WiFi: N/A"); 693 tft.setTextColor(TFT_PINK, darkerGrey); 694 tft.setTextDatum(MR_DATUM); 695 tft.drawString(wifiStr, SCREEN_WIDTH - 100, STATUS_BAR_HEIGHT / 2); 696 } else { 697 int32_t rssi = WiFi.RSSI(); 698 if (rssi > -50) wifiSignal = 100; 699 else if (rssi > -60) wifiSignal = 80; 700 else if (rssi > -70) wifiSignal = 60; 701 else if (rssi > -80) wifiSignal = 40; 702 else if (rssi > -90) wifiSignal = 20; 703 else wifiSignal = 0; 704 705 sprintf(wifiStr, "WiFi: %d%%", wifiSignal); 706 tft.setTextDatum(MR_DATUM); 707 tft.setTextColor(TFT_PINK, darkerGrey); 708 tft.drawString("WiFi:", SCREEN_WIDTH - 120, STATUS_BAR_HEIGHT / 2); 709 tft.setTextColor(getColorFromPercentage(wifiSignal), darkerGrey); 710 tft.drawString(wifiStr + 6, SCREEN_WIDTH - 100, STATUS_BAR_HEIGHT / 2); 711 } 712 713 int batteryLevel = BL.getBatteryChargeLevel(); 714 char batteryStr[15]; 715 sprintf(batteryStr, "Batt: %d%%", batteryLevel); 716 tft.setTextDatum(MR_DATUM); 717 tft.setTextColor(TFT_CYAN, darkerGrey); 718 tft.drawString("Batt:", SCREEN_WIDTH - 40, STATUS_BAR_HEIGHT / 2); 719 tft.setTextColor(getColorFromPercentage(batteryLevel), darkerGrey); 720 tft.drawString(batteryStr + 5, SCREEN_WIDTH - 5, STATUS_BAR_HEIGHT / 2); 721 }