├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── stale.yml ├── .gitignore ├── Action.h ├── ConfigHelper.h ├── ConfigLoad.h ├── DrawHelper.h ├── FreeTouchDeck.ino ├── Keydefines.h ├── LICENSE ├── README.md ├── ScreenHelper.h ├── Touch.h ├── UserActions.h ├── Webserver.h ├── case └── ESP32_TFT_Combiner_Case │ ├── Bottom.stl │ ├── Case_for_tft_with_headers.png │ ├── FreeTouchDeck Case V1.f3z │ ├── FreeTouchDeck_Case.png │ ├── Top.stl │ ├── Top_for_TFT_with_Headers.stl │ ├── bottom_screenshot.jpg │ ├── freetouchdeck_case1.jpg │ ├── readme.md │ └── top_screenshot.jpg ├── data ├── config │ ├── default.json │ ├── general.json │ ├── homescreen.json │ ├── menu1.json │ ├── menu2.json │ ├── menu3.json │ ├── menu4.json │ ├── menu5.json │ └── wificonfig.json ├── editor.htm ├── error.htm ├── favicon.ico ├── index.htm ├── jquery-3.5.1.slim.min.js ├── logos │ ├── brightnessdown.bmp │ ├── brightnessup.bmp │ ├── dslr.bmp │ ├── firefox.bmp │ ├── freetouchdeck_logo.bmp │ ├── home.bmp │ ├── info.bmp │ ├── mail.bmp │ ├── micmute.bmp │ ├── music.bmp │ ├── mute.bmp │ ├── obs.bmp │ ├── play.bmp │ ├── question.bmp │ ├── settings.bmp │ ├── sleep.bmp │ ├── spanner.bmp │ ├── splash.bmp │ ├── stop.bmp │ ├── volumedown.bmp │ ├── volumeup.bmp │ ├── webcam.bmp │ ├── wifi.bmp │ └── youtube.bmp ├── saveconfig.htm └── upload.htm └── user_setup.h Examples ├── ESP32_Dev_Kit_V1_ILI9488_Resistive.h ├── ESP32_Dev_Kit_V1_ILI9488_Resistive.png ├── ESP32_TouchDown_User_Setup.h ├── Makerfabs_Capacitive_Touch_User_Setup.h └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dustinwatts 2 | patreon: dustinwatts 3 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 2 | # 3 | # You can adjust the behavior by modifying this file. 4 | # For more information, see: 5 | # https://github.com/actions/stale 6 | name: Mark stale issues and pull requests 7 | 8 | on: 9 | workflow_dispatch: 10 | schedule: 11 | - cron: '20 21 * * *' 12 | 13 | jobs: 14 | stale: 15 | 16 | runs-on: ubuntu-latest 17 | permissions: 18 | issues: write 19 | pull-requests: write 20 | 21 | steps: 22 | - uses: actions/stale@v5 23 | with: 24 | repo-token: ${{ secrets.GITHUB_TOKEN }} 25 | stale-issue-message: 'This issue has been stale for a while now' 26 | stale-pr-message: 'This pull requesst has been stale for a while now' 27 | stale-issue-label: 'Stale Issue' 28 | stale-pr-label: 'Stale PR' 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | data/config/wificonfig.json 4 | .vscode/c_cpp_properties.json 5 | .vscode/arduino.json 6 | .vscode/settings.json 7 | .c_cpp_properties 8 | .pio -------------------------------------------------------------------------------- /Action.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief This function takes an int as an "action" and "value". It uses 3 | a switch statement to determine which type of action to do. 4 | e.g. write, print, press. If an action requires a char, you 5 | can pass the pointer to that char through the parameter "symbol" 6 | * 7 | * @param action int 8 | * @param value int 9 | * @param symbol char * 10 | * 11 | * @return none 12 | * 13 | * @note Case 11 is used for special functions, none bleKeyboard related. 14 | */ 15 | 16 | void bleKeyboardAction(int action, int value, char *symbol) 17 | { 18 | 19 | Serial.println("[INFO]: BLE Keyboard action received"); 20 | 21 | switch (action) 22 | { 23 | case 0: 24 | // No Action 25 | break; 26 | case 1: // Delay 27 | delay(value); 28 | break; 29 | case 2: // Send TAB ARROW etc 30 | switch (value) 31 | { 32 | case 1: 33 | bleKeyboard.write(KEY_UP_ARROW); 34 | break; 35 | case 2: 36 | bleKeyboard.write(KEY_DOWN_ARROW); 37 | break; 38 | case 3: 39 | bleKeyboard.write(KEY_LEFT_ARROW); 40 | break; 41 | case 4: 42 | bleKeyboard.write(KEY_RIGHT_ARROW); 43 | break; 44 | case 5: 45 | bleKeyboard.write(KEY_BACKSPACE); 46 | break; 47 | case 6: 48 | bleKeyboard.write(KEY_TAB); 49 | break; 50 | case 7: 51 | bleKeyboard.write(KEY_RETURN); 52 | break; 53 | case 8: 54 | bleKeyboard.write(KEY_PAGE_UP); 55 | break; 56 | case 9: 57 | bleKeyboard.write(KEY_PAGE_DOWN); 58 | break; 59 | case 10: 60 | bleKeyboard.write(KEY_DELETE); 61 | break; 62 | case 11: 63 | bleKeyboard.write(KEY_PRTSC); 64 | break; 65 | case 12: 66 | bleKeyboard.write(KEY_ESC); 67 | break; 68 | case 13: 69 | bleKeyboard.write(KEY_HOME); 70 | break; 71 | case 14: 72 | bleKeyboard.write(KEY_END); 73 | break; 74 | 75 | default: 76 | //if nothing matches do nothing 77 | break; 78 | } 79 | break; 80 | case 3: // Send Media Key 81 | #if defined(USEUSBHID) 82 | 83 | #else 84 | switch (value) 85 | { 86 | case 1: 87 | bleKeyboard.write(KEY_MEDIA_MUTE); 88 | break; 89 | case 2: 90 | bleKeyboard.write(KEY_MEDIA_VOLUME_DOWN); 91 | break; 92 | case 3: 93 | bleKeyboard.write(KEY_MEDIA_VOLUME_UP); 94 | break; 95 | case 4: 96 | bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE); 97 | break; 98 | case 5: 99 | bleKeyboard.write(KEY_MEDIA_STOP); 100 | break; 101 | case 6: 102 | bleKeyboard.write(KEY_MEDIA_NEXT_TRACK); 103 | break; 104 | case 7: 105 | bleKeyboard.write(KEY_MEDIA_PREVIOUS_TRACK); 106 | break; 107 | default: 108 | //if nothing matches do nothing 109 | break; 110 | } 111 | break; 112 | #endif //if defined(USEUSBHID) 113 | case 4: // Send Character 114 | bleKeyboard.print(symbol); 115 | break; 116 | case 5: // Option Keys 117 | switch (value) 118 | { 119 | case 1: 120 | bleKeyboard.press(KEY_LEFT_CTRL); 121 | break; 122 | case 2: 123 | bleKeyboard.press(KEY_LEFT_SHIFT); 124 | break; 125 | case 3: 126 | bleKeyboard.press(KEY_LEFT_ALT); 127 | break; 128 | case 4: 129 | bleKeyboard.press(KEY_LEFT_GUI); 130 | break; 131 | case 5: 132 | bleKeyboard.press(KEY_RIGHT_CTRL); 133 | break; 134 | case 6: 135 | bleKeyboard.press(KEY_RIGHT_SHIFT); 136 | break; 137 | case 7: 138 | bleKeyboard.press(KEY_RIGHT_ALT); 139 | break; 140 | case 8: 141 | bleKeyboard.press(KEY_RIGHT_GUI); 142 | break; 143 | case 9: 144 | bleKeyboard.releaseAll(); 145 | break; 146 | default: 147 | //if nothing matches do nothing 148 | break; 149 | } 150 | break; 151 | case 6: // Function Keys 152 | switch (value) 153 | { 154 | case 1: 155 | bleKeyboard.press(KEY_F1); 156 | break; 157 | case 2: 158 | bleKeyboard.press(KEY_F2); 159 | break; 160 | case 3: 161 | bleKeyboard.press(KEY_F3); 162 | break; 163 | case 4: 164 | bleKeyboard.press(KEY_F4); 165 | break; 166 | case 5: 167 | bleKeyboard.press(KEY_F5); 168 | break; 169 | case 6: 170 | bleKeyboard.press(KEY_F6); 171 | break; 172 | case 7: 173 | bleKeyboard.press(KEY_F7); 174 | break; 175 | case 8: 176 | bleKeyboard.press(KEY_F8); 177 | break; 178 | case 9: 179 | bleKeyboard.press(KEY_F9); 180 | break; 181 | case 10: 182 | bleKeyboard.press(KEY_F10); 183 | break; 184 | case 11: 185 | bleKeyboard.press(KEY_F11); 186 | break; 187 | case 12: 188 | bleKeyboard.press(KEY_F12); 189 | break; 190 | case 13: 191 | bleKeyboard.press(KEY_F13); 192 | break; 193 | case 14: 194 | bleKeyboard.press(KEY_F14); 195 | break; 196 | case 15: 197 | bleKeyboard.press(KEY_F15); 198 | break; 199 | case 16: 200 | bleKeyboard.press(KEY_F16); 201 | break; 202 | case 17: 203 | bleKeyboard.press(KEY_F17); 204 | break; 205 | case 18: 206 | bleKeyboard.press(KEY_F18); 207 | break; 208 | case 19: 209 | bleKeyboard.press(KEY_F19); 210 | break; 211 | case 20: 212 | bleKeyboard.press(KEY_F20); 213 | break; 214 | case 21: 215 | bleKeyboard.press(KEY_F21); 216 | break; 217 | case 22: 218 | bleKeyboard.press(KEY_F22); 219 | break; 220 | case 23: 221 | bleKeyboard.press(KEY_F23); 222 | break; 223 | case 24: 224 | bleKeyboard.press(KEY_F24); 225 | break; 226 | default: 227 | //if nothing matches do nothing 228 | break; 229 | } 230 | break; 231 | case 7: // Send Number 232 | bleKeyboard.print(value); 233 | break; 234 | case 8: // Send Special Character 235 | bleKeyboard.print(symbol); 236 | break; 237 | case 9: // Combos 238 | switch (value) 239 | { 240 | case 1: 241 | bleKeyboard.press(KEY_LEFT_CTRL); 242 | bleKeyboard.press(KEY_LEFT_SHIFT); 243 | break; 244 | case 2: 245 | bleKeyboard.press(KEY_LEFT_ALT); 246 | bleKeyboard.press(KEY_LEFT_SHIFT); 247 | break; 248 | case 3: 249 | bleKeyboard.press(KEY_LEFT_GUI); 250 | bleKeyboard.press(KEY_LEFT_SHIFT); 251 | break; 252 | case 4: 253 | bleKeyboard.press(KEY_LEFT_CTRL); 254 | bleKeyboard.press(KEY_LEFT_GUI); 255 | break; 256 | case 5: 257 | bleKeyboard.press(KEY_LEFT_ALT); 258 | bleKeyboard.press(KEY_LEFT_GUI); 259 | break; 260 | case 6: 261 | bleKeyboard.press(KEY_LEFT_CTRL); 262 | bleKeyboard.press(KEY_LEFT_ALT); 263 | break; 264 | case 7: 265 | bleKeyboard.press(KEY_LEFT_CTRL); 266 | bleKeyboard.press(KEY_LEFT_ALT); 267 | bleKeyboard.press(KEY_LEFT_GUI); 268 | break; 269 | case 8: 270 | bleKeyboard.press(KEY_RIGHT_CTRL); 271 | bleKeyboard.press(KEY_RIGHT_SHIFT); 272 | break; 273 | case 9: 274 | bleKeyboard.press(KEY_RIGHT_ALT); 275 | bleKeyboard.press(KEY_RIGHT_SHIFT); 276 | break; 277 | case 10: 278 | bleKeyboard.press(KEY_RIGHT_GUI); 279 | bleKeyboard.press(KEY_RIGHT_SHIFT); 280 | break; 281 | case 11: 282 | bleKeyboard.press(KEY_RIGHT_CTRL); 283 | bleKeyboard.press(KEY_RIGHT_GUI); 284 | break; 285 | case 12: 286 | bleKeyboard.press(KEY_RIGHT_ALT); 287 | bleKeyboard.press(KEY_RIGHT_GUI); 288 | break; 289 | case 13: 290 | bleKeyboard.press(KEY_RIGHT_CTRL); 291 | bleKeyboard.press(KEY_RIGHT_ALT); 292 | break; 293 | case 14: 294 | bleKeyboard.press(KEY_RIGHT_CTRL); 295 | bleKeyboard.press(KEY_RIGHT_ALT); 296 | bleKeyboard.press(KEY_RIGHT_GUI); 297 | break; 298 | } 299 | break; 300 | case 10: // Helpers 301 | switch (value) 302 | { 303 | case 1: 304 | if(generalconfig.modifier1 != 0){ 305 | bleKeyboard.press(generalconfig.modifier1); 306 | } 307 | if(generalconfig.modifier2 != 0){ 308 | bleKeyboard.press(generalconfig.modifier2); 309 | } 310 | if(generalconfig.modifier3 != 0){ 311 | bleKeyboard.press(generalconfig.modifier3); 312 | } 313 | bleKeyboard.press(KEY_F1); 314 | bleKeyboard.releaseAll(); 315 | delay(generalconfig.helperdelay); 316 | break; 317 | case 2: 318 | if(generalconfig.modifier1 != 0){ 319 | bleKeyboard.press(generalconfig.modifier1); 320 | } 321 | if(generalconfig.modifier2 != 0){ 322 | bleKeyboard.press(generalconfig.modifier2); 323 | } 324 | if(generalconfig.modifier3 != 0){ 325 | bleKeyboard.press(generalconfig.modifier3); 326 | } 327 | bleKeyboard.press(KEY_F2); 328 | bleKeyboard.releaseAll(); 329 | delay(generalconfig.helperdelay); 330 | break; 331 | case 3: 332 | if(generalconfig.modifier1 != 0){ 333 | bleKeyboard.press(generalconfig.modifier1); 334 | } 335 | if(generalconfig.modifier2 != 0){ 336 | bleKeyboard.press(generalconfig.modifier2); 337 | } 338 | if(generalconfig.modifier3 != 0){ 339 | bleKeyboard.press(generalconfig.modifier3); 340 | } 341 | bleKeyboard.press(KEY_F3); 342 | bleKeyboard.releaseAll(); 343 | delay(generalconfig.helperdelay); 344 | break; 345 | case 4: 346 | if(generalconfig.modifier1 != 0){ 347 | bleKeyboard.press(generalconfig.modifier1); 348 | } 349 | if(generalconfig.modifier2 != 0){ 350 | bleKeyboard.press(generalconfig.modifier2); 351 | } 352 | if(generalconfig.modifier3 != 0){ 353 | bleKeyboard.press(generalconfig.modifier3); 354 | } 355 | bleKeyboard.press(KEY_F4); 356 | bleKeyboard.releaseAll(); 357 | delay(generalconfig.helperdelay); 358 | break; 359 | case 5: 360 | if(generalconfig.modifier1 != 0){ 361 | bleKeyboard.press(generalconfig.modifier1); 362 | } 363 | if(generalconfig.modifier2 != 0){ 364 | bleKeyboard.press(generalconfig.modifier2); 365 | } 366 | if(generalconfig.modifier3 != 0){ 367 | bleKeyboard.press(generalconfig.modifier3); 368 | } 369 | bleKeyboard.press(KEY_F5); 370 | bleKeyboard.releaseAll(); 371 | delay(generalconfig.helperdelay); 372 | break; 373 | case 6: 374 | if(generalconfig.modifier1 != 0){ 375 | bleKeyboard.press(generalconfig.modifier1); 376 | } 377 | if(generalconfig.modifier2 != 0){ 378 | bleKeyboard.press(generalconfig.modifier2); 379 | } 380 | if(generalconfig.modifier3 != 0){ 381 | bleKeyboard.press(generalconfig.modifier3); 382 | } 383 | bleKeyboard.press(KEY_F6); 384 | bleKeyboard.releaseAll(); 385 | delay(generalconfig.helperdelay); 386 | break; 387 | case 7: 388 | if(generalconfig.modifier1 != 0){ 389 | bleKeyboard.press(generalconfig.modifier1); 390 | } 391 | if(generalconfig.modifier2 != 0){ 392 | bleKeyboard.press(generalconfig.modifier2); 393 | } 394 | if(generalconfig.modifier3 != 0){ 395 | bleKeyboard.press(generalconfig.modifier3); 396 | } 397 | bleKeyboard.press(KEY_F7); 398 | bleKeyboard.releaseAll(); 399 | delay(generalconfig.helperdelay); 400 | break; 401 | case 8: 402 | if(generalconfig.modifier1 != 0){ 403 | bleKeyboard.press(generalconfig.modifier1); 404 | } 405 | if(generalconfig.modifier2 != 0){ 406 | bleKeyboard.press(generalconfig.modifier2); 407 | } 408 | if(generalconfig.modifier3 != 0){ 409 | bleKeyboard.press(generalconfig.modifier3); 410 | } 411 | bleKeyboard.press(KEY_F8); 412 | bleKeyboard.releaseAll(); 413 | delay(generalconfig.helperdelay); 414 | break; 415 | case 9: 416 | if(generalconfig.modifier1 != 0){ 417 | bleKeyboard.press(generalconfig.modifier1); 418 | } 419 | if(generalconfig.modifier2 != 0){ 420 | bleKeyboard.press(generalconfig.modifier2); 421 | } 422 | if(generalconfig.modifier3 != 0){ 423 | bleKeyboard.press(generalconfig.modifier3); 424 | } 425 | bleKeyboard.press(KEY_F9); 426 | bleKeyboard.releaseAll(); 427 | delay(generalconfig.helperdelay); 428 | break; 429 | case 10: 430 | if(generalconfig.modifier1 != 0){ 431 | bleKeyboard.press(generalconfig.modifier1); 432 | } 433 | if(generalconfig.modifier2 != 0){ 434 | bleKeyboard.press(generalconfig.modifier2); 435 | } 436 | if(generalconfig.modifier3 != 0){ 437 | bleKeyboard.press(generalconfig.modifier3); 438 | } 439 | bleKeyboard.press(KEY_F10); 440 | bleKeyboard.releaseAll(); 441 | delay(generalconfig.helperdelay); 442 | break; 443 | case 11: 444 | if(generalconfig.modifier1 != 0){ 445 | bleKeyboard.press(generalconfig.modifier1); 446 | } 447 | if(generalconfig.modifier2 != 0){ 448 | bleKeyboard.press(generalconfig.modifier2); 449 | } 450 | if(generalconfig.modifier3 != 0){ 451 | bleKeyboard.press(generalconfig.modifier3); 452 | } 453 | bleKeyboard.press(KEY_F11); 454 | bleKeyboard.releaseAll(); 455 | delay(generalconfig.helperdelay); 456 | break; 457 | } 458 | break; 459 | case 11: // Special functions 460 | switch (value) 461 | { 462 | case 1: // Enter config mode 463 | 464 | configmode(); 465 | 466 | break; 467 | case 2: // Display Brightness Down 468 | if (ledBrightness > 25) 469 | { 470 | ledBrightness = ledBrightness - 25; 471 | ledcWrite(0, ledBrightness); 472 | savedStates.putInt("ledBrightness", ledBrightness); 473 | } 474 | break; 475 | case 3: // Display Brightness Up 476 | if (ledBrightness < 230) 477 | { 478 | ledBrightness = ledBrightness + 25; 479 | ledcWrite(0, ledBrightness); 480 | savedStates.putInt("ledBrightness", ledBrightness); 481 | } 482 | break; 483 | case 4: // Sleep Enabled 484 | if (generalconfig.sleepenable) 485 | { 486 | generalconfig.sleepenable = false; 487 | Serial.println("[INFO]: Sleep disabled."); 488 | } 489 | else 490 | { 491 | generalconfig.sleepenable = true; 492 | Interval = generalconfig.sleeptimer * 60000; 493 | Serial.println("[INFO]: Sleep enabled."); 494 | Serial.print("[INFO]: Timer set to: "); 495 | Serial.println(generalconfig.sleeptimer); 496 | } 497 | break; 498 | } 499 | break; 500 | case 12: // Numpad 501 | switch (value) 502 | { 503 | case 0: 504 | bleKeyboard.write(KEY_NUM_0); 505 | break; 506 | case 1: 507 | bleKeyboard.write(KEY_NUM_1); 508 | break; 509 | case 2: 510 | bleKeyboard.write(KEY_NUM_2); 511 | break; 512 | case 3: 513 | bleKeyboard.write(KEY_NUM_3); 514 | break; 515 | case 4: 516 | bleKeyboard.write(KEY_NUM_4); 517 | break; 518 | case 5: 519 | bleKeyboard.write(KEY_NUM_5); 520 | break; 521 | case 6: 522 | bleKeyboard.write(KEY_NUM_6); 523 | break; 524 | case 7: 525 | bleKeyboard.write(KEY_NUM_7); 526 | break; 527 | case 8: 528 | bleKeyboard.write(KEY_NUM_8); 529 | break; 530 | case 9: 531 | bleKeyboard.write(KEY_NUM_9); 532 | break; 533 | case 10: 534 | bleKeyboard.write(KEY_NUM_SLASH); 535 | break; 536 | case 11: 537 | bleKeyboard.write(KEY_NUM_ASTERISK); 538 | break; 539 | case 12: 540 | bleKeyboard.write(KEY_NUM_MINUS); 541 | break; 542 | case 13: 543 | bleKeyboard.write(KEY_NUM_PLUS); 544 | break; 545 | case 14: 546 | bleKeyboard.write(KEY_NUM_ENTER); 547 | break; 548 | case 15: 549 | bleKeyboard.write(KEY_NUM_PERIOD); 550 | break; 551 | } 552 | break; 553 | case 13: // Custom functions 554 | switch (value) 555 | { 556 | case 1: 557 | userAction1(); 558 | break; 559 | case 2: 560 | userAction2(); 561 | break; 562 | case 3: 563 | userAction3(); 564 | break; 565 | case 4: 566 | userAction4(); 567 | break; 568 | case 5: 569 | userAction5(); 570 | break; 571 | case 6: 572 | userAction6(); 573 | break; 574 | case 7: 575 | userAction7(); 576 | break; 577 | } 578 | break; 579 | default: 580 | //If nothing matches do nothing 581 | break; 582 | } 583 | } 584 | -------------------------------------------------------------------------------- /ConfigHelper.h: -------------------------------------------------------------------------------- 1 | // Start as WiFi station 2 | 3 | bool startWifiStation(bool stopble, bool startwebserver){ 4 | 5 | Serial.printf("[INFO]: Connecting to %s", wificonfig.ssid); 6 | if (String(WiFi.SSID()) != String(wificonfig.ssid)) 7 | { 8 | WiFi.mode(WIFI_STA); 9 | WiFi.begin(wificonfig.ssid, wificonfig.password); 10 | uint8_t attempts = wificonfig.attempts; 11 | while (WiFi.status() != WL_CONNECTED) 12 | { 13 | if(attempts == 0) { 14 | WiFi.disconnect(); 15 | Serial.println(""); 16 | return false; 17 | 18 | } 19 | delay(wificonfig.attemptdelay); 20 | Serial.print("."); 21 | attempts--; 22 | 23 | } 24 | } 25 | if(stopble){ 26 | // Delete the task bleKeyboard had create to free memory and to not interfere with AsyncWebServer 27 | bleKeyboard.end(); 28 | 29 | // Stop BLE from interfering with our WIFI signal 30 | btStop(); 31 | esp_bt_controller_disable(); 32 | esp_bt_controller_deinit(); 33 | esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); 34 | 35 | Serial.println(""); 36 | Serial.println("[INFO]: BLE Stopped"); 37 | // Set pageNum to 7 so no buttons are displayed and touches are ignored 38 | pageNum = 7; 39 | } 40 | Serial.print("[INFO]: Connected! IP address: "); 41 | Serial.println(WiFi.localIP()); 42 | 43 | if(startwebserver){ 44 | // Open port 80 45 | MDNS.begin(wificonfig.hostname); 46 | MDNS.addService("http", "tcp", 80); 47 | // Start the webserver 48 | webserver.begin(); 49 | Serial.println("[INFO]: Webserver started"); 50 | } 51 | return true; 52 | } 53 | 54 | // Start as WiFi AP 55 | 56 | void startWifiAP(){ 57 | 58 | WiFi.mode(WIFI_AP); 59 | WiFi.softAP(wificonfig.ssid, wificonfig.password); 60 | Serial.println(""); 61 | Serial.print("[INFO]: Access Point Started! IP address: "); 62 | Serial.println(WiFi.softAPIP()); 63 | 64 | // Delete the task bleKeyboard had create to free memory and to not interfere with AsyncWebServer 65 | bleKeyboard.end(); 66 | 67 | // Stop BLE from interfering with our WIFI signal 68 | btStop(); 69 | esp_bt_controller_disable(); 70 | esp_bt_controller_deinit(); 71 | esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); 72 | 73 | Serial.println(""); 74 | Serial.println("[INFO]: BLE Stopped"); 75 | 76 | MDNS.begin(wificonfig.hostname); 77 | MDNS.addService("http", "tcp", 80); 78 | 79 | // Set pageNum to 7 so no buttons are displayed and touches are ignored 80 | pageNum = 7; 81 | 82 | // Start the webserver 83 | webserver.begin(); 84 | Serial.println("[INFO]: Webserver started"); 85 | } 86 | 87 | // Start the default AP 88 | 89 | void startDefaultAP(){ 90 | 91 | const char* ssid = "FreeTouchDeck"; 92 | const char* password = "defaultpass"; 93 | 94 | WiFi.mode(WIFI_AP); 95 | WiFi.softAP(ssid, password); 96 | Serial.print("[INFO]: Access Point Started! IP address: "); 97 | Serial.println(WiFi.softAPIP()); 98 | 99 | // Delete the task bleKeyboard had create to free memory and to not interfere with AsyncWebServer 100 | bleKeyboard.end(); 101 | 102 | // Stop BLE from interfering with our WIFI signal 103 | btStop(); 104 | esp_bt_controller_disable(); 105 | esp_bt_controller_deinit(); 106 | esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); 107 | 108 | Serial.println("[INFO]: BLE Stopped"); 109 | 110 | MDNS.begin("freetouchdeck"); 111 | MDNS.addService("http", "tcp", 80); 112 | 113 | // Set pageNum to 7 so no buttons are displayed and touches are ignored 114 | pageNum = 7; 115 | 116 | // Start the webserver 117 | webserver.begin(); 118 | Serial.println("[INFO]: Webserver started"); 119 | 120 | } 121 | 122 | /** 123 | * @brief This function stops Bluetooth and connects to the given 124 | WiFi network. It the starts mDNS and starts the Async 125 | Webserver. 126 | * 127 | * @param none 128 | * 129 | * @return none 130 | * 131 | * @note none 132 | */ 133 | void configmode() 134 | { 135 | 136 | tft.fillScreen(TFT_BLACK); 137 | tft.setCursor(0, 0); 138 | tft.setTextFont(2); 139 | tft.setTextSize(1); 140 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 141 | 142 | Serial.println("[INFO]: Entering Config Mode"); 143 | tft.println("Connecting to Wifi..."); 144 | 145 | if (String(wificonfig.ssid) == "YOUR_WIFI_SSID" || String(wificonfig.password) == "YOUR_WIFI_PASSWORD") // Still default 146 | { 147 | tft.println("WiFi Config still set to default! Starting as AP."); 148 | Serial.println("[WARNING]: WiFi Config still set to default! Configurator started as AP."); 149 | startDefaultAP(); 150 | tft.println("Started as AP because WiFi settings are still set to default."); 151 | tft.println("To configure, connect to 'FreeTouchDeck' with password 'defaultpass'"); 152 | tft.println("Then go to http://freetouchdeck.local"); 153 | tft.print("The IP is: "); 154 | tft.println(WiFi.softAPIP()); 155 | drawSingleButton(140, 180, 200, 80, generalconfig.menuButtonColour, TFT_WHITE, "Restart"); 156 | return; 157 | } 158 | 159 | if (String(wificonfig.ssid) == "FAILED" || String(wificonfig.password) == "FAILED" || String(wificonfig.wifimode) == "FAILED") // The wificonfig.json failed to load 160 | { 161 | tft.println("WiFi Config Failed to load! Starting as AP."); 162 | Serial.println("[WARNING]: WiFi Config Failed to load! Configurator started as AP."); 163 | startDefaultAP(); 164 | tft.println("Started as AP because WiFi settings failed to load."); 165 | tft.println("To configure, connect to 'FreeTouchDeck' with password 'defaultpass'"); 166 | tft.println("Then go to http://freetouchdeck.local"); 167 | tft.print("The IP is: "); 168 | tft.println(WiFi.softAPIP()); 169 | drawSingleButton(140, 180, 200, 80, generalconfig.menuButtonColour, TFT_WHITE, "Restart"); 170 | return; 171 | } 172 | 173 | if (strcmp(wificonfig.wifimode, "WIFI_STA") == 0) 174 | { 175 | if(!startWifiStation(true, true)){ 176 | startDefaultAP(); 177 | Serial.println("[WARNING]: Could not connect to AP, so started as AP."); 178 | tft.println("Started as AP because WiFi connection failed."); 179 | tft.println("To configure, connect to 'FreeTouchDeck' with password 'defaultpass'"); 180 | tft.println("Then go to http://freetouchdeck.local"); 181 | tft.print("The IP is: "); 182 | tft.println(WiFi.softAPIP()); 183 | drawSingleButton(140, 180, 200, 80, generalconfig.menuButtonColour, TFT_WHITE, "Restart"); 184 | } 185 | else 186 | { 187 | tft.println("Started as STA and in config mode."); 188 | tft.println("To configure:"); 189 | tft.println("http://freetouchdeck.local"); 190 | tft.print("The IP is: "); 191 | tft.println(WiFi.localIP()); 192 | drawSingleButton(140, 180, 200, 80, generalconfig.menuButtonColour, TFT_WHITE, "Restart"); 193 | } 194 | 195 | } 196 | else if (strcmp(wificonfig.wifimode, "WIFI_AP") == 0) 197 | { 198 | startWifiAP(); 199 | tft.println("Started as AP and in config mode."); 200 | tft.println("To configure:"); 201 | tft.println("http://freetouchdeck.local"); 202 | tft.print("The IP is: "); 203 | tft.println(WiFi.softAPIP()); 204 | drawSingleButton(140, 180, 200, 80, generalconfig.menuButtonColour, TFT_WHITE, "Restart"); 205 | } 206 | } 207 | 208 | 209 | /** 210 | * @brief This function allows for saving (updating) the WiFi SSID 211 | * 212 | * @param String ssid 213 | * 214 | * @return boolean True if succeeded. False otherwise. 215 | * 216 | * @note Returns true if successful. To enable the new set SSID, you must reload the the 217 | configuration using loadMainConfig() 218 | */ 219 | bool saveWifiSSID(String ssid) 220 | { 221 | 222 | FILESYSTEM.remove("/config/wificonfig.json"); 223 | File file = FILESYSTEM.open("/config/wificonfig.json", "w"); 224 | 225 | DynamicJsonDocument doc(384); 226 | 227 | JsonObject wificonfigobject = doc.to(); 228 | 229 | wificonfigobject["ssid"] = ssid; 230 | wificonfigobject["password"] = wificonfig.password; 231 | wificonfigobject["wifimode"] = wificonfig.wifimode; 232 | wificonfigobject["wifihostname"] = wificonfig.hostname; 233 | wificonfigobject["attempts"] = wificonfig.attempts; 234 | wificonfigobject["attemptdelay"] = wificonfig.attemptdelay; 235 | 236 | 237 | if (serializeJsonPretty(doc, file) == 0) 238 | { 239 | Serial.println("[WARNING]: Failed to write to file"); 240 | return false; 241 | } 242 | file.close(); 243 | return true; 244 | } 245 | 246 | /** 247 | * @brief This function allows for saving (updating) the WiFi Password 248 | * 249 | * @param String password 250 | * 251 | * @return boolean True if succeeded. False otherwise. 252 | * 253 | * @note Returns true if successful. To enable the new set password, you must reload the the 254 | configuration using loadMainConfig() 255 | */ 256 | bool saveWifiPW(String password) 257 | { 258 | 259 | FILESYSTEM.remove("/config/wificonfig.json"); 260 | File file = FILESYSTEM.open("/config/wificonfig.json", "w"); 261 | 262 | DynamicJsonDocument doc(384); 263 | 264 | JsonObject wificonfigobject = doc.to(); 265 | 266 | wificonfigobject["ssid"] = wificonfig.ssid; 267 | wificonfigobject["password"] = password; 268 | wificonfigobject["wifimode"] = wificonfig.wifimode; 269 | wificonfigobject["wifihostname"] = wificonfig.hostname; 270 | wificonfigobject["attempts"] = wificonfig.attempts; 271 | wificonfigobject["attemptdelay"] = wificonfig.attemptdelay; 272 | 273 | 274 | if (serializeJsonPretty(doc, file) == 0) 275 | { 276 | Serial.println("[WARNING]: Failed to write to file"); 277 | return false; 278 | } 279 | file.close(); 280 | return true; 281 | } 282 | 283 | /** 284 | * @brief This function allows for saving (updating) the WiFi Mode 285 | * 286 | * @param String wifimode "WIFI_STA" of "WIFI_AP" 287 | * 288 | * @return boolean True if succeeded. False otherwise. 289 | * 290 | * @note Returns true if successful. To enable the new set WiFi Mode, you must reload the the 291 | configuration using loadMainConfig() 292 | */ 293 | bool saveWifiMode(String wifimode) 294 | { 295 | 296 | if (wifimode != "WIFI_STA" && wifimode != "WIFI_AP") 297 | { 298 | Serial.println("[WARNING]: WiFi Mode not supported. Try WIFI_STA or WIFI_AP."); 299 | return false; 300 | } 301 | 302 | FILESYSTEM.remove("/config/wificonfig.json"); 303 | File file = FILESYSTEM.open("/config/wificonfig.json", "w"); 304 | 305 | DynamicJsonDocument doc(384); 306 | 307 | JsonObject wificonfigobject = doc.to(); 308 | 309 | wificonfigobject["ssid"] = wificonfig.ssid; 310 | wificonfigobject["password"] = wificonfig.password; 311 | wificonfigobject["wifimode"] = wifimode; 312 | wificonfigobject["wifihostname"] = wificonfig.hostname; 313 | wificonfigobject["attempts"] = wificonfig.attempts; 314 | wificonfigobject["attemptdelay"] = wificonfig.attemptdelay; 315 | 316 | 317 | if (serializeJsonPretty(doc, file) == 0) 318 | { 319 | Serial.println("[WARNING]: Failed to write to file"); 320 | return false; 321 | } 322 | file.close(); 323 | return true; 324 | } 325 | 326 | /** 327 | * @brief This function checks if a file exists and returns a boolean accordingly. 328 | It then prints a debug message to the serial as wel as the tft. 329 | * 330 | * @param filename (const char *) 331 | * 332 | * @return boolean True if succeeded. False otherwise. 333 | * 334 | * @note Pass the filename including a leading / 335 | */ 336 | bool checkfile(const char *filename) 337 | { 338 | 339 | if (!FILESYSTEM.exists(filename)) 340 | { 341 | tft.fillScreen(TFT_BLACK); 342 | tft.setCursor(1, 3); 343 | tft.setTextFont(2); 344 | tft.setTextSize(2); 345 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 346 | tft.printf("%s not found!\n\n", filename); 347 | tft.setTextSize(1); 348 | tft.printf("If this has happend after confguration, the data on the ESP may \nbe corrupted."); 349 | return false; 350 | } 351 | else 352 | { 353 | return true; 354 | } 355 | } 356 | 357 | bool resetconfig(String file){ 358 | 359 | if (file != "menu1" && file != "menu2" && file != "menu3" && file != "menu4" && file != "menu5" && file != "homescreen" && file != "general") 360 | { 361 | Serial.println("[WARNING]: Invalid reset option. Choose: menu1, menu2, menu3, menu4, menu5, homescreen, or general"); 362 | return false; 363 | } 364 | 365 | if (file == "menu1" || file == "menu2" || file == "menu3" || file == "menu4" || file == "menu5") 366 | { 367 | // Reset a menu config 368 | 369 | 370 | // Delete the corrupted json file 371 | String filetoremove = "/config/" + file; 372 | if(!filetoremove.endsWith(".json")){ 373 | filetoremove = filetoremove + ".json"; 374 | } 375 | 376 | FILESYSTEM.remove(filetoremove); 377 | 378 | // Copy default.json to the new config file 379 | File defaultfile = FILESYSTEM.open("/config/default.json", "r"); 380 | 381 | size_t n; 382 | uint8_t buf[64]; 383 | 384 | if (defaultfile) { 385 | File newfile = FILESYSTEM.open(filetoremove, "w"); 386 | if (newfile) { 387 | while ((n = defaultfile.read(buf, sizeof(buf))) > 0) { 388 | newfile.write(buf, n); 389 | } 390 | // Close the newly created file 391 | newfile.close(); 392 | } 393 | Serial.println("[INFO]: Done resetting."); 394 | Serial.println("[INFO]: Type \"restart\" to reload configuration."); 395 | 396 | // Close the default.json file 397 | defaultfile.close(); 398 | return true; 399 | } 400 | 401 | } 402 | else if(file == "homescreen") 403 | { 404 | 405 | // Reset the homescreen 406 | // For this we do not need to open a default file because we can easily write it ourselfs 407 | String filetoremove = "/config/" + file; 408 | if(!filetoremove.endsWith(".json")){ 409 | filetoremove = filetoremove + ".json"; 410 | } 411 | 412 | FILESYSTEM.remove(filetoremove); 413 | 414 | File newfile = FILESYSTEM.open(filetoremove, "w"); 415 | newfile.println("{"); 416 | newfile.println("\"logo0\": \"question.bmp\","); 417 | newfile.println("\"logo1\": \"question.bmp\","); 418 | newfile.println("\"logo2\": \"question.bmp\","); 419 | newfile.println("\"logo3\": \"question.bmp\","); 420 | newfile.println("\"logo4\": \"question.bmp\","); 421 | newfile.println("\"logo5\": \"settings.bmp\""); 422 | newfile.println("}"); 423 | 424 | newfile.close(); 425 | Serial.println("[INFO]: Done resetting homescreen."); 426 | Serial.println("[INFO]: Type \"restart\" to reload configuration."); 427 | return true; 428 | 429 | } 430 | else if(file == "general") 431 | { 432 | 433 | // Reset the general config 434 | // For this we do not need to open a default file because we can easily write it ourselfs 435 | 436 | String filetoremove = "/config/" + file; 437 | if(!filetoremove.endsWith(".json")){ 438 | filetoremove = filetoremove + ".json"; 439 | } 440 | 441 | FILESYSTEM.remove(filetoremove); 442 | 443 | File newfile = FILESYSTEM.open(filetoremove, "w"); 444 | newfile.println("{"); 445 | newfile.println("\"menubuttoncolor\": \"#009bf4\","); 446 | newfile.println("\"functionbuttoncolor\": \"#00efcb\","); 447 | newfile.println("\"latchcolor\": \"#fe0149\","); 448 | newfile.println("\"background\": \"#000000\","); 449 | newfile.println("\"sleepenable\": true,"); 450 | newfile.println("\"sleeptimer\": 10,"); 451 | newfile.println("\"beep\": true,"); 452 | newfile.println("\"modifier1\": 130,"); 453 | newfile.println("\"modifier2\": 129,"); 454 | newfile.println("\"modifier3\": 0,"); 455 | newfile.println("\"helperdelay\": 500"); 456 | newfile.println("}"); 457 | 458 | newfile.close(); 459 | Serial.println("[INFO]: Done resetting general config."); 460 | Serial.println("[INFO]: Type \"restart\" to reload configuration."); 461 | return true; 462 | 463 | } 464 | else 465 | { 466 | return false; 467 | } 468 | 469 | return false; 470 | 471 | } 472 | -------------------------------------------------------------------------------- /DrawHelper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief This function draws the a "latched" dot. it uses the logonumber, colomn and row to 3 | * determine where. 4 | * 5 | * @param b int 6 | * @param col int 7 | * @param row int 8 | * 9 | * @return none 10 | * 11 | * @note none 12 | */ 13 | void drawlatched(int b, int col, int row) 14 | { 15 | int offset; 16 | if(SCREEN_WIDTH < 480) 17 | { 18 | offset = 2; 19 | } 20 | else 21 | { 22 | offset = 12; 23 | } 24 | tft.fillRoundRect((KEY_X - 37 + col * (KEY_W + KEY_SPACING_X)) - offset, (KEY_Y - 37 + row * (KEY_H + KEY_SPACING_Y)) - offset, 18, 18, 4, generalconfig.latchedColour); 25 | } 26 | 27 | /** 28 | * @brief This function draws the logos according to the page 29 | we are currently on. The pagenumber is a global variable 30 | and doesn't need to be passed. 31 | * 32 | * @param logonumber int 33 | * @param col int 34 | * @param row int 35 | * @param transparent boolean 36 | * 37 | * @return none 38 | * 39 | * @note Logos start at the top left and are 0 indexed. The same goes 40 | for the colomn and the row. 41 | */ 42 | void drawlogo(int logonumber, int col, int row, bool transparent, bool latch) 43 | { 44 | 45 | if (pageNum == 0) 46 | { 47 | //Draw Home screen logo's 48 | if (logonumber == 0) 49 | { 50 | if (transparent == true) 51 | { 52 | 53 | drawBmpTransparent(screen0.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 54 | } 55 | else 56 | { 57 | drawBmp(screen0.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 58 | } 59 | } 60 | else if (logonumber == 1) 61 | { 62 | if (transparent == true) 63 | { 64 | drawBmpTransparent(screen0.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 65 | } 66 | else 67 | { 68 | drawBmp(screen0.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 69 | } 70 | } 71 | else if (logonumber == 2) 72 | { 73 | if (transparent == true) 74 | { 75 | drawBmpTransparent(screen0.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 76 | } 77 | else 78 | { 79 | drawBmp(screen0.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 80 | } 81 | } 82 | else if (logonumber == 3) 83 | { 84 | if (transparent == true) 85 | { 86 | drawBmpTransparent(screen0.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 87 | } 88 | else 89 | { 90 | drawBmp(screen0.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 91 | } 92 | } 93 | else if (logonumber == 4) 94 | { 95 | if (transparent == true) 96 | { 97 | drawBmpTransparent(screen0.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 98 | } 99 | else 100 | { 101 | drawBmp(screen0.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 102 | } 103 | } 104 | else if (logonumber == 5) 105 | { 106 | if (transparent == true) 107 | { 108 | drawBmpTransparent(screen0.logo5, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 109 | } 110 | else 111 | { 112 | drawBmp(screen0.logo5, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 113 | } 114 | } 115 | } 116 | else if (pageNum == 1) 117 | { 118 | // --------------------------------- MENU 1 -------------------------------------- 119 | if (logonumber == 0) 120 | { 121 | if (transparent == true) 122 | { 123 | 124 | if (latch == true) 125 | { 126 | if (strcmp(menu1.button0.latchlogo, "/logos/") != 0) 127 | { 128 | drawBmpTransparent(menu1.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 129 | } 130 | else 131 | { 132 | drawBmpTransparent(screen1.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 133 | drawlatched(logonumber, col, row); 134 | } 135 | } 136 | else 137 | { 138 | drawBmpTransparent(screen1.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 139 | } 140 | } 141 | else 142 | { 143 | if (latch == true) 144 | { 145 | 146 | if (strcmp(menu1.button0.latchlogo, "/logos/") != 0) 147 | { 148 | drawBmp(menu1.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 149 | } 150 | else 151 | { 152 | drawBmp(screen1.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 153 | drawlatched(logonumber, col, row); 154 | } 155 | } 156 | else 157 | { 158 | drawBmp(screen1.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 159 | } 160 | } 161 | } 162 | else if (logonumber == 1) 163 | { 164 | if (transparent == true) 165 | { 166 | if (latch == true) 167 | { 168 | if (strcmp(menu1.button1.latchlogo, "/logos/") != 0) 169 | { 170 | drawBmpTransparent(menu1.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 171 | } 172 | else 173 | { 174 | drawBmpTransparent(screen1.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 175 | drawlatched(logonumber, col, row); 176 | } 177 | } 178 | else 179 | { 180 | drawBmpTransparent(screen1.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 181 | } 182 | } 183 | else 184 | { 185 | if (latch == true) 186 | { 187 | if (strcmp(menu1.button1.latchlogo, "/logos/") != 0) 188 | { 189 | drawBmp(menu1.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 190 | } 191 | else 192 | { 193 | drawBmp(screen1.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 194 | drawlatched(logonumber, col, row); 195 | } 196 | } 197 | else 198 | { 199 | drawBmp(screen1.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 200 | } 201 | } 202 | } 203 | else if (logonumber == 2) 204 | { 205 | if (transparent == true) 206 | { 207 | 208 | if (latch == true) 209 | { 210 | if (strcmp(menu1.button2.latchlogo, "/logos/") != 0) 211 | { 212 | drawBmpTransparent(menu1.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 213 | } 214 | else 215 | { 216 | drawBmpTransparent(screen1.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 217 | drawlatched(logonumber, col, row); 218 | } 219 | } 220 | else 221 | { 222 | drawBmpTransparent(screen1.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 223 | } 224 | } 225 | else 226 | { 227 | if (latch == true) 228 | { 229 | 230 | if (strcmp(menu1.button2.latchlogo, "/logos/") != 0) 231 | { 232 | drawBmp(menu1.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 233 | } 234 | else 235 | { 236 | drawBmp(screen1.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 237 | drawlatched(logonumber, col, row); 238 | } 239 | } 240 | else 241 | { 242 | drawBmp(screen1.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 243 | } 244 | } 245 | } 246 | else if (logonumber == 3) 247 | { 248 | if (transparent == true) 249 | { 250 | 251 | if (latch == true) 252 | { 253 | if (strcmp(menu1.button3.latchlogo, "/logos/") != 0) 254 | { 255 | drawBmpTransparent(menu1.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 256 | } 257 | else 258 | { 259 | drawBmpTransparent(screen1.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 260 | drawlatched(logonumber, col, row); 261 | } 262 | } 263 | else 264 | { 265 | drawBmpTransparent(screen1.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 266 | } 267 | } 268 | else 269 | { 270 | if (latch == true) 271 | { 272 | 273 | if (strcmp(menu1.button3.latchlogo, "/logos/") != 0) 274 | { 275 | drawBmp(menu1.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 276 | } 277 | else 278 | { 279 | drawBmp(screen1.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 280 | drawlatched(logonumber, col, row); 281 | } 282 | } 283 | else 284 | { 285 | drawBmp(screen1.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 286 | } 287 | } 288 | } 289 | else if (logonumber == 4) 290 | { 291 | 292 | if (transparent == true) 293 | { 294 | 295 | if (latch == true) 296 | { 297 | if (strcmp(menu1.button4.latchlogo, "/logos/") != 0) 298 | { 299 | drawBmpTransparent(menu1.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 300 | } 301 | else 302 | { 303 | drawBmpTransparent(screen1.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 304 | drawlatched(logonumber, col, row); 305 | } 306 | } 307 | else 308 | { 309 | drawBmpTransparent(screen1.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 310 | } 311 | } 312 | else 313 | { 314 | if (latch == true) 315 | { 316 | 317 | if (strcmp(menu1.button4.latchlogo, "/logos/") != 0) 318 | { 319 | drawBmp(menu1.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 320 | } 321 | else 322 | { 323 | drawBmp(screen1.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 324 | drawlatched(logonumber, col, row); 325 | } 326 | } 327 | else 328 | { 329 | drawBmp(screen1.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 330 | } 331 | } 332 | } 333 | else if (logonumber == 5) 334 | { 335 | if (transparent == true) 336 | { 337 | drawBmpTransparent(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 338 | } 339 | else 340 | { 341 | drawBmp(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 342 | } 343 | } 344 | } 345 | else if (pageNum == 2) 346 | { 347 | // --------------------------------- MENU 2 -------------------------------------- 348 | if (logonumber == 0) 349 | { 350 | if (transparent == true) 351 | { 352 | 353 | if (latch == true) 354 | { 355 | if (strcmp(menu2.button0.latchlogo, "/logos/") != 0) 356 | { 357 | drawBmpTransparent(menu2.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 358 | } 359 | else 360 | { 361 | drawBmpTransparent(screen2.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 362 | drawlatched(logonumber, col, row); 363 | } 364 | } 365 | else 366 | { 367 | drawBmpTransparent(screen2.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 368 | } 369 | } 370 | else 371 | { 372 | if (latch == true) 373 | { 374 | 375 | if (strcmp(menu2.button0.latchlogo, "/logos/") != 0) 376 | { 377 | drawBmp(menu2.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 378 | } 379 | else 380 | { 381 | drawBmp(screen2.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 382 | drawlatched(logonumber, col, row); 383 | } 384 | } 385 | else 386 | { 387 | drawBmp(screen2.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 388 | } 389 | } 390 | } 391 | else if (logonumber == 1) 392 | { 393 | if (transparent == true) 394 | { 395 | if (latch == true) 396 | { 397 | if (strcmp(menu2.button1.latchlogo, "/logos/") != 0) 398 | { 399 | drawBmpTransparent(menu2.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 400 | } 401 | else 402 | { 403 | 404 | drawBmpTransparent(screen2.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 405 | drawlatched(logonumber, col, row); 406 | } 407 | } 408 | else 409 | { 410 | drawBmpTransparent(screen2.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 411 | } 412 | } 413 | else 414 | { 415 | if (latch == true) 416 | { 417 | if (strcmp(menu2.button1.latchlogo, "/logos/") != 0) 418 | { 419 | drawBmp(menu2.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 420 | } 421 | else 422 | { 423 | drawBmp(screen2.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 424 | drawlatched(logonumber, col, row); 425 | } 426 | } 427 | else 428 | { 429 | drawBmp(screen2.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 430 | } 431 | } 432 | } 433 | else if (logonumber == 2) 434 | { 435 | if (transparent == true) 436 | { 437 | 438 | if (latch == true) 439 | { 440 | if (strcmp(menu2.button2.latchlogo, "/logos/") != 0) 441 | { 442 | drawBmpTransparent(menu2.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 443 | } 444 | else 445 | { 446 | drawBmpTransparent(screen2.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 447 | drawlatched(logonumber, col, row); 448 | } 449 | } 450 | else 451 | { 452 | drawBmpTransparent(screen2.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 453 | } 454 | } 455 | else 456 | { 457 | if (latch == true) 458 | { 459 | 460 | if (strcmp(menu2.button2.latchlogo, "/logos/") != 0) 461 | { 462 | drawBmp(menu2.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 463 | } 464 | else 465 | { 466 | drawBmp(screen2.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 467 | drawlatched(logonumber, col, row); 468 | } 469 | } 470 | else 471 | { 472 | drawBmp(screen2.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 473 | } 474 | } 475 | } 476 | else if (logonumber == 3) 477 | { 478 | if (transparent == true) 479 | { 480 | 481 | if (latch == true) 482 | { 483 | if (strcmp(menu2.button3.latchlogo, "/logos/") != 0) 484 | { 485 | drawBmpTransparent(menu2.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 486 | } 487 | else 488 | { 489 | drawBmpTransparent(screen2.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 490 | drawlatched(logonumber, col, row); 491 | } 492 | } 493 | else 494 | { 495 | drawBmpTransparent(screen2.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 496 | } 497 | } 498 | else 499 | { 500 | if (latch == true) 501 | { 502 | 503 | if (strcmp(menu2.button3.latchlogo, "/logos/") != 0) 504 | { 505 | drawBmp(menu2.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 506 | } 507 | else 508 | { 509 | drawBmp(screen2.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 510 | drawlatched(logonumber, col, row); 511 | } 512 | } 513 | else 514 | { 515 | drawBmp(screen2.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 516 | } 517 | } 518 | } 519 | else if (logonumber == 4) 520 | { 521 | 522 | if (transparent == true) 523 | { 524 | 525 | if (latch == true) 526 | { 527 | if (strcmp(menu2.button4.latchlogo, "/logos/") != 0) 528 | { 529 | drawBmpTransparent(menu2.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 530 | } 531 | else 532 | { 533 | drawBmpTransparent(screen2.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 534 | drawlatched(logonumber, col, row); 535 | } 536 | } 537 | else 538 | { 539 | drawBmpTransparent(screen2.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 540 | } 541 | } 542 | else 543 | { 544 | if (latch == true) 545 | { 546 | 547 | if (strcmp(menu2.button4.latchlogo, "/logos/") != 0) 548 | { 549 | drawBmp(menu2.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 550 | } 551 | else 552 | { 553 | drawBmp(screen2.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 554 | drawlatched(logonumber, col, row); 555 | } 556 | } 557 | else 558 | { 559 | drawBmp(screen2.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 560 | } 561 | } 562 | } 563 | else if (logonumber == 5) 564 | { 565 | if (transparent == true) 566 | { 567 | drawBmpTransparent(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 568 | } 569 | else 570 | { 571 | drawBmp(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 572 | } 573 | } 574 | } 575 | else if (pageNum == 3) 576 | { 577 | // --------------------------------- MENU 3 -------------------------------------- 578 | if (logonumber == 0) 579 | { 580 | if (transparent == true) 581 | { 582 | 583 | if (latch == true) 584 | { 585 | if (strcmp(menu3.button0.latchlogo, "/logos/") != 0) 586 | { 587 | drawBmpTransparent(menu3.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 588 | } 589 | else 590 | { 591 | drawBmpTransparent(screen3.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 592 | drawlatched(logonumber, col, row); 593 | } 594 | } 595 | else 596 | { 597 | drawBmpTransparent(screen3.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 598 | } 599 | } 600 | else 601 | { 602 | if (latch == true) 603 | { 604 | 605 | if (strcmp(menu3.button0.latchlogo, "/logos/") != 0) 606 | { 607 | drawBmp(menu3.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 608 | } 609 | else 610 | { 611 | drawBmp(screen3.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 612 | drawlatched(logonumber, col, row); 613 | } 614 | } 615 | else 616 | { 617 | drawBmp(screen3.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 618 | } 619 | } 620 | } 621 | else if (logonumber == 1) 622 | { 623 | if (transparent == true) 624 | { 625 | if (latch == true) 626 | { 627 | if (strcmp(menu3.button1.latchlogo, "/logos/") != 0) 628 | { 629 | drawBmpTransparent(menu3.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 630 | } 631 | else 632 | { 633 | 634 | drawBmpTransparent(screen3.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 635 | drawlatched(logonumber, col, row); 636 | } 637 | } 638 | else 639 | { 640 | drawBmpTransparent(screen3.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 641 | } 642 | } 643 | else 644 | { 645 | if (latch == true) 646 | { 647 | if (strcmp(menu3.button1.latchlogo, "/logos/") != 0) 648 | { 649 | drawBmp(menu3.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 650 | } 651 | else 652 | { 653 | drawBmp(screen3.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 654 | drawlatched(logonumber, col, row); 655 | } 656 | } 657 | else 658 | { 659 | drawBmp(screen3.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 660 | } 661 | } 662 | } 663 | else if (logonumber == 2) 664 | { 665 | if (transparent == true) 666 | { 667 | 668 | if (latch == true) 669 | { 670 | if (strcmp(menu3.button2.latchlogo, "/logos/") != 0) 671 | { 672 | drawBmpTransparent(menu3.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 673 | } 674 | else 675 | { 676 | drawBmpTransparent(screen3.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 677 | drawlatched(logonumber, col, row); 678 | } 679 | } 680 | else 681 | { 682 | drawBmpTransparent(screen3.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 683 | } 684 | } 685 | else 686 | { 687 | if (latch == true) 688 | { 689 | 690 | if (strcmp(menu3.button2.latchlogo, "/logos/") != 0) 691 | { 692 | drawBmp(menu3.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 693 | } 694 | else 695 | { 696 | drawBmp(screen3.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 697 | drawlatched(logonumber, col, row); 698 | } 699 | } 700 | else 701 | { 702 | drawBmp(screen3.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 703 | } 704 | } 705 | } 706 | else if (logonumber == 3) 707 | { 708 | if (transparent == true) 709 | { 710 | 711 | if (latch == true) 712 | { 713 | if (strcmp(menu3.button3.latchlogo, "/logos/") != 0) 714 | { 715 | drawBmpTransparent(menu3.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 716 | } 717 | else 718 | { 719 | drawBmpTransparent(screen3.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 720 | drawlatched(logonumber, col, row); 721 | } 722 | } 723 | else 724 | { 725 | drawBmpTransparent(screen3.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 726 | } 727 | } 728 | else 729 | { 730 | if (latch == true) 731 | { 732 | 733 | if (strcmp(menu3.button3.latchlogo, "/logos/") != 0) 734 | { 735 | drawBmp(menu3.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 736 | } 737 | else 738 | { 739 | drawBmp(screen3.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 740 | drawlatched(logonumber, col, row); 741 | } 742 | } 743 | else 744 | { 745 | drawBmp(screen3.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 746 | } 747 | } 748 | } 749 | else if (logonumber == 4) 750 | { 751 | 752 | if (transparent == true) 753 | { 754 | 755 | if (latch == true) 756 | { 757 | if (strcmp(menu3.button4.latchlogo, "/logos/") != 0) 758 | { 759 | drawBmpTransparent(menu3.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 760 | } 761 | else 762 | { 763 | drawBmpTransparent(screen3.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 764 | drawlatched(logonumber, col, row); 765 | } 766 | } 767 | else 768 | { 769 | drawBmpTransparent(screen3.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 770 | } 771 | } 772 | else 773 | { 774 | if (latch == true) 775 | { 776 | 777 | if (strcmp(menu3.button4.latchlogo, "/logos/") != 0) 778 | { 779 | drawBmp(menu3.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 780 | } 781 | else 782 | { 783 | drawBmp(screen3.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 784 | drawlatched(logonumber, col, row); 785 | } 786 | } 787 | else 788 | { 789 | drawBmp(screen3.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 790 | } 791 | } 792 | } 793 | else if (logonumber == 5) 794 | { 795 | if (transparent == true) 796 | { 797 | drawBmpTransparent(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 798 | } 799 | else 800 | { 801 | drawBmp(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 802 | } 803 | } 804 | } 805 | else if (pageNum == 4) 806 | { 807 | // --------------------------------- MENU 4 -------------------------------------- 808 | if (logonumber == 0) 809 | { 810 | if (transparent == true) 811 | { 812 | 813 | if (latch == true) 814 | { 815 | if (strcmp(menu4.button0.latchlogo, "/logos/") != 0) 816 | { 817 | drawBmpTransparent(menu4.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 818 | } 819 | else 820 | { 821 | drawBmpTransparent(screen4.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 822 | drawlatched(logonumber, col, row); 823 | } 824 | } 825 | else 826 | { 827 | drawBmpTransparent(screen4.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 828 | } 829 | } 830 | else 831 | { 832 | if (latch == true) 833 | { 834 | 835 | if (strcmp(menu4.button0.latchlogo, "/logos/") != 0) 836 | { 837 | drawBmp(menu4.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 838 | } 839 | else 840 | { 841 | drawBmp(screen4.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 842 | drawlatched(logonumber, col, row); 843 | } 844 | } 845 | else 846 | { 847 | drawBmp(screen4.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 848 | } 849 | } 850 | } 851 | else if (logonumber == 1) 852 | { 853 | if (transparent == true) 854 | { 855 | if (latch == true) 856 | { 857 | if (strcmp(menu4.button1.latchlogo, "/logos/") != 0) 858 | { 859 | drawBmpTransparent(menu4.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 860 | } 861 | else 862 | { 863 | 864 | drawBmpTransparent(screen4.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 865 | drawlatched(logonumber, col, row); 866 | } 867 | } 868 | else 869 | { 870 | drawBmpTransparent(screen4.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 871 | } 872 | } 873 | else 874 | { 875 | if (latch == true) 876 | { 877 | if (strcmp(menu4.button1.latchlogo, "/logos/") != 0) 878 | { 879 | drawBmp(menu4.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 880 | } 881 | else 882 | { 883 | drawBmp(screen4.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 884 | drawlatched(logonumber, col, row); 885 | } 886 | } 887 | else 888 | { 889 | drawBmp(screen4.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 890 | } 891 | } 892 | } 893 | else if (logonumber == 2) 894 | { 895 | if (transparent == true) 896 | { 897 | 898 | if (latch == true) 899 | { 900 | if (strcmp(menu4.button2.latchlogo, "/logos/") != 0) 901 | { 902 | drawBmpTransparent(menu4.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 903 | } 904 | else 905 | { 906 | drawBmpTransparent(screen4.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 907 | drawlatched(logonumber, col, row); 908 | } 909 | } 910 | else 911 | { 912 | drawBmpTransparent(screen4.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 913 | } 914 | } 915 | else 916 | { 917 | if (latch == true) 918 | { 919 | 920 | if (strcmp(menu4.button2.latchlogo, "/logos/") != 0) 921 | { 922 | drawBmp(menu4.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 923 | } 924 | else 925 | { 926 | drawBmp(screen4.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 927 | drawlatched(logonumber, col, row); 928 | } 929 | } 930 | else 931 | { 932 | drawBmp(screen4.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 933 | } 934 | } 935 | } 936 | else if (logonumber == 3) 937 | { 938 | if (transparent == true) 939 | { 940 | 941 | if (latch == true) 942 | { 943 | if (strcmp(menu4.button3.latchlogo, "/logos/") != 0) 944 | { 945 | drawBmpTransparent(menu4.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 946 | } 947 | else 948 | { 949 | drawBmpTransparent(screen4.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 950 | drawlatched(logonumber, col, row); 951 | } 952 | } 953 | else 954 | { 955 | drawBmpTransparent(screen4.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 956 | } 957 | } 958 | else 959 | { 960 | if (latch == true) 961 | { 962 | 963 | if (strcmp(menu4.button3.latchlogo, "/logos/") != 0) 964 | { 965 | drawBmp(menu4.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 966 | } 967 | else 968 | { 969 | drawBmp(screen4.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 970 | drawlatched(logonumber, col, row); 971 | } 972 | } 973 | else 974 | { 975 | drawBmp(screen4.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 976 | } 977 | } 978 | } 979 | else if (logonumber == 4) 980 | { 981 | 982 | if (transparent == true) 983 | { 984 | 985 | if (latch == true) 986 | { 987 | if (strcmp(menu4.button4.latchlogo, "/logos/") != 0) 988 | { 989 | drawBmpTransparent(menu4.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 990 | } 991 | else 992 | { 993 | drawBmpTransparent(screen4.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 994 | drawlatched(logonumber, col, row); 995 | } 996 | } 997 | else 998 | { 999 | drawBmpTransparent(screen4.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1000 | } 1001 | } 1002 | else 1003 | { 1004 | if (latch == true) 1005 | { 1006 | 1007 | if (strcmp(menu4.button4.latchlogo, "/logos/") != 0) 1008 | { 1009 | drawBmp(menu4.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1010 | } 1011 | else 1012 | { 1013 | drawBmp(screen4.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1014 | drawlatched(logonumber, col, row); 1015 | } 1016 | } 1017 | else 1018 | { 1019 | drawBmp(screen4.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1020 | } 1021 | } 1022 | } 1023 | else if (logonumber == 5) 1024 | { 1025 | if (transparent == true) 1026 | { 1027 | drawBmpTransparent(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1028 | } 1029 | else 1030 | { 1031 | drawBmp(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1032 | } 1033 | } 1034 | } 1035 | else if (pageNum == 5) 1036 | { 1037 | // --------------------------------- MENU 5 -------------------------------------- 1038 | if (logonumber == 0) 1039 | { 1040 | if (transparent == true) 1041 | { 1042 | 1043 | if (latch == true) 1044 | { 1045 | if (strcmp(menu5.button0.latchlogo, "/logos/") != 0) 1046 | { 1047 | drawBmpTransparent(menu5.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1048 | } 1049 | else 1050 | { 1051 | drawBmpTransparent(screen5.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1052 | drawlatched(logonumber, col, row); 1053 | } 1054 | } 1055 | else 1056 | { 1057 | drawBmpTransparent(screen5.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1058 | } 1059 | } 1060 | else 1061 | { 1062 | if (latch == true) 1063 | { 1064 | 1065 | if (strcmp(menu5.button0.latchlogo, "/logos/") != 0) 1066 | { 1067 | drawBmp(menu5.button0.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1068 | } 1069 | else 1070 | { 1071 | drawBmp(screen5.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1072 | drawlatched(logonumber, col, row); 1073 | } 1074 | } 1075 | else 1076 | { 1077 | drawBmp(screen5.logo0, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1078 | } 1079 | } 1080 | } 1081 | else if (logonumber == 1) 1082 | { 1083 | if (transparent == true) 1084 | { 1085 | if (latch == true) 1086 | { 1087 | if (strcmp(menu5.button1.latchlogo, "/logos/") != 0) 1088 | { 1089 | drawBmpTransparent(menu5.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1090 | } 1091 | else 1092 | { 1093 | 1094 | drawBmpTransparent(screen5.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1095 | drawlatched(logonumber, col, row); 1096 | } 1097 | } 1098 | else 1099 | { 1100 | drawBmpTransparent(screen5.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1101 | } 1102 | } 1103 | else 1104 | { 1105 | if (latch == true) 1106 | { 1107 | if (strcmp(menu5.button1.latchlogo, "/logos/") != 0) 1108 | { 1109 | drawBmp(menu5.button1.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1110 | } 1111 | else 1112 | { 1113 | drawBmp(screen5.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1114 | drawlatched(logonumber, col, row); 1115 | } 1116 | } 1117 | else 1118 | { 1119 | drawBmp(screen5.logo1, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1120 | } 1121 | } 1122 | } 1123 | else if (logonumber == 2) 1124 | { 1125 | if (transparent == true) 1126 | { 1127 | 1128 | if (latch == true) 1129 | { 1130 | if (strcmp(menu5.button2.latchlogo, "/logos/") != 0) 1131 | { 1132 | drawBmpTransparent(menu5.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1133 | } 1134 | else 1135 | { 1136 | drawBmpTransparent(screen5.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1137 | drawlatched(logonumber, col, row); 1138 | } 1139 | } 1140 | else 1141 | { 1142 | drawBmpTransparent(screen5.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1143 | } 1144 | } 1145 | else 1146 | { 1147 | if (latch == true) 1148 | { 1149 | 1150 | if (strcmp(menu5.button2.latchlogo, "/logos/") != 0) 1151 | { 1152 | drawBmp(menu5.button2.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1153 | } 1154 | else 1155 | { 1156 | drawBmp(screen5.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1157 | drawlatched(logonumber, col, row); 1158 | } 1159 | } 1160 | else 1161 | { 1162 | drawBmp(screen5.logo2, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1163 | } 1164 | } 1165 | } 1166 | else if (logonumber == 3) 1167 | { 1168 | if (transparent == true) 1169 | { 1170 | 1171 | if (latch == true) 1172 | { 1173 | if (strcmp(menu5.button3.latchlogo, "/logos/") != 0) 1174 | { 1175 | drawBmpTransparent(menu5.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1176 | } 1177 | else 1178 | { 1179 | drawBmpTransparent(screen5.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1180 | drawlatched(logonumber, col, row); 1181 | } 1182 | } 1183 | else 1184 | { 1185 | drawBmpTransparent(screen5.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1186 | } 1187 | } 1188 | else 1189 | { 1190 | if (latch == true) 1191 | { 1192 | 1193 | if (strcmp(menu5.button3.latchlogo, "/logos/") != 0) 1194 | { 1195 | drawBmp(menu5.button3.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1196 | } 1197 | else 1198 | { 1199 | drawBmp(screen5.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1200 | drawlatched(logonumber, col, row); 1201 | } 1202 | } 1203 | else 1204 | { 1205 | drawBmp(screen5.logo3, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1206 | } 1207 | } 1208 | } 1209 | else if (logonumber == 4) 1210 | { 1211 | 1212 | if (transparent == true) 1213 | { 1214 | 1215 | if (latch == true) 1216 | { 1217 | if (strcmp(menu5.button4.latchlogo, "/logos/") != 0) 1218 | { 1219 | drawBmpTransparent(menu5.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1220 | } 1221 | else 1222 | { 1223 | drawBmpTransparent(screen5.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1224 | drawlatched(logonumber, col, row); 1225 | } 1226 | } 1227 | else 1228 | { 1229 | drawBmpTransparent(screen5.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1230 | } 1231 | } 1232 | else 1233 | { 1234 | if (latch == true) 1235 | { 1236 | 1237 | if (strcmp(menu5.button4.latchlogo, "/logos/") != 0) 1238 | { 1239 | drawBmp(menu5.button4.latchlogo, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1240 | } 1241 | else 1242 | { 1243 | drawBmp(screen5.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1244 | drawlatched(logonumber, col, row); 1245 | } 1246 | } 1247 | else 1248 | { 1249 | drawBmp(screen5.logo4, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1250 | } 1251 | } 1252 | } 1253 | else if (logonumber == 5) 1254 | { 1255 | if (transparent == true) 1256 | { 1257 | drawBmpTransparent(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1258 | } 1259 | else 1260 | { 1261 | drawBmp(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1262 | } 1263 | } 1264 | } 1265 | else if (pageNum == 6) 1266 | { 1267 | // pageNum6 contains settings logos 1268 | if (logonumber == 0) 1269 | { 1270 | drawBmpTransparent(generallogo.configurator, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1271 | } 1272 | else if (logonumber == 1) 1273 | { 1274 | drawBmpTransparent("/logos/brightnessdown.bmp", KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1275 | } 1276 | else if (logonumber == 2) 1277 | { 1278 | drawBmpTransparent("/logos/brightnessup.bmp", KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1279 | } 1280 | else if (logonumber == 3) 1281 | { 1282 | drawBmpTransparent("/logos/sleep.bmp", KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1283 | if (latch) 1284 | { 1285 | drawlatched(logonumber, col, row); 1286 | } 1287 | } 1288 | else if (logonumber == 4) 1289 | { 1290 | drawBmpTransparent("/logos/info.bmp", KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1291 | } 1292 | else if (logonumber == 5) 1293 | { 1294 | if (transparent == true) 1295 | { 1296 | drawBmpTransparent(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1297 | } 1298 | else 1299 | { 1300 | drawBmp(generallogo.homebutton, KEY_X - 36 + col * (KEY_W + KEY_SPACING_X), KEY_Y - 36 + row * (KEY_H + KEY_SPACING_Y)); 1301 | } 1302 | } 1303 | } 1304 | } 1305 | 1306 | /** 1307 | * @brief This function draws the 6 buttons that are on every page. 1308 | Pagenumber is global and doesn't need to be passed. 1309 | * 1310 | * @param none 1311 | * 1312 | * @return none 1313 | * 1314 | * @note Three possibilities: pagenumber = 0 means homescreen, 1315 | pagenumber = 7 means config mode, anything else is a menu. 1316 | */ 1317 | void drawKeypad() 1318 | { 1319 | // Draw the home screen button outlines and fill them with colours 1320 | if (pageNum == 0) 1321 | { 1322 | for (uint8_t row = 0; row < 2; row++) 1323 | { 1324 | for (uint8_t col = 0; col < 3; col++) 1325 | { 1326 | 1327 | uint8_t b = col + row * 3; 1328 | uint16_t buttonBG; 1329 | bool drawTransparent; 1330 | uint16_t imageBGColor = getImageBG(b); 1331 | if (imageBGColor > 0) 1332 | { 1333 | buttonBG = imageBGColor; 1334 | drawTransparent = false; 1335 | } 1336 | else 1337 | { 1338 | buttonBG = generalconfig.menuButtonColour; 1339 | drawTransparent = true; 1340 | } 1341 | tft.setFreeFont(LABEL_FONT); 1342 | key[b].initButton(&tft, KEY_X + col * (KEY_W + KEY_SPACING_X), 1343 | KEY_Y + row * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text 1344 | KEY_W, KEY_H, TFT_WHITE, buttonBG, TFT_WHITE, 1345 | "", KEY_TEXTSIZE); 1346 | key[b].drawButton(); 1347 | drawlogo(b, col, row, drawTransparent, false); // After drawing the button outline we call this to draw a logo. 1348 | } 1349 | } 1350 | } 1351 | 1352 | else if (pageNum == 10) 1353 | { 1354 | // Pagenum 10 means that a JSON config failed to load completely. 1355 | tft.fillScreen(TFT_BLACK); 1356 | tft.setCursor(0, 0); 1357 | tft.setTextFont(2); 1358 | tft.setTextSize(1); 1359 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 1360 | 1361 | tft.printf(" %s failed to load and might be corrupted.\n", jsonfilefail); 1362 | tft.println(" You can reset that specific file to default by opening the serial monitor"); 1363 | tft.printf(" and typing \"reset %s\"\n", jsonfilefail); 1364 | tft.println(" If you don't do this, the configurator will fail to load."); 1365 | } 1366 | else 1367 | { 1368 | // Draw the button outlines and fill them with colours 1369 | for (uint8_t row = 0; row < 2; row++) 1370 | { 1371 | for (uint8_t col = 0; col < 3; col++) 1372 | { 1373 | 1374 | uint8_t b = col + row * 3; 1375 | 1376 | if (row == 1 && col == 2) 1377 | { 1378 | 1379 | // Check if "home.bmp" is a transparent one 1380 | 1381 | uint16_t buttonBG; 1382 | bool drawTransparent; 1383 | uint16_t imageBGColor; 1384 | 1385 | imageBGColor = getImageBG(b); 1386 | 1387 | if (imageBGColor > 0) 1388 | { 1389 | buttonBG = imageBGColor; 1390 | drawTransparent = false; 1391 | } 1392 | else 1393 | { 1394 | buttonBG = generalconfig.menuButtonColour; 1395 | drawTransparent = true; 1396 | } 1397 | 1398 | tft.setFreeFont(LABEL_FONT); 1399 | key[b].initButton(&tft, KEY_X + col * (KEY_W + KEY_SPACING_X), 1400 | KEY_Y + row * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text 1401 | KEY_W, KEY_H, TFT_WHITE, buttonBG, TFT_WHITE, 1402 | "", KEY_TEXTSIZE); 1403 | key[b].drawButton(); 1404 | drawlogo(b, col, row, drawTransparent, false); 1405 | } 1406 | else 1407 | { 1408 | // Otherwise use functionButtonColour 1409 | 1410 | int index; 1411 | 1412 | if (pageNum == 2) 1413 | { 1414 | index = b + 5; 1415 | } 1416 | else if (pageNum == 3) 1417 | { 1418 | index = b + 10; 1419 | } 1420 | else if (pageNum == 4) 1421 | { 1422 | index = b + 15; 1423 | } 1424 | else if (pageNum == 5) 1425 | { 1426 | index = b + 20; 1427 | } 1428 | else if (pageNum == 6) 1429 | { 1430 | index = b + 25; 1431 | } 1432 | else 1433 | { 1434 | index = b; 1435 | } 1436 | 1437 | uint16_t buttonBG; 1438 | bool drawTransparent; 1439 | uint16_t imageBGColor; 1440 | if (islatched[index] && b < 5) 1441 | { 1442 | imageBGColor = getLatchImageBG(b); 1443 | } 1444 | else 1445 | { 1446 | imageBGColor = getImageBG(b); 1447 | } 1448 | 1449 | if (imageBGColor > 0) 1450 | { 1451 | buttonBG = imageBGColor; 1452 | drawTransparent = false; 1453 | } 1454 | else 1455 | { 1456 | buttonBG = generalconfig.functionButtonColour; 1457 | drawTransparent = true; 1458 | } 1459 | tft.setFreeFont(LABEL_FONT); 1460 | key[b].initButton(&tft, KEY_X + col * (KEY_W + KEY_SPACING_X), 1461 | KEY_Y + row * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text 1462 | KEY_W, KEY_H, TFT_WHITE, buttonBG, TFT_WHITE, 1463 | "", KEY_TEXTSIZE); 1464 | key[b].drawButton(); 1465 | // After drawing the button outline we call this to draw a logo. 1466 | if (islatched[index] && b < 5) 1467 | { 1468 | drawlogo(b, col, row, drawTransparent, true); 1469 | } 1470 | else 1471 | { 1472 | drawlogo(b, col, row, drawTransparent, false); 1473 | } 1474 | } 1475 | } 1476 | } 1477 | } 1478 | } 1479 | 1480 | /* ------------- Print an error message the TFT screen ---------------- 1481 | Purpose: This function prints an message to the TFT screen on a black 1482 | background. 1483 | Input : String message 1484 | Output : none 1485 | Note : none 1486 | */ 1487 | 1488 | void drawErrorMessage(String message) 1489 | { 1490 | 1491 | tft.fillScreen(TFT_BLACK); 1492 | tft.setCursor(20, 20); 1493 | tft.setTextFont(2); 1494 | tft.setTextSize(1); 1495 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 1496 | tft.println(message); 1497 | } 1498 | 1499 | /** 1500 | * @brief This function gets the Bluetooth device address and prints it to the serial monitor 1501 | and the TFT screen in a 6 byte format, seperating each byte by ":". 1502 | * 1503 | * @param none 1504 | * 1505 | * @return none 1506 | * 1507 | * @note e.g. 00:11:22:33:22:EE 1508 | */ 1509 | void printDeviceAddress() 1510 | { 1511 | 1512 | const uint8_t *point = esp_bt_dev_get_address(); 1513 | 1514 | for (int i = 0; i < 6; i++) 1515 | { 1516 | 1517 | char str[3]; 1518 | 1519 | sprintf(str, "%02X", (int)point[i]); 1520 | //Serial.print(str); 1521 | tft.print(str); 1522 | 1523 | if (i < 5) 1524 | { 1525 | // Serial.print(":"); 1526 | tft.print(":"); 1527 | } 1528 | } 1529 | } 1530 | 1531 | /** 1532 | * @brief This function prints some information about the current version 1533 | and setup of FreetouchDeck to the TFT screen. 1534 | * 1535 | * @param none 1536 | * 1537 | * @return none 1538 | * 1539 | * @note none 1540 | */ 1541 | void printinfo() 1542 | { 1543 | tft.fillScreen(TFT_BLACK); 1544 | tft.setCursor(1, 3); 1545 | tft.setTextFont(2); 1546 | if(SCREEN_WIDTH < 480) 1547 | { 1548 | tft.setTextSize(1); 1549 | } 1550 | else 1551 | { 1552 | tft.setTextSize(2); 1553 | } 1554 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 1555 | tft.printf("Version: %s\n", versionnumber); 1556 | 1557 | #ifdef touchInterruptPin 1558 | if (generalconfig.sleepenable) 1559 | { 1560 | tft.println("Sleep: Enabled"); 1561 | tft.printf("Sleep timer: %u minutes\n", generalconfig.sleeptimer); 1562 | } 1563 | else 1564 | { 1565 | tft.println("Sleep: Disabled"); 1566 | } 1567 | #else 1568 | tft.println("Sleep: Disabled"); 1569 | #endif 1570 | 1571 | #ifdef speakerPin 1572 | if(generalconfig.beep){ 1573 | tft.println("Speaker: Enabled"); 1574 | } 1575 | else 1576 | { 1577 | tft.println("Speaker: Disabled"); 1578 | } 1579 | #else 1580 | tft.println("Speaker: Disabled"); 1581 | #endif 1582 | 1583 | tft.print("Free Storage: "); 1584 | float freemem = FILESYSTEM.totalBytes() - FILESYSTEM.usedBytes(); 1585 | tft.print(freemem / 1000); 1586 | tft.println(" kB"); 1587 | #if defined(USEUSBHID) 1588 | tft.println("Using USB Keyboard"); 1589 | #else 1590 | tft.print("BLE Keyboard version: "); 1591 | tft.println(BLE_KEYBOARD_VERSION); 1592 | #endif //if defined(USEUSBHID) 1593 | 1594 | tft.print("ArduinoJson version: "); 1595 | tft.println(ARDUINOJSON_VERSION); 1596 | tft.print("TFT_eSPI version: "); 1597 | tft.println(TFT_ESPI_VERSION); 1598 | tft.println("ESP-IDF: "); 1599 | tft.println(esp_get_idf_version()); 1600 | 1601 | displayinginfo = true; 1602 | } 1603 | 1604 | /** 1605 | * @brief This function allow for drawing a single button which is not part of the 1606 | button class. It should be manually checked for touched. 1607 | * 1608 | * @param int32_t x 1609 | int32_t y 1610 | int32_t w 1611 | int32_t h 1612 | int32_t r 1613 | uint32_t color 1614 | uint32_t outline 1615 | char *label 1616 | * 1617 | * @return none 1618 | * 1619 | * @note Use the X and Y coordinates and check in the loop if it was pressed. 1620 | */ 1621 | 1622 | void drawSingleButton(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color, uint32_t outline, String label) 1623 | { 1624 | 1625 | //Draw the button 1626 | uint8_t r = min(w, h) / 4; // Corner radius 1627 | tft.fillRoundRect(x, y, w, h, r, color); 1628 | tft.drawRoundRect(x, y, w, h, r, outline); 1629 | 1630 | //Print the label 1631 | tft.setTextColor(TFT_WHITE,color); 1632 | tft.setTextSize(2); 1633 | uint8_t tempdatum = tft.getTextDatum(); 1634 | tft.setTextDatum(MC_DATUM); 1635 | uint16_t tempPadding = tft.getTextPadding(); 1636 | tft.setTextPadding(0); 1637 | 1638 | tft.drawString(label, x + (w/2), y + (h/2)); 1639 | tft.setTextDatum(tempdatum); 1640 | tft.setTextPadding(tempPadding); 1641 | 1642 | } -------------------------------------------------------------------------------- /Keydefines.h: -------------------------------------------------------------------------------- 1 | /* 2 | Some extra KEY definitions the original USB Keyboard library does not have 3 | to make it compatible with FreeTouchDeck. Only included when #define USEUSBHID 4 | is defined. 5 | */ 6 | const uint8_t KEY_PRTSC = 0xCE; 7 | 8 | const uint8_t KEY_NUM_0 = 0xEA; 9 | const uint8_t KEY_NUM_1 = 0xE1; 10 | const uint8_t KEY_NUM_2 = 0xE2; 11 | const uint8_t KEY_NUM_3 = 0xE3; 12 | const uint8_t KEY_NUM_4 = 0xE4; 13 | const uint8_t KEY_NUM_5 = 0xE5; 14 | const uint8_t KEY_NUM_6 = 0xE6; 15 | const uint8_t KEY_NUM_7 = 0xE7; 16 | const uint8_t KEY_NUM_8 = 0xE8; 17 | const uint8_t KEY_NUM_9 = 0xE9; 18 | const uint8_t KEY_NUM_SLASH = 0xDC; 19 | const uint8_t KEY_NUM_ASTERISK = 0xDD; 20 | const uint8_t KEY_NUM_MINUS = 0xDE; 21 | const uint8_t KEY_NUM_PLUS = 0xDF; 22 | const uint8_t KEY_NUM_ENTER = 0xE0; 23 | const uint8_t KEY_NUM_PERIOD = 0xEB; 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dustin Watts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeTouchDeck 2 | [![](https://badgen.net/github/last-commit/DustinWatts/FreeTouchDeck)](https://github.com/DustinWatts/FreeTouchDeck/commits/master) 3 | [![](https://badgen.net/badge/0.9.17%20compile/succes/green)](https://badgen.net/badge/0.9.17%20compile/succes/green) 4 | [![](https://badgen.net/github/release/DustinWatts/FreeTouchDeck)](https://github.com/DustinWatts/FreeTouchDeck/releases) 5 | [![](https://img.shields.io/discord/693862273864827012?color=5165f6&label=chat%20on%20Discord)](https://discord.gg/RE3XevS) 6 | [![](https://badgen.net/github/license/DustinWatts/FreeTouchDeck)](https://github.com/DustinWatts/FreeTouchDeck/blob/master/LICENSE) 7 | [![](https://badgen.net/badge/watch/on%20youtube/ff0000)](https://www.youtube.com/watch?v=s2X4BQ9VmEU) 8 | [![](https://img.shields.io/twitter/follow/DustinWattsNL)](https://twitter.com/DustinWattsNL) 9 | 10 | For interfacing with Windows/macOS/Linux using an ESP32, a touchscreen, and BLE. 11 | 12 | ## Install Using The Web Installer (recommended!) 13 | 14 | Easy installation without the need for the Arduino IDE, and downloading and editing libraries is now available using ESP Web Tools. Visit this url to install FreeTouchDeck to your board via your browser: (https://dustinwatts.github.io/install-freetouchdeck/) Chrome, Edge, and Opera only at the moment. 15 | 16 | ## Install yourself 17 | 18 | If you want to do it yourself you can use the Instrucables https://www.instructables.com/A-Bluetooth-ESP32-TFT-Touch-Macro-Keypad/ for a detailed install guide. 19 | 20 | _**The ESP Data Upload Tool refered in the wiki or the Instrucables only works under Arduino IDE 1.x.x. But the great thing is you can have both IDE's installed next to each other.**_ 21 | 22 | ## !IMPORTANT! 23 | 24 | There are some breaking changes going from the ESP32 Arduino core version 2.x.x to version 3.x.x which can cause the code to not compile. I'm trying to fix all the ones I can find. But if you run in to one, please open an issue so I can fix it. Also, if you want to upload the code without any issues, one option is to downgrade to the v2.0.14 (which is the latest v2 arduino core) and everything should be fine. The best (and easiest) way is still using the **webinstaller** (see below), which is the recommend install method. 25 | 26 | 27 | 28 | *** 29 | ***Due to constant improvements in the `-master` branche, getting the latest code straight from the `-master` branche (by dowloading it as a ZIP for example) might be unstable. If you want a stable and tested version, download the latest release from the [Releases](https://github.com/DustinWatts/FreeTouchDeck/releases) page.*** 30 | 31 | ***The current latest stable is 0.9.17.*** 32 | *** 33 | 34 | ## We are looking for (web) developers! 35 | 36 | We are looking for (web) developers who are willing to help out updating the configurator (or changing it completely!) to work with all the new features that the Development branch offers. Once we have a working configurator, the Development branch will replace the current master as FreeTouchDeck version 2.0. As a token of my appreciation, you can get (if you will) an ESP32 TouchDown S3 at cost price before it releases! Also you will be credited here, in the source code and in the documentation. If you want to join, let us know on Discord: https://discord.gg/RE3XevS 37 | 38 | ## ESP32 TouchDown users 39 | 40 | Make sure to uncomment the line `//#define USECAPTOUCH`! 41 | And if you wish to use the speaker uncomment the line `//#define speakerPin 26` 42 | 43 | If FreeTouchDeck came pre-installed, you can find how to set up the configurator here: 44 | https://github.com/DustinWatts/esp32-touchdown/wiki/With-FreeTouchDeck-pre-installed 45 | 46 | ## Helper app 47 | 48 | I wrote a helper app for Windows/macOS/Linux that will help you start applications, run scripts and can auto-switch FreeTouchDeck to a page you choose when an application comes in to focus. You can find it here: https://github.com/DustinWatts/FreeTouchDeck-Helper 49 | 50 | ## Delete the old clone and use the new 51 | 52 | ### Mixing files of different versions may cause some unexpected behavior! 53 | 54 | The FreeTouchDeck.ino and other files (for example in the data folder) rely on each other, they come as one. So when you download the new version, make sure that you only use the files that come with the current download, and not files from other versions. Best practise is to completely delete the old version and then download/clone the new version to make sure you do not accidently mix files from different versions. **After downloading/cloning the latest version, make sure to also upload the "data" folder again using 'ESP32 Sketch Data Upload".** 55 | 56 | ## Supported Hardware 57 | 58 | For Resistive touch: 59 | - an ESP32 DEVKIT V1 (WROOM32) (Partition scheme: NO OTA with 2MB app and 2MB SPIFFS) I used: https://s.click.aliexpress.com/e/_AmfI1H They call it "ESP32-WROOM-32D" 60 | - an 3.5" (480x320) TFT + Touchscreen with ILI9488 driver and XPT2046 resitive touch controller I used: https://s.click.aliexpress.com/e/_AMAa6B 61 | 62 | _the links are affiliate links_ 63 | 64 | For Capacitive touch: 65 | - an ESP32 TouchDown: https://www.esp32touchdown.com/ 66 | 67 | If you try to run FTD on something other than the boards above, it might not work. Support for custom combinations of boards and screens is out of the scope of support. I will try and help you if you run in to something, but issues here on Github are not dealt with and will be closed with this reason. 68 | 69 | ## Community support 70 | 71 | Lot's of people have made FreeTouchDeck work on other hardware then the above hardware. Although not offically suported. I will list those forks here. 72 | 73 | - ESP32-3248S035 Capacitive / GT911 touchscreen: https://github.com/Raycast7373/FreeTouchDeck 74 | - ESP32-2432S028 Resistive touchscreen users: https://github.com/Raycast7373/FreeTouchDeck 75 | 76 | I will also point you to my Discord (https://discord.gg/RE3XevS) Where these people are active. If you have a custom build/fork for a board you would like listed here, contact me and I will add it to the list. 77 | 78 | ## Library Dependencies 79 | - Adafruit-GFX-Library (tested with version 1.10.4), available through Library Manager 80 | - TFT_eSPI (tested with version 2.3.70), available through Library Manager 81 | - ESP32-BLE-Keyboard (latest version) download from: https://github.com/T-vK/ESP32-BLE-Keyboard 82 | - ESPAsyncWebserver (latest version) download from: https://github.com/me-no-dev/ESPAsyncWebServer 83 | - AsyncTCP (latest version) download from: https://github.com/me-no-dev/AsyncTCP 84 | - ArduinoJson (tested with version 6.17.3), available through Library Manager 85 | 86 | If you use capacitive touch: 87 | - Dustin Watts FT6236 Library (version 1.0.2), https://github.com/DustinWatts/FT6236 88 | 89 | ## Combiner PCB for an ESP32 DevKit C (38-pin only) + ILI9488 Touch Module: 90 | 91 | https://github.com/DustinWatts/ESP32_TFT_Combiner 92 | 93 | ## TFT_eSPI configuration 94 | 95 | Before compiling and uploading the FreeTouchDeck.ino sketch, you will have to edit the **user_setup.h** file included with the TFT_eSPI library. This can be found in your Arduino skechtbook folder under "libraries". If you have not renamed the TFT_eSPI library folder, the file **user_setup.h** can be found in **TFT_eSPI-master**. 96 | 97 | You can find examples of **User_Setup.h** files in the folder called [user_setup.h Examples](https://github.com/DustinWatts/FreeTouchDeck/tree/master/user_setup.h%20Examples). Delete everything that is in the **User_Setup.h** file (so you have a blank file) and copy & paste the _contents_ of the file you need for your setup. Don't forget to save! 98 | 99 | If you do it manually you will have to uncomment the lines that apply to you hardware configuration. For example: if you have an TFT with an ILI9488 driver, you will have to uncomment that line under `Section 1`. Make sure all the other drivers are commented out! 100 | 101 | The next section is `Section 2`. This also depends on what hardware you are using. For example for an ESP32 you'll have to uncomment the correct #define(s) under `EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP`. Also if your TFT has the blacklight control pin available you will have to uncomment the line found under `#define TFT_BL` and make sure the pinnumber is the correct one (for The DevKit version and the ESP32TouchDown this is 32). 102 | 103 | "Section 3" can be left alone. 104 | 105 | ## Cases 106 | 107 | In the [case/ESP32_TFT_Combiner_Case](https://github.com/DustinWatts/FreeTouchDeck/tree/master/case/ESP32_TFT_Combiner_Case) you can find a case with different tops (front) and bottoms (back). You can also find user made cases on [Thingiverse](https://www.thingiverse.com/search?q=FreeTouchDeck) by following the link or searching for `FreeTouchDeck` on Thingiverse, Printables or good old Google. 108 | 109 | ## Support Me 110 | 111 | If you like what I am doing, there are a number of ways you can support me. 112 | 113 | | Platform | Link| 114 | |:-----:|:-----| 115 | | [Twtter](https://twitter.com/dustinwattsnl "Follow me on Twitter") | You can follow me on Twitter: [@dustinwattsnl](https://twitter.com/dustinwattsnl "Follow me on Twitter")| 116 | | [YouTube](https://www.youtube.com/dustinwatts "Subscrive to my YouTube channel") | You can subscribe to my channel on Youtube: [/dustinWatts](https://www.youtube.com/dustinwatts "Subscribe to my YouTube channel") | 117 | | [Patreon](https://www.patreon.com/dustinwatts) | You can support me by becoming a patron on Patreon: https://www.patreon.com/dustinwatts | 118 | | [PayPal.me](https://www.paypal.me/dustinwattsnl) | You can make a one time donation using PayPal.me: https://www.paypal.me/dustinwattsnl | 119 | 120 | ## Get help 121 | 122 | For quick access to help you can join my Discord server where I have a dedicated #freetouchdeck channel. https://discord.gg/RE3XevS 123 | -------------------------------------------------------------------------------- /ScreenHelper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief This function reads chuncks of 2 bytes of data from a 3 | file and returns the data. 4 | * 5 | * @param &f 6 | * 7 | * @return uint16_t 8 | * 9 | * @note litte-endian 10 | */ 11 | uint16_t read16(fs::File &f) 12 | { 13 | uint16_t result; 14 | ((uint8_t *)&result)[0] = f.read(); // LSB 15 | ((uint8_t *)&result)[1] = f.read(); // MSB 16 | return result; 17 | } 18 | 19 | /** 20 | * @brief This function reads chuncks of 4 bytes of data from a 21 | file and returns the data. 22 | * 23 | * @param &f 24 | * 25 | * @return uint32_t 26 | * 27 | * @note litte-endian 28 | */ 29 | uint32_t read32(fs::File &f) 30 | { 31 | uint32_t result; 32 | ((uint8_t *)&result)[0] = f.read(); // LSB 33 | ((uint8_t *)&result)[1] = f.read(); 34 | ((uint8_t *)&result)[2] = f.read(); 35 | ((uint8_t *)&result)[3] = f.read(); // MSB 36 | return result; 37 | } 38 | 39 | /** 40 | * @brief This functions accepts a HTML including the # colour code 41 | (eg. #FF00FF) and returns it in RGB888 format. 42 | * 43 | * @param *html char (including #) 44 | * 45 | * @return unsigned long 46 | * 47 | * @note none 48 | */ 49 | unsigned long convertHTMLtoRGB888(char *html) 50 | { 51 | char *hex = html + 1; // remove the # 52 | unsigned long rgb = strtoul(hex, NULL, 16); 53 | return rgb; 54 | } 55 | 56 | /** 57 | * @brief This function converts RGB888 to RGB565. 58 | * 59 | * @param rgb unsigned long 60 | * 61 | * @return unsigned int 62 | * 63 | * @note none 64 | */ 65 | unsigned int convertRGB888ToRGB565(unsigned long rgb) 66 | { 67 | return (((rgb & 0xf80000) >> 8) | ((rgb & 0xfc00) >> 5) | ((rgb & 0xf8) >> 3)); 68 | } 69 | 70 | /** 71 | * @brief This function draws a transparent BMP on the TFT screen according 72 | to the given x and y coordinates. 73 | * 74 | * @param *filename 75 | * @param x int16_t 76 | * @param y int16_t 77 | * 78 | * @return none 79 | * 80 | * @note A completely black pixel is transparent e.g. (0x0000) not drawn. 81 | */ 82 | void drawBmpTransparent(const char *filename, int16_t x, int16_t y) 83 | { 84 | 85 | if ((x >= tft.width()) || (y >= tft.height())) 86 | return; 87 | 88 | fs::File bmpFS; 89 | 90 | bmpFS = FILESYSTEM.open(filename, "r"); 91 | 92 | if (!bmpFS) 93 | { 94 | Serial.println("[WARNING]: Bitmap not found: "); 95 | Serial.println(filename); 96 | filename = "/logos/question.bmp"; 97 | bmpFS = FILESYSTEM.open(filename, "r"); 98 | } 99 | 100 | uint32_t seekOffset; 101 | uint16_t w, h, row; 102 | uint8_t r, g, b; 103 | 104 | if (read16(bmpFS) == 0x4D42) 105 | { 106 | read32(bmpFS); 107 | read32(bmpFS); 108 | seekOffset = read32(bmpFS); 109 | read32(bmpFS); 110 | w = read32(bmpFS); 111 | h = read32(bmpFS); 112 | 113 | if ((read16(bmpFS) == 1) && (read16(bmpFS) == 24) && (read32(bmpFS) == 0)) 114 | { 115 | y += h - 1; 116 | 117 | bool oldSwapBytes = tft.getSwapBytes(); 118 | tft.setSwapBytes(true); 119 | bmpFS.seek(seekOffset); 120 | 121 | uint16_t padding = (4 - ((w * 3) & 3)) & 3; 122 | uint8_t lineBuffer[w * 3 + padding]; 123 | 124 | for (row = 0; row < h; row++) 125 | { 126 | 127 | bmpFS.read(lineBuffer, sizeof(lineBuffer)); 128 | uint8_t *bptr = lineBuffer; 129 | uint16_t *tptr = (uint16_t *)lineBuffer; 130 | // Convert 24 to 16 bit colours 131 | for (uint16_t col = 0; col < w; col++) 132 | { 133 | b = *bptr++; 134 | g = *bptr++; 135 | r = *bptr++; 136 | *tptr++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); 137 | } 138 | 139 | // Push the pixel row to screen, pushImage will crop the line if needed 140 | // y is decremented as the BMP image is drawn bottom up 141 | tft.pushImage(x, y--, w, 1, (uint16_t *)lineBuffer, TFT_BLACK); 142 | } 143 | tft.setSwapBytes(oldSwapBytes); 144 | } 145 | else 146 | Serial.println("BMP format not recognized."); 147 | } 148 | bmpFS.close(); 149 | } 150 | 151 | /** 152 | * @brief This function draws a BMP on the TFT screen according 153 | to the given x and y coordinates. 154 | * 155 | * @param *filename 156 | * @param x int16_t 157 | * @param y int16_t 158 | * 159 | * @return none 160 | * 161 | * @note In contradiction to drawBmpTransparent() this does draw black pixels. 162 | */ 163 | void drawBmp(const char *filename, int16_t x, int16_t y) 164 | { 165 | 166 | if ((x >= tft.width()) || (y >= tft.height())) 167 | return; 168 | 169 | fs::File bmpFS; 170 | 171 | bmpFS = FILESYSTEM.open(filename, "r"); 172 | 173 | if (!bmpFS) 174 | { 175 | 176 | Serial.print("File not found:"); 177 | Serial.println(filename); 178 | return; 179 | } 180 | 181 | uint32_t seekOffset; 182 | uint16_t w, h, row; 183 | uint8_t r, g, b; 184 | 185 | if (read16(bmpFS) == 0x4D42) 186 | { 187 | read32(bmpFS); 188 | read32(bmpFS); 189 | seekOffset = read32(bmpFS); 190 | read32(bmpFS); 191 | w = read32(bmpFS); 192 | h = read32(bmpFS); 193 | 194 | if ((read16(bmpFS) == 1) && (read16(bmpFS) == 24) && (read32(bmpFS) == 0)) 195 | { 196 | y += h - 1; 197 | 198 | bool oldSwapBytes = tft.getSwapBytes(); 199 | tft.setSwapBytes(true); 200 | bmpFS.seek(seekOffset); 201 | 202 | uint16_t padding = (4 - ((w * 3) & 3)) & 3; 203 | uint8_t lineBuffer[w * 3 + padding]; 204 | 205 | for (row = 0; row < h; row++) 206 | { 207 | 208 | bmpFS.read(lineBuffer, sizeof(lineBuffer)); 209 | uint8_t *bptr = lineBuffer; 210 | uint16_t *tptr = (uint16_t *)lineBuffer; 211 | // Convert 24 to 16 bit colours 212 | for (uint16_t col = 0; col < w; col++) 213 | { 214 | b = *bptr++; 215 | g = *bptr++; 216 | r = *bptr++; 217 | *tptr++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); 218 | } 219 | 220 | // Push the pixel row to screen, pushImage will crop the line if needed 221 | // y is decremented as the BMP image is drawn bottom up 222 | tft.pushImage(x, y--, w, 1, (uint16_t *)lineBuffer); 223 | } 224 | tft.setSwapBytes(oldSwapBytes); 225 | } 226 | else 227 | Serial.println("[WARNING]: BMP format not recognized."); 228 | } 229 | bmpFS.close(); 230 | } 231 | 232 | /** 233 | * @brief This function reads a number of bytes from the given 234 | file at the given position. 235 | * 236 | * @param *p_file File 237 | * @param position int 238 | * @param nBytes byte 239 | * 240 | * @return int32_t 241 | * 242 | * @note none 243 | */ 244 | int32_t readNbytesInt(File *p_file, int position, byte nBytes) 245 | { 246 | if (nBytes > 4) 247 | return 0; 248 | 249 | p_file->seek(position); 250 | 251 | int32_t weight = 1; 252 | int32_t result = 0; 253 | for (; nBytes; nBytes--) 254 | { 255 | result += weight * p_file->read(); 256 | weight <<= 8; 257 | } 258 | return result; 259 | } 260 | 261 | /** 262 | * @brief This function reads the RGB565 colour of the first pixel for a 263 | given the logo number. The pagenumber is global. 264 | * 265 | * @param *filename const char 266 | * 267 | * @return uint16_t 268 | * 269 | * @note Uses readNbytesInt 270 | */ 271 | uint16_t getBMPColor(const char *filename) 272 | { 273 | 274 | // Open File 275 | File bmpImage; 276 | bmpImage = FILESYSTEM.open(filename, FILE_READ); 277 | 278 | int32_t dataStartingOffset = readNbytesInt(&bmpImage, 0x0A, 4); 279 | int16_t pixelsize = readNbytesInt(&bmpImage, 0x1C, 2); 280 | 281 | if (pixelsize != 24) 282 | { 283 | Serial.println("[WARNING]: getBMPColor: Image is not 24 bpp"); 284 | return 0x0000; 285 | } 286 | 287 | bmpImage.seek(dataStartingOffset); //skip bitmap header 288 | 289 | byte R, G, B; 290 | 291 | B = bmpImage.read(); 292 | G = bmpImage.read(); 293 | R = bmpImage.read(); 294 | 295 | bmpImage.close(); 296 | 297 | return tft.color565(R, G, B); 298 | } 299 | 300 | /** 301 | * @brief This function returns the RGB565 colour of the first pixel for a 302 | given the logo number. The pagenumber is global. 303 | * 304 | * @param logonumber int 305 | * 306 | * @return uint16_t 307 | * 308 | * @note Uses getBMPColor to read the actual image data. 309 | */ 310 | uint16_t getImageBG(int logonumber) 311 | { 312 | 313 | // Logo 5 on each screen is the back home button except on the home screen 314 | if (logonumber == 5 && pageNum > 0) 315 | { 316 | return getBMPColor("/logos/home.bmp"); 317 | } 318 | else 319 | { 320 | 321 | if (pageNum == 0) 322 | { 323 | if (logonumber == 0) 324 | { 325 | return getBMPColor(screen0.logo0); 326 | } 327 | else if (logonumber == 1) 328 | { 329 | return getBMPColor(screen0.logo1); 330 | } 331 | else if (logonumber == 2) 332 | { 333 | return getBMPColor(screen0.logo2); 334 | } 335 | else if (logonumber == 3) 336 | { 337 | return getBMPColor(screen0.logo3); 338 | } 339 | else if (logonumber == 4) 340 | { 341 | return getBMPColor(screen0.logo4); 342 | } 343 | else if (logonumber == 5) 344 | { 345 | return getBMPColor(screen0.logo5); 346 | } 347 | else 348 | { 349 | return 0x0000; 350 | } 351 | } 352 | else if (pageNum == 1) 353 | { 354 | if (logonumber == 0) 355 | { 356 | return getBMPColor(screen1.logo0); 357 | } 358 | else if (logonumber == 1) 359 | { 360 | return getBMPColor(screen1.logo1); 361 | } 362 | else if (logonumber == 2) 363 | { 364 | return getBMPColor(screen1.logo2); 365 | } 366 | else if (logonumber == 3) 367 | { 368 | return getBMPColor(screen1.logo3); 369 | } 370 | else if (logonumber == 4) 371 | { 372 | return getBMPColor(screen1.logo4); 373 | } 374 | else 375 | { 376 | return 0x0000; 377 | } 378 | } 379 | else if (pageNum == 2) 380 | { 381 | if (logonumber == 0) 382 | { 383 | return getBMPColor(screen2.logo0); 384 | } 385 | else if (logonumber == 1) 386 | { 387 | return getBMPColor(screen2.logo1); 388 | } 389 | else if (logonumber == 2) 390 | { 391 | return getBMPColor(screen2.logo2); 392 | } 393 | else if (logonumber == 3) 394 | { 395 | return getBMPColor(screen2.logo3); 396 | } 397 | else if (logonumber == 4) 398 | { 399 | return getBMPColor(screen2.logo4); 400 | } 401 | else 402 | { 403 | return 0x0000; 404 | } 405 | } 406 | else if (pageNum == 3) 407 | { 408 | if (logonumber == 0) 409 | { 410 | return getBMPColor(screen3.logo0); 411 | } 412 | else if (logonumber == 1) 413 | { 414 | return getBMPColor(screen3.logo1); 415 | } 416 | else if (logonumber == 2) 417 | { 418 | return getBMPColor(screen3.logo2); 419 | } 420 | else if (logonumber == 3) 421 | { 422 | return getBMPColor(screen3.logo3); 423 | } 424 | else if (logonumber == 4) 425 | { 426 | return getBMPColor(screen3.logo4); 427 | } 428 | else 429 | { 430 | return 0x0000; 431 | } 432 | } 433 | else if (pageNum == 4) 434 | { 435 | if (logonumber == 0) 436 | { 437 | return getBMPColor(screen4.logo0); 438 | } 439 | else if (logonumber == 1) 440 | { 441 | return getBMPColor(screen4.logo1); 442 | } 443 | else if (logonumber == 2) 444 | { 445 | return getBMPColor(screen4.logo2); 446 | } 447 | else if (logonumber == 3) 448 | { 449 | return getBMPColor(screen4.logo3); 450 | } 451 | else if (logonumber == 4) 452 | { 453 | return getBMPColor(screen4.logo4); 454 | } 455 | else 456 | { 457 | return 0x0000; 458 | } 459 | } 460 | else if (pageNum == 5) 461 | { 462 | if (logonumber == 0) 463 | { 464 | return getBMPColor(screen5.logo0); 465 | } 466 | else if (logonumber == 1) 467 | { 468 | return getBMPColor(screen5.logo1); 469 | } 470 | else if (logonumber == 2) 471 | { 472 | return getBMPColor(screen5.logo2); 473 | } 474 | else if (logonumber == 3) 475 | { 476 | return getBMPColor(screen5.logo3); 477 | } 478 | else if (logonumber == 4) 479 | { 480 | return getBMPColor(screen5.logo4); 481 | } 482 | else 483 | { 484 | return 0x0000; 485 | } 486 | } 487 | else if (pageNum == 6) 488 | { 489 | return 0x0000; 490 | } 491 | else 492 | { 493 | return 0x0000; 494 | } 495 | 496 | } 497 | } 498 | 499 | /** 500 | * @brief This function returns the RGB565 colour of the first pixel of the image which 501 | * is being latched to for a given the logo number. The pagenumber is global. 502 | * 503 | * @param logonumber int 504 | * 505 | * @return uint16_t 506 | * 507 | * @note Uses getBMPColor to read the actual image data. 508 | */ 509 | uint16_t getLatchImageBG(int logonumber) 510 | { 511 | 512 | if (pageNum == 1) 513 | { 514 | if (logonumber == 0) 515 | { 516 | if (strcmp(menu1.button0.latchlogo, "/logos/") == 0) 517 | { 518 | return getBMPColor(screen1.logo0); 519 | } 520 | return getBMPColor(menu1.button0.latchlogo); 521 | } 522 | else if (logonumber == 1) 523 | { 524 | if (strcmp(menu1.button1.latchlogo, "/logos/") == 0) 525 | { 526 | return getBMPColor(screen1.logo1); 527 | } 528 | return getBMPColor(menu1.button1.latchlogo); 529 | } 530 | else if (logonumber == 2) 531 | { 532 | if (strcmp(menu1.button2.latchlogo, "/logos/") == 0) 533 | { 534 | return getBMPColor(screen1.logo2); 535 | } 536 | return getBMPColor(menu1.button2.latchlogo); 537 | } 538 | else if (logonumber == 3) 539 | { 540 | if (strcmp(menu1.button3.latchlogo, "/logos/") == 0) 541 | { 542 | return getBMPColor(screen1.logo3); 543 | } 544 | return getBMPColor(menu1.button3.latchlogo); 545 | } 546 | else if (logonumber == 4) 547 | { 548 | if (strcmp(menu1.button4.latchlogo, "/logos/") == 0) 549 | { 550 | return getBMPColor(screen1.logo4); 551 | } 552 | return getBMPColor(menu1.button4.latchlogo); 553 | } 554 | else 555 | { 556 | return 0x0000; 557 | } 558 | } 559 | else if (pageNum == 2) 560 | { 561 | if (logonumber == 0) 562 | { 563 | if (strcmp(menu2.button0.latchlogo, "/logos/") == 0) 564 | { 565 | return getBMPColor(screen2.logo0); 566 | } 567 | return getBMPColor(menu2.button0.latchlogo); 568 | } 569 | else if (logonumber == 1) 570 | { 571 | if (strcmp(menu2.button1.latchlogo, "/logos/") == 0) 572 | { 573 | return getBMPColor(screen2.logo1); 574 | } 575 | return getBMPColor(menu2.button1.latchlogo); 576 | } 577 | else if (logonumber == 2) 578 | { 579 | if (strcmp(menu2.button2.latchlogo, "/logos/") == 0) 580 | { 581 | return getBMPColor(screen2.logo2); 582 | } 583 | return getBMPColor(menu2.button2.latchlogo); 584 | } 585 | else if (logonumber == 3) 586 | { 587 | if (strcmp(menu2.button3.latchlogo, "/logos/") == 0) 588 | { 589 | return getBMPColor(screen2.logo3); 590 | } 591 | return getBMPColor(menu2.button3.latchlogo); 592 | } 593 | else if (logonumber == 4) 594 | { 595 | if (strcmp(menu2.button4.latchlogo, "/logos/") == 0) 596 | { 597 | return getBMPColor(screen2.logo4); 598 | } 599 | return getBMPColor(menu2.button4.latchlogo); 600 | } 601 | else 602 | { 603 | return 0x0000; 604 | } 605 | } 606 | else if (pageNum == 3) 607 | { 608 | if (logonumber == 0) 609 | { 610 | if (strcmp(menu3.button0.latchlogo, "/logos/") == 0) 611 | { 612 | return getBMPColor(screen3.logo0); 613 | } 614 | return getBMPColor(menu3.button0.latchlogo); 615 | } 616 | else if (logonumber == 1) 617 | { 618 | if (strcmp(menu3.button1.latchlogo, "/logos/") == 0) 619 | { 620 | return getBMPColor(screen3.logo1); 621 | } 622 | return getBMPColor(menu3.button1.latchlogo); 623 | } 624 | else if (logonumber == 2) 625 | { 626 | if (strcmp(menu3.button2.latchlogo, "/logos/") == 0) 627 | { 628 | return getBMPColor(screen3.logo2); 629 | } 630 | return getBMPColor(menu3.button2.latchlogo); 631 | } 632 | else if (logonumber == 3) 633 | { 634 | if (strcmp(menu3.button3.latchlogo, "/logos/") == 0) 635 | { 636 | return getBMPColor(screen3.logo3); 637 | } 638 | return getBMPColor(menu3.button3.latchlogo); 639 | } 640 | else if (logonumber == 4) 641 | { 642 | if (strcmp(menu3.button4.latchlogo, "/logos/") == 0) 643 | { 644 | return getBMPColor(screen3.logo4); 645 | } 646 | return getBMPColor(menu3.button4.latchlogo); 647 | } 648 | else 649 | { 650 | return 0x0000; 651 | } 652 | } 653 | else if (pageNum == 4) 654 | { 655 | if (logonumber == 0) 656 | { 657 | if (strcmp(menu4.button0.latchlogo, "/logos/") == 0) 658 | { 659 | return getBMPColor(screen4.logo0); 660 | } 661 | return getBMPColor(menu4.button0.latchlogo); 662 | } 663 | else if (logonumber == 1) 664 | { 665 | if (strcmp(menu4.button1.latchlogo, "/logos/") == 0) 666 | { 667 | return getBMPColor(screen4.logo1); 668 | } 669 | return getBMPColor(menu4.button1.latchlogo); 670 | } 671 | else if (logonumber == 2) 672 | { 673 | if (strcmp(menu4.button2.latchlogo, "/logos/") == 0) 674 | { 675 | return getBMPColor(screen4.logo2); 676 | } 677 | return getBMPColor(menu4.button2.latchlogo); 678 | } 679 | else if (logonumber == 3) 680 | { 681 | if (strcmp(menu4.button3.latchlogo, "/logos/") == 0) 682 | { 683 | return getBMPColor(screen4.logo3); 684 | } 685 | return getBMPColor(menu4.button3.latchlogo); 686 | } 687 | else if (logonumber == 4) 688 | { 689 | if (strcmp(menu4.button4.latchlogo, "/logos/") == 0) 690 | { 691 | return getBMPColor(screen4.logo4); 692 | } 693 | return getBMPColor(menu4.button4.latchlogo); 694 | } 695 | else 696 | { 697 | return 0x0000; 698 | } 699 | } 700 | else if (pageNum == 5) 701 | { 702 | if (logonumber == 0) 703 | { 704 | if (strcmp(menu5.button0.latchlogo, "/logos/") == 0) 705 | { 706 | return getBMPColor(screen5.logo0); 707 | } 708 | return getBMPColor(menu5.button0.latchlogo); 709 | } 710 | else if (logonumber == 1) 711 | { 712 | if (strcmp(menu5.button1.latchlogo, "/logos/") == 0) 713 | { 714 | return getBMPColor(screen5.logo1); 715 | } 716 | return getBMPColor(menu5.button1.latchlogo); 717 | } 718 | else if (logonumber == 2) 719 | { 720 | if (strcmp(menu5.button2.latchlogo, "/logos/") == 0) 721 | { 722 | return getBMPColor(screen5.logo2); 723 | } 724 | return getBMPColor(menu5.button2.latchlogo); 725 | } 726 | else if (logonumber == 3) 727 | { 728 | if (strcmp(menu5.button3.latchlogo, "/logos/") == 0) 729 | { 730 | return getBMPColor(screen5.logo3); 731 | } 732 | return getBMPColor(menu5.button3.latchlogo); 733 | } 734 | else if (logonumber == 4) 735 | { 736 | if (strcmp(menu5.button4.latchlogo, "/logos/") == 0) 737 | { 738 | return getBMPColor(screen5.logo4); 739 | } 740 | return getBMPColor(menu5.button4.latchlogo); 741 | } 742 | else 743 | { 744 | return 0x0000; 745 | } 746 | } 747 | else if (pageNum == 6) 748 | { 749 | return 0x0000; 750 | } 751 | else 752 | { 753 | return 0x0000; 754 | } 755 | } 756 | -------------------------------------------------------------------------------- /Touch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief This function presents the user with 4 points to touch and saves 3 | that data to a claibration file. 4 | * 5 | * @param none 6 | * 7 | * @return none 8 | * 9 | * @note If USECAPTOUCH is defined we do not need to calibrate touch 10 | */ 11 | 12 | #if !defined(USECAPTOUCH) 13 | void touch_calibrate() 14 | { 15 | uint16_t calData[5]; 16 | uint8_t calDataOK = 0; 17 | 18 | // check if calibration file exists and size is correct 19 | if (FILESYSTEM.exists(CALIBRATION_FILE)) 20 | { 21 | if (REPEAT_CAL) 22 | { 23 | // Delete if we want to re-calibrate 24 | FILESYSTEM.remove(CALIBRATION_FILE); 25 | } 26 | else 27 | { 28 | File f = FILESYSTEM.open(CALIBRATION_FILE, "r"); 29 | if (f) 30 | { 31 | if (f.readBytes((char *)calData, 14) == 14) 32 | calDataOK = 1; 33 | f.close(); 34 | } 35 | } 36 | } 37 | 38 | if (calDataOK && !REPEAT_CAL) 39 | { 40 | // calibration data valid 41 | tft.setTouch(calData); 42 | } 43 | else 44 | { 45 | // data not valid so recalibrate 46 | tft.fillScreen(TFT_BLACK); 47 | tft.setCursor(20, 0); 48 | tft.setTextFont(2); 49 | tft.setTextSize(1); 50 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 51 | 52 | tft.println("Touch corners as indicated"); 53 | 54 | tft.setTextFont(1); 55 | tft.println(); 56 | 57 | if (REPEAT_CAL) 58 | { 59 | tft.setTextColor(TFT_RED, TFT_BLACK); 60 | tft.println("Set REPEAT_CAL to false to stop this running again!"); 61 | } 62 | 63 | tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); 64 | 65 | tft.setTextColor(TFT_GREEN, TFT_BLACK); 66 | tft.println("Calibration complete!"); 67 | 68 | // store data 69 | File f = FILESYSTEM.open(CALIBRATION_FILE, "w"); 70 | if (f) 71 | { 72 | f.write((const unsigned char *)calData, 14); 73 | f.close(); 74 | } 75 | } 76 | } 77 | #endif //!defined(USECAPTOUCH) 78 | -------------------------------------------------------------------------------- /UserActions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * User actions you can modify these functions to do custom stuff. 3 | * Have a look at the 3 examples for some inspiration. Also, have a look 4 | * at Actions.h to see what actions you can use (eg. print, write, press, etc.) 5 | * 6 | */ 7 | 8 | // After any action you might need a delay, this delay (in ms) is defined here: 9 | #define USER_ACTION_DELAY 50 10 | 11 | // Function used to print large pieces of text. 12 | void printLargeString(const char string[]); 13 | 14 | void userAction1(){ 15 | 16 | // (All OS) This functions prints a large string of text to the active window. 17 | printLargeString("This is an example of printing long pieces of text."); 18 | delay(USER_ACTION_DELAY); 19 | bleKeyboard.write(KEY_RETURN); 20 | printLargeString("After KEY_RETURN it will print on a new line."); 21 | 22 | } 23 | 24 | void userAction2(){ 25 | 26 | } 27 | 28 | void userAction3(){ 29 | 30 | // (Windows Only) This function rickroll's you. 31 | 32 | bleKeyboard.press(KEY_LEFT_GUI); 33 | delay(USER_ACTION_DELAY); 34 | bleKeyboard.print("r"); 35 | bleKeyboard.releaseAll(); 36 | delay(500); 37 | printLargeString("https://youtu.be/dQw4w9WgXcQ"); 38 | bleKeyboard.write(KEY_RETURN); 39 | 40 | } 41 | 42 | void userAction4(){ 43 | 44 | // (Mac Only) This function rickroll's you. 45 | 46 | bleKeyboard.press(KEY_LEFT_GUI); 47 | delay(USER_ACTION_DELAY); 48 | bleKeyboard.print(" "); 49 | bleKeyboard.releaseAll(); 50 | delay(USER_ACTION_DELAY); 51 | printLargeString("https://youtu.be/dQw4w9WgXcQ"); 52 | bleKeyboard.write(KEY_RETURN); 53 | 54 | } 55 | 56 | void userAction5(){ 57 | 58 | // (Mac only) This opens a new file in Sublime (has to be installed off course and pastes the last thing you copied to the clipboard. 59 | // I use this to select pieces of text and copy them to a new file. 60 | bleKeyboard.press(KEY_LEFT_GUI); 61 | delay(USER_ACTION_DELAY); 62 | bleKeyboard.print(" "); 63 | bleKeyboard.releaseAll(); 64 | printLargeString("Sublime"); 65 | bleKeyboard.write(KEY_RETURN); 66 | delay(500); 67 | bleKeyboard.press(KEY_LEFT_GUI); 68 | bleKeyboard.print("n"); 69 | bleKeyboard.releaseAll(); 70 | delay(USER_ACTION_DELAY); 71 | bleKeyboard.press(KEY_LEFT_GUI); 72 | bleKeyboard.print("v"); 73 | bleKeyboard.releaseAll(); 74 | 75 | } 76 | 77 | void userAction6(){ 78 | 79 | // (Windows only) This opens a new file in Sublime (has to be installed off course and pastes the last thing you copied to the clipboard. 80 | // I use this to select pieces of text and copy them to a new file. 81 | bleKeyboard.press(KEY_LEFT_GUI); 82 | delay(USER_ACTION_DELAY); 83 | bleKeyboard.print("r"); 84 | bleKeyboard.releaseAll(); 85 | delay(500); 86 | printLargeString("notepad"); 87 | bleKeyboard.write(KEY_RETURN); 88 | delay(500); 89 | bleKeyboard.press(KEY_LEFT_CTRL); 90 | bleKeyboard.print("v"); 91 | bleKeyboard.releaseAll(); 92 | 93 | } 94 | 95 | void userAction7(){ 96 | 97 | // Nothing yet, create your own! 98 | 99 | } 100 | 101 | 102 | 103 | /* A simple function to print large strings using bleKeyboard. The function has a delay 104 | * between keypresses to not miss any, or hang on 1. 105 | */ 106 | 107 | void printLargeString(const char string[]){ 108 | 109 | for(int i=0; i < strlen(string); i++ ) { 110 | char c = string[i]; 111 | bleKeyboard.print(c); 112 | delay(10); // 10ms is on most systems enough to not miss a character 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/Bottom.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/Bottom.stl -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/Case_for_tft_with_headers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/Case_for_tft_with_headers.png -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/FreeTouchDeck Case V1.f3z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/FreeTouchDeck Case V1.f3z -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/FreeTouchDeck_Case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/FreeTouchDeck_Case.png -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/Top.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/Top.stl -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/Top_for_TFT_with_Headers.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/Top_for_TFT_with_Headers.stl -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/bottom_screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/bottom_screenshot.jpg -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/freetouchdeck_case1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/freetouchdeck_case1.jpg -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/readme.md: -------------------------------------------------------------------------------- 1 | # FreeTouchDeck Case 2 | 3 | **Pay attention! This case is for the combiner board. For the ESP32 TouchDown board, check this link: https://github.com/DustinWatts/esp32-touchdown/tree/main/Case** 4 | 5 | ![](freetouchdeck_case1.jpg) 6 | 7 | You can print the case that will house both The ILI9488 + TFT screen and you ESP32 in a nice case. This housing is made with the ESP32 TFT Combiner PCB in mind, but will also work if you made your FreeTouchDeck on a prototyping board. 8 | 9 | This case is made to have a press fit. If you are happy with your print and don't plan on taking it apart again, you could use some CA glue to hold the two halves together, but is it not necessary. 10 | 11 | ## Two different "Top" versions 12 | 13 | There are two "Top" versions. The only difference is that one of them is a bit higher. If you use the TFT + ESP32 combiner PCB with female headers for the TFT it needs a slightly higher front. So if you are using female headers for the TFT --> use Top_for_TFT_with_Headers.stl! 14 | 15 | # Print settings 16 | 17 | I recommend the following settings when printing the case: 18 | 19 | ## Top 20 | 21 | - Orientation: Flat side down 22 | - Layer height: 0.2 mm 23 | - Infill: 100% 24 | - Support: none 25 | 26 | ![](top_screenshot.jpg) 27 | 28 | ## Top for TFT with Headers 29 | 30 | - Orientation: Flat side down 31 | - Layer height: 0.2 mm 32 | - Infill: 100% 33 | - Support: none 34 | 35 | ![](Case_for_tft_with_headers.png) 36 | 37 | 38 | ## Bottom 39 | 40 | - Orientation: The part that connects to the top down 41 | - Layer Height: 0.2 mm 42 | - Infill: 20% 43 | - Support: Everywhere 44 | - Support Overhang Angle: 70 45 | 46 | ![](bottom_screenshot.jpg) 47 | 48 | Setting the Support Overhang Angle to 70 ensures that the hole for the USB cable and the the press fit ridge are properly supported, but doesn't over-support everything. 49 | 50 | You can also download the case from Thingiverse: [FreeTouchDeck Case](https://www.thingiverse.com/thing:4661069) 51 | 52 | Or any of the awesome cases other people have designed! [FreeTouchDeck Cases on Thingiverse](https://www.thingiverse.com/search?q=freetouchdeck&type=things&sort=relevant) 53 | -------------------------------------------------------------------------------- /case/ESP32_TFT_Combiner_Case/top_screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/case/ESP32_TFT_Combiner_Case/top_screenshot.jpg -------------------------------------------------------------------------------- /data/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo0": "question.bmp", 3 | "logo1": "question.bmp", 4 | "logo2": "question.bmp", 5 | "logo3": "question.bmp", 6 | "logo4": "question.bmp", 7 | "button0":{ 8 | "latch": false, 9 | "latchlogo": "", 10 | "actionarray": [ "0", "0", "0" ], 11 | "valuearray": [ "0", "0", "0" ] 12 | }, 13 | "button1":{ 14 | "latch": false, 15 | "latchlogo": "", 16 | "actionarray": [ "0", "0", "0" ], 17 | "valuearray": [ "0", "0", "0" ] 18 | }, 19 | "button2":{ 20 | "latch": false, 21 | "latchlogo": "", 22 | "actionarray": [ "0", "0", "0" ], 23 | "valuearray": [ "0", "0", "0" ] 24 | }, 25 | "button3":{ 26 | "latch": false, 27 | "latchlogo": "", 28 | "actionarray": [ "0", "0", "0"], 29 | "valuearray": [ "0", "0", "0" ] 30 | }, 31 | "button4":{ 32 | "latch": false, 33 | "latchlogo": "", 34 | "actionarray": [ "0", "0", "0" ], 35 | "valuearray": [ "0", "0", "0" ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data/config/general.json: -------------------------------------------------------------------------------- 1 | { 2 | "menubuttoncolor": "#009bf4", 3 | "functionbuttoncolor": "#00efcb", 4 | "latchcolor": "#fe0149", 5 | "background": "#000000", 6 | "sleepenable": true, 7 | "sleeptimer": 10, 8 | "beep": true, 9 | "modifier1": 130, 10 | "modifier2": 129, 11 | "modifier3": 0, 12 | "helperdelay": 500 13 | } -------------------------------------------------------------------------------- /data/config/homescreen.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo0": "music.bmp", 3 | "logo1": "obs.bmp", 4 | "logo2": "firefox.bmp", 5 | "logo3": "mail.bmp", 6 | "logo4": "youtube.bmp", 7 | "logo5": "settings.bmp" 8 | } -------------------------------------------------------------------------------- /data/config/menu1.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo0": "mute.bmp", 3 | "logo1": "volumedown.bmp", 4 | "logo2": "volumeup.bmp", 5 | "logo3": "play.bmp", 6 | "logo4": "stop.bmp", 7 | "button0":{ 8 | "latch": true, 9 | "latchlogo": "", 10 | "actionarray": [ "3", "0", "0" ], 11 | "valuearray": [ "1", "0", "0" ] 12 | }, 13 | "button1":{ 14 | "latch": false, 15 | "latchlogo": "", 16 | "actionarray": [ "3", "0", "0" ], 17 | "valuearray": [ "2", "0", "0" ] 18 | }, 19 | "button2":{ 20 | "latch": false, 21 | "latchlogo": "", 22 | "actionarray": [ "3", "0", "0" ], 23 | "valuearray": [ "3", "0", "0" ] 24 | }, 25 | "button3":{ 26 | "latch": false, 27 | "latchlogo": "", 28 | "actionarray": [ "3", "0", "0"], 29 | "valuearray": [ "4", "0", "0" ] 30 | }, 31 | "button4":{ 32 | "latch": false, 33 | "latchlogo": "", 34 | "actionarray": [ "3", "0", "0" ], 35 | "valuearray": [ "5", "0", "0" ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data/config/menu2.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo0": "question.bmp", 3 | "logo1": "question.bmp", 4 | "logo2": "question.bmp", 5 | "logo3": "question.bmp", 6 | "logo4": "question.bmp", 7 | "button0":{ 8 | "latch": false, 9 | "latchlogo": "", 10 | "actionarray": [ "0", "0", "0" ], 11 | "valuearray": [ "0", "0", "0" ] 12 | }, 13 | "button1":{ 14 | "latch": false, 15 | "latchlogo": "", 16 | "actionarray": [ "0", "0", "0" ], 17 | "valuearray": [ "0", "0", "0" ] 18 | }, 19 | "button2":{ 20 | "latch": false, 21 | "latchlogo": "", 22 | "actionarray": [ "0", "0", "0" ], 23 | "valuearray": [ "0", "0", "0" ] 24 | }, 25 | "button3":{ 26 | "latch": false, 27 | "latchlogo": "", 28 | "actionarray": [ "0", "0", "0"], 29 | "valuearray": [ "0", "0", "0" ] 30 | }, 31 | "button4":{ 32 | "latch": false, 33 | "latchlogo": "", 34 | "actionarray": [ "0", "0", "0" ], 35 | "valuearray": [ "0", "0", "0" ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data/config/menu3.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo0": "question.bmp", 3 | "logo1": "question.bmp", 4 | "logo2": "question.bmp", 5 | "logo3": "question.bmp", 6 | "logo4": "question.bmp", 7 | "button0":{ 8 | "latch": false, 9 | "latchlogo": "", 10 | "actionarray": [ "0", "0", "0" ], 11 | "valuearray": [ "0", "0", "0" ] 12 | }, 13 | "button1":{ 14 | "latch": false, 15 | "latchlogo": "", 16 | "actionarray": [ "0", "0", "0" ], 17 | "valuearray": [ "0", "0", "0" ] 18 | }, 19 | "button2":{ 20 | "latch": false, 21 | "latchlogo": "", 22 | "actionarray": [ "0", "0", "0" ], 23 | "valuearray": [ "0", "0", "0" ] 24 | }, 25 | "button3":{ 26 | "latch": false, 27 | "latchlogo": "", 28 | "actionarray": [ "0", "0", "0"], 29 | "valuearray": [ "0", "0", "0" ] 30 | }, 31 | "button4":{ 32 | "latch": false, 33 | "latchlogo": "", 34 | "actionarray": [ "0", "0", "0" ], 35 | "valuearray": [ "0", "0", "0" ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data/config/menu4.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo0": "question.bmp", 3 | "logo1": "question.bmp", 4 | "logo2": "question.bmp", 5 | "logo3": "question.bmp", 6 | "logo4": "question.bmp", 7 | "button0":{ 8 | "latch": false, 9 | "latchlogo": "", 10 | "actionarray": [ "0", "0", "0" ], 11 | "valuearray": [ "0", "0", "0" ] 12 | }, 13 | "button1":{ 14 | "latch": false, 15 | "latchlogo": "", 16 | "actionarray": [ "0", "0", "0" ], 17 | "valuearray": [ "0", "0", "0" ] 18 | }, 19 | "button2":{ 20 | "latch": false, 21 | "latchlogo": "", 22 | "actionarray": [ "0", "0", "0" ], 23 | "valuearray": [ "0", "0", "0" ] 24 | }, 25 | "button3":{ 26 | "latch": false, 27 | "latchlogo": "", 28 | "actionarray": [ "0", "0", "0"], 29 | "valuearray": [ "0", "0", "0" ] 30 | }, 31 | "button4":{ 32 | "latch": false, 33 | "latchlogo": "", 34 | "actionarray": [ "0", "0", "0" ], 35 | "valuearray": [ "0", "0", "0" ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data/config/menu5.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo0": "question.bmp", 3 | "logo1": "question.bmp", 4 | "logo2": "question.bmp", 5 | "logo3": "question.bmp", 6 | "logo4": "question.bmp", 7 | "button0":{ 8 | "latch": false, 9 | "latchlogo": "", 10 | "actionarray": [ "0", "0", "0" ], 11 | "valuearray": [ "0", "0", "0" ] 12 | }, 13 | "button1":{ 14 | "latch": false, 15 | "latchlogo": "", 16 | "actionarray": [ "0", "0", "0" ], 17 | "valuearray": [ "0", "0", "0" ] 18 | }, 19 | "button2":{ 20 | "latch": false, 21 | "latchlogo": "", 22 | "actionarray": [ "0", "0", "0" ], 23 | "valuearray": [ "0", "0", "0" ] 24 | }, 25 | "button3":{ 26 | "latch": false, 27 | "latchlogo": "", 28 | "actionarray": [ "0", "0", "0"], 29 | "valuearray": [ "0", "0", "0" ] 30 | }, 31 | "button4":{ 32 | "latch": false, 33 | "latchlogo": "", 34 | "actionarray": [ "0", "0", "0" ], 35 | "valuearray": [ "0", "0", "0" ] 36 | } 37 | } -------------------------------------------------------------------------------- /data/config/wificonfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid": "YOUR_WIFI_SSID", 3 | "password": "YOUR_WIFI_PASSWORD", 4 | "wifimode": "WIFI_STA", 5 | "wifihostname": "freetouchdeck", 6 | "attempts": 10, 7 | "attemptdelay": 500 8 | } -------------------------------------------------------------------------------- /data/editor.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FreeTouchDeck Configurator 10 | 11 | 34 | 35 | 36 | 37 | 38 |
39 |

FreeTouchDeck Configurator

40 |
41 |
42 |

%RESULT% %TEXT% file(s) deleted.
43 |

44 | %FILES% 45 |
46 |
47 | 50 |
51 |
52 |
53 | 54 |
55 |

56 |
57 | 58 |
59 | 60 | 61 | -------------------------------------------------------------------------------- /data/error.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FreeTouchDeck Configurator 10 | 11 | 34 | 35 | 36 | 37 | 38 |
39 |

FreeTouchDeck Configurator

40 |
41 |
42 |

Oops! Something went wrong. The configurator returned the following error:
43 |

44 | %ERROR_CODE% : %ERROR_TEXT% 45 |

46 |
47 |
48 | 49 |
50 | 51 |
52 | 53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /data/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/favicon.ico -------------------------------------------------------------------------------- /data/logos/brightnessdown.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/brightnessdown.bmp -------------------------------------------------------------------------------- /data/logos/brightnessup.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/brightnessup.bmp -------------------------------------------------------------------------------- /data/logos/dslr.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/dslr.bmp -------------------------------------------------------------------------------- /data/logos/firefox.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/firefox.bmp -------------------------------------------------------------------------------- /data/logos/freetouchdeck_logo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/freetouchdeck_logo.bmp -------------------------------------------------------------------------------- /data/logos/home.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/home.bmp -------------------------------------------------------------------------------- /data/logos/info.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/info.bmp -------------------------------------------------------------------------------- /data/logos/mail.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/mail.bmp -------------------------------------------------------------------------------- /data/logos/micmute.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/micmute.bmp -------------------------------------------------------------------------------- /data/logos/music.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/music.bmp -------------------------------------------------------------------------------- /data/logos/mute.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/mute.bmp -------------------------------------------------------------------------------- /data/logos/obs.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/obs.bmp -------------------------------------------------------------------------------- /data/logos/play.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/play.bmp -------------------------------------------------------------------------------- /data/logos/question.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/question.bmp -------------------------------------------------------------------------------- /data/logos/settings.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/settings.bmp -------------------------------------------------------------------------------- /data/logos/sleep.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/sleep.bmp -------------------------------------------------------------------------------- /data/logos/spanner.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/spanner.bmp -------------------------------------------------------------------------------- /data/logos/splash.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/splash.bmp -------------------------------------------------------------------------------- /data/logos/stop.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/stop.bmp -------------------------------------------------------------------------------- /data/logos/volumedown.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/volumedown.bmp -------------------------------------------------------------------------------- /data/logos/volumeup.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/volumeup.bmp -------------------------------------------------------------------------------- /data/logos/webcam.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/webcam.bmp -------------------------------------------------------------------------------- /data/logos/wifi.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/wifi.bmp -------------------------------------------------------------------------------- /data/logos/youtube.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/data/logos/youtube.bmp -------------------------------------------------------------------------------- /data/saveconfig.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FreeTouchDeck Configurator 10 | 11 | 38 | 39 | 40 | 41 | 42 |
43 |

FreeTouchDeck Configurator

44 |
45 |
46 |

Configuration Saved! Go back to the configurator or click Restart to re-enable Bluetooth.

47 |
48 |
49 | 50 |
51 |
52 | 53 | 54 |

55 |
56 | 57 |
58 |
59 | 60 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /data/upload.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FreeTouchDeck Configurator 10 | 18 | 19 | 20 | 21 |

FreeTouchDeck Configurator


22 | Upload Succes! 23 | 24 | 25 |

26 |
27 | 30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /user_setup.h Examples/ESP32_Dev_Kit_V1_ILI9488_Resistive.h: -------------------------------------------------------------------------------- 1 | /* This is a stripped down version of a User_Setup.h file for TFT_eSPI 2 | 3 | Copy the contents of this file to TFT_eSPI-master/User_setup.h 4 | 5 | This version is for a 38-pin ESP32 DevKitC and an ILI9488 screen with resistive touch controller. 6 | 7 | */ 8 | 9 | #define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) 10 | 11 | #define TFT_BL 32 // LED back-light control pin 12 | #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) 13 | 14 | #define TFT_MISO 19 15 | #define TFT_MOSI 23 16 | #define TFT_SCLK 18 17 | #define TFT_CS 15 // Chip select control pin 18 | #define TFT_DC 2 // Data Command control pin 19 | #define TFT_RST 4 // Reset pin (could connect to RST pin) 20 | 21 | #define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen 22 | 23 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 24 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 25 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 26 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 27 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 28 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 29 | 30 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 31 | 32 | #define SMOOTH_FONT 33 | 34 | #define SPI_FREQUENCY 27000000 35 | #define SPI_READ_FREQUENCY 20000000 36 | #define SPI_TOUCH_FREQUENCY 2500000 37 | -------------------------------------------------------------------------------- /user_setup.h Examples/ESP32_Dev_Kit_V1_ILI9488_Resistive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DustinWatts/FreeTouchDeck/6f6556a3ceaf05b8909d116daa350d8832763e1b/user_setup.h Examples/ESP32_Dev_Kit_V1_ILI9488_Resistive.png -------------------------------------------------------------------------------- /user_setup.h Examples/ESP32_TouchDown_User_Setup.h: -------------------------------------------------------------------------------- 1 | // USER DEFINED SETTINGS 2 | // Set driver type, fonts to be loaded, pins used and SPI control method etc 3 | // 4 | // See the User_Setup_Select.h file if you wish to be able to define multiple 5 | // setups and then easily select which setup file is used by the compiler. 6 | // 7 | // If this file is edited correctly then all the library example sketches should 8 | // run without the need to make any more changes for a particular hardware setup! 9 | // Note that some sketches are designed for a particular TFT pixel width/height 10 | 11 | 12 | // ################################################################################## 13 | // 14 | // Section 1. Call up the right driver file and any options for it 15 | // 16 | // ################################################################################## 17 | 18 | // Define STM32 to invoke optimised processor support (only for STM32) 19 | //#define STM32 20 | 21 | // Defining the STM32 board allows the library to optimise the performance 22 | // for UNO compatible "MCUfriend" style shields 23 | //#define NUCLEO_64_TFT 24 | //#define NUCLEO_144_TFT 25 | 26 | // STM32 8 bit parallel only: 27 | // If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7 28 | // then this will improve rendering performance by a factor of ~8x 29 | //#define STM_PORTA_DATA_BUS 30 | //#define STM_PORTA_DATA_BUS 31 | 32 | // Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) 33 | //#define TFT_PARALLEL_8_BIT 34 | 35 | // Display type - only define if RPi display 36 | //#define RPI_DISPLAY_TYPE // 20MHz maximum SPI 37 | 38 | // Only define one driver, the other ones must be commented out 39 | //#define ILI9341_DRIVER 40 | //#define ST7735_DRIVER // Define additional parameters below for this display 41 | //#define ILI9163_DRIVER // Define additional parameters below for this display 42 | //#define S6D02A1_DRIVER 43 | //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI 44 | //#define HX8357D_DRIVER 45 | //#define ILI9481_DRIVER 46 | //#define ILI9486_DRIVER 47 | #define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) 48 | //#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display 49 | //#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display 50 | //#define R61581_DRIVER 51 | //#define RM68140_DRIVER 52 | //#define ST7796_DRIVER 53 | //#define SSD1963_480_DRIVER 54 | //#define SSD1963_800_DRIVER 55 | //#define SSD1963_800ALT_DRIVER 56 | //#define ILI9225_DRIVER 57 | 58 | // Some displays support SPI reads via the MISO pin, other displays have a single 59 | // bi-directional SDA pin and the library will try to read this via the MOSI line. 60 | // To use the SDA line for reading data from the TFT uncomment the following line: 61 | 62 | // #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only 63 | 64 | // For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display 65 | // Try ONE option at a time to find the correct colour order for your display 66 | 67 | // #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue 68 | // #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red 69 | 70 | // For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below 71 | 72 | // #define M5STACK 73 | 74 | // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation 75 | // #define TFT_WIDTH 80 76 | // #define TFT_WIDTH 128 77 | // #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 78 | // #define TFT_HEIGHT 160 79 | // #define TFT_HEIGHT 128 80 | // #define TFT_HEIGHT 240 // ST7789 240 x 240 81 | // #define TFT_HEIGHT 320 // ST7789 240 x 320 82 | 83 | // For ST7735 ONLY, define the type of display, originally this was based on the 84 | // colour of the tab on the screen protector film but this is not always true, so try 85 | // out the different options below if the screen does not display graphics correctly, 86 | // e.g. colours wrong, mirror images, or tray pixels at the edges. 87 | // Comment out ALL BUT ONE of these options for a ST7735 display driver, save this 88 | // this User_Setup file, then rebuild and upload the sketch to the board again: 89 | 90 | // #define ST7735_INITB 91 | // #define ST7735_GREENTAB 92 | // #define ST7735_GREENTAB2 93 | // #define ST7735_GREENTAB3 94 | // #define ST7735_GREENTAB128 // For 128 x 128 display 95 | // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) 96 | // #define ST7735_REDTAB 97 | // #define ST7735_BLACKTAB 98 | // #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset 99 | 100 | // If colours are inverted (white shows as black) then uncomment one of the next 101 | // 2 lines try both options, one of the options should correct the inversion. 102 | 103 | // #define TFT_INVERSION_ON 104 | // #define TFT_INVERSION_OFF 105 | 106 | 107 | // ################################################################################## 108 | // 109 | // Section 2. Define the pins that are used to interface with the display here 110 | // 111 | // ################################################################################## 112 | 113 | // If a backlight control signal is available then define the TFT_BL pin in Section 2 114 | // below. The backlight will be turned ON when tft.begin() is called, but the library 115 | // needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be 116 | // driven with a PWM signal or turned OFF/ON then this must be handled by the user 117 | // sketch. e.g. with digitalWrite(TFT_BL, LOW); 118 | 119 | #define TFT_BL 32 // LED back-light control pin 120 | // #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) 121 | 122 | 123 | 124 | // We must use hardware SPI, a minimum of 3 GPIO pins is needed. 125 | // Typical setup for ESP8266 NodeMCU ESP-12 is : 126 | // 127 | // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) 128 | // Display LED to NodeMCU pin VIN (or 5V, see below) 129 | // Display SCK to NodeMCU pin D5 130 | // Display SDI/MOSI to NodeMCU pin D7 131 | // Display DC (RS/AO)to NodeMCU pin D3 132 | // Display RESET to NodeMCU pin D4 (or RST, see below) 133 | // Display CS to NodeMCU pin D8 (or GND, see below) 134 | // Display GND to NodeMCU pin GND (0V) 135 | // Display VCC to NodeMCU 5V or 3.3V 136 | // 137 | // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin 138 | // 139 | // The DC (Data Command) pin may be labeled AO or RS (Register Select) 140 | // 141 | // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more 142 | // SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS 143 | // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin 144 | // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. 145 | // 146 | // The NodeMCU D0 pin can be used for RST 147 | // 148 | // 149 | // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin 150 | // If 5V is not available at a pin you can use 3.3V but backlight brightness 151 | // will be lower. 152 | 153 | 154 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### 155 | 156 | // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation 157 | //#define TFT_CS PIN_D8 // Chip select control pin D8 158 | //#define TFT_DC PIN_D3 // Data Command control pin 159 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 160 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 161 | 162 | //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) 163 | 164 | //#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen 165 | 166 | //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only 167 | 168 | 169 | // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### 170 | 171 | // Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact 172 | // but saves pins for other functions. It is best not to connect MISO as some displays 173 | // do not tristate that line wjen chip select is high! 174 | // On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode 175 | // On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK 176 | // In ESP8266 overlap mode the following must be defined 177 | 178 | //#define TFT_SPI_OVERLAP 179 | 180 | // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 181 | //#define TFT_CS PIN_D3 182 | //#define TFT_DC PIN_D5 // Data Command control pin 183 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 184 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 185 | 186 | 187 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### 188 | 189 | // For ESP32 Dev board (only tested with ILI9341 display) 190 | // The hardware SPI can be mapped to any pins 191 | 192 | #define TFT_MISO 19 193 | #define TFT_MOSI 23 194 | #define TFT_SCLK 18 195 | #define TFT_CS 15 // Chip select control pin 196 | #define TFT_DC 2 // Data Command control pin 197 | #define TFT_RST 4 // Reset pin (could connect to RST pin) 198 | //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST 199 | 200 | #define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen 201 | 202 | //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only 203 | 204 | // For the M5Stack module use these #define lines 205 | //#define TFT_MISO 19 206 | //#define TFT_MOSI 23 207 | //#define TFT_SCLK 18 208 | //#define TFT_CS 14 // Chip select control pin 209 | //#define TFT_DC 27 // Data Command control pin 210 | //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) 211 | //#define TFT_BL 32 // LED back-light (required for M5Stack) 212 | 213 | // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### 214 | 215 | // The library supports 8 bit parallel TFTs with the ESP32, the pin 216 | // selection below is compatible with ESP32 boards in UNO format. 217 | // Wemos D32 boards need to be modified, see diagram in Tools folder. 218 | // Only ILI9481 and ILI9341 based displays have been tested! 219 | 220 | // Parallel bus is only supported for the STM32 and ESP32 221 | // Example below is for ESP32 Parallel interface with UNO displays 222 | 223 | // Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) 224 | //#define TFT_PARALLEL_8_BIT 225 | 226 | // The ESP32 and TFT the pins used for testing are: 227 | //#define TFT_CS 33 // Chip select control pin (library pulls permanently low 228 | //#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 229 | //#define TFT_RST 32 // Reset pin, toggles on startup 230 | 231 | //#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 232 | //#define TFT_RD 2 // Read strobe control pin 233 | 234 | //#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus 235 | //#define TFT_D1 13 // so a single register write sets/clears all bits. 236 | //#define TFT_D2 26 // Pins can be randomly assigned, this does not affect 237 | //#define TFT_D3 25 // TFT screen update performance. 238 | //#define TFT_D4 17 239 | //#define TFT_D5 16 240 | //#define TFT_D6 27 241 | //#define TFT_D7 14 242 | 243 | // ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ###### 244 | 245 | // The TFT can be connected to SPI port 1 or 2 246 | //#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz 247 | //#define TFT_MOSI PA7 248 | //#define TFT_MISO PA6 249 | //#define TFT_SCLK PA5 250 | 251 | //#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz 252 | //#define TFT_MOSI PB15 253 | //#define TFT_MISO PB14 254 | //#define TFT_SCLK PB13 255 | 256 | // Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select 257 | //#define TFT_CS D5 // Chip select control pin to TFT CS 258 | //#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select) 259 | //#define TFT_RST D7 // Reset pin to TFT RST (or RESET) 260 | // OR alternatively, we can use STM32 port reference names PXnn 261 | //#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5 262 | //#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6 263 | //#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7 264 | 265 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset 266 | // Use an Arduino pin for initial testing as connecting to processor reset 267 | // may not work (pulse too short at power up?) 268 | 269 | // ################################################################################## 270 | // 271 | // Section 3. Define the fonts that are to be used here 272 | // 273 | // ################################################################################## 274 | 275 | // Comment out the #defines below with // to stop that font being loaded 276 | // The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not 277 | // normally necessary. If all fonts are loaded the extra FLASH space required is 278 | // about 17Kbytes. To save FLASH space only enable the fonts you need! 279 | 280 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 281 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 282 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 283 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 284 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 285 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 286 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 287 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 288 | 289 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 290 | // this will save ~20kbytes of FLASH 291 | #define SMOOTH_FONT 292 | 293 | 294 | // ################################################################################## 295 | // 296 | // Section 4. Other options 297 | // 298 | // ################################################################################## 299 | 300 | // Define the SPI clock frequency, this affects the graphics rendering speed. Too 301 | // fast and the TFT driver will not keep up and display corruption appears. 302 | // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails 303 | // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) 304 | // With an ILI9163 display 27 MHz works OK. 305 | 306 | // #define SPI_FREQUENCY 1000000 307 | // #define SPI_FREQUENCY 5000000 308 | // #define SPI_FREQUENCY 10000000 309 | // #define SPI_FREQUENCY 20000000 310 | #define SPI_FREQUENCY 27000000 311 | // #define SPI_FREQUENCY 40000000 312 | // #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) 313 | // #define SPI_FREQUENCY 80000000 314 | 315 | // Optional reduced SPI frequency for reading TFT 316 | #define SPI_READ_FREQUENCY 20000000 317 | 318 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 319 | #define SPI_TOUCH_FREQUENCY 2500000 320 | 321 | // The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. 322 | // If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) 323 | // then uncomment the following line: 324 | //#define USE_HSPI_PORT 325 | 326 | // Comment out the following #define if "SPI Transactions" do not need to be 327 | // supported. When commented out the code size will be smaller and sketches will 328 | // run slightly faster, so leave it commented out unless you need it! 329 | 330 | // Transaction support is needed to work with SD library but not needed with TFT_SdFat 331 | // Transaction support is required if other SPI devices are connected. 332 | 333 | // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) 334 | // so changing it here has no effect 335 | 336 | // #define SUPPORT_TRANSACTIONS 337 | -------------------------------------------------------------------------------- /user_setup.h Examples/Makerfabs_Capacitive_Touch_User_Setup.h: -------------------------------------------------------------------------------- 1 | // USER DEFINED SETTINGS 2 | // Set driver type, fonts to be loaded, pins used and SPI control method etc 3 | // 4 | // See the User_Setup_Select.h file if you wish to be able to define multiple 5 | // setups and then easily select which setup file is used by the compiler. 6 | // 7 | // If this file is edited correctly then all the library example sketches should 8 | // run without the need to make any more changes for a particular hardware setup! 9 | // Note that some sketches are designed for a particular TFT pixel width/height 10 | 11 | 12 | // ################################################################################## 13 | // 14 | // Section 1. Call up the right driver file and any options for it 15 | // 16 | // ################################################################################## 17 | 18 | // Define STM32 to invoke optimised processor support (only for STM32) 19 | //#define STM32 20 | 21 | // Defining the STM32 board allows the library to optimise the performance 22 | // for UNO compatible "MCUfriend" style shields 23 | //#define NUCLEO_64_TFT 24 | //#define NUCLEO_144_TFT 25 | 26 | // STM32 8 bit parallel only: 27 | // If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7 28 | // then this will improve rendering performance by a factor of ~8x 29 | //#define STM_PORTA_DATA_BUS 30 | //#define STM_PORTA_DATA_BUS 31 | 32 | // Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) 33 | //#define TFT_PARALLEL_8_BIT 34 | 35 | // Display type - only define if RPi display 36 | //#define RPI_DISPLAY_TYPE // 20MHz maximum SPI 37 | 38 | // Only define one driver, the other ones must be commented out 39 | //#define ILI9341_DRIVER 40 | //#define ST7735_DRIVER // Define additional parameters below for this display 41 | //#define ILI9163_DRIVER // Define additional parameters below for this display 42 | //#define S6D02A1_DRIVER 43 | //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI 44 | //#define HX8357D_DRIVER 45 | //#define ILI9481_DRIVER 46 | //#define ILI9486_DRIVER 47 | #define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) 48 | //#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display 49 | //#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display 50 | //#define R61581_DRIVER 51 | //#define RM68140_DRIVER 52 | //#define ST7796_DRIVER 53 | 54 | // Some displays support SPI reads via the MISO pin, other displays have a single 55 | // bi-directional SDA pin and the library will try to read this via the MOSI line. 56 | // To use the SDA line for reading data from the TFT uncomment the following line: 57 | 58 | // #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only 59 | 60 | // For ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display 61 | // Try ONE option at a time to find the correct colour order for your display 62 | 63 | // #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue 64 | // #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red 65 | 66 | // For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below 67 | 68 | // #define M5STACK 69 | 70 | // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation 71 | // #define TFT_WIDTH 80 72 | // #define TFT_WIDTH 128 73 | // #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 74 | // #define TFT_HEIGHT 160 75 | // #define TFT_HEIGHT 128 76 | // #define TFT_HEIGHT 240 // ST7789 240 x 240 77 | // #define TFT_HEIGHT 320 // ST7789 240 x 320 78 | 79 | // For ST7735 ONLY, define the type of display, originally this was based on the 80 | // colour of the tab on the screen protector film but this is not always true, so try 81 | // out the different options below if the screen does not display graphics correctly, 82 | // e.g. colours wrong, mirror images, or tray pixels at the edges. 83 | // Comment out ALL BUT ONE of these options for a ST7735 display driver, save this 84 | // this User_Setup file, then rebuild and upload the sketch to the board again: 85 | 86 | // #define ST7735_INITB 87 | // #define ST7735_GREENTAB 88 | // #define ST7735_GREENTAB2 89 | // #define ST7735_GREENTAB3 90 | // #define ST7735_GREENTAB128 // For 128 x 128 display 91 | // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) 92 | // #define ST7735_REDTAB 93 | // #define ST7735_BLACKTAB 94 | // #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset 95 | 96 | // If colours are inverted (white shows as black) then uncomment one of the next 97 | // 2 lines try both options, one of the options should correct the inversion. 98 | 99 | // #define TFT_INVERSION_ON 100 | // #define TFT_INVERSION_OFF 101 | 102 | 103 | // ################################################################################## 104 | // 105 | // Section 2. Define the pins that are used to interface with the display here 106 | // 107 | // ################################################################################## 108 | 109 | // If a backlight control signal is available then define the TFT_BL pin in Section 2 110 | // below. The backlight will be turned ON when tft.begin() is called, but the library 111 | // needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be 112 | // driven with a PWM signal or turned OFF/ON then this must be handled by the user 113 | // sketch. e.g. with digitalWrite(TFT_BL, LOW); 114 | 115 | // #define TFT_BL 32 // LED back-light control pin 116 | // #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) 117 | 118 | 119 | 120 | // We must use hardware SPI, a minimum of 3 GPIO pins is needed. 121 | // Typical setup for ESP8266 NodeMCU ESP-12 is : 122 | // 123 | // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) 124 | // Display LED to NodeMCU pin VIN (or 5V, see below) 125 | // Display SCK to NodeMCU pin D5 126 | // Display SDI/MOSI to NodeMCU pin D7 127 | // Display DC (RS/AO)to NodeMCU pin D3 128 | // Display RESET to NodeMCU pin D4 (or RST, see below) 129 | // Display CS to NodeMCU pin D8 (or GND, see below) 130 | // Display GND to NodeMCU pin GND (0V) 131 | // Display VCC to NodeMCU 5V or 3.3V 132 | // 133 | // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin 134 | // 135 | // The DC (Data Command) pin may be labeled AO or RS (Register Select) 136 | // 137 | // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more 138 | // SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS 139 | // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin 140 | // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. 141 | // 142 | // The NodeMCU D0 pin can be used for RST 143 | // 144 | // 145 | // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin 146 | // If 5V is not available at a pin you can use 3.3V but backlight brightness 147 | // will be lower. 148 | 149 | 150 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### 151 | 152 | // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation 153 | //#define TFT_CS PIN_D8 // Chip select control pin D8 154 | //#define TFT_DC PIN_D3 // Data Command control pin 155 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 156 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 157 | 158 | //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) 159 | 160 | //#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen 161 | 162 | //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only 163 | 164 | 165 | // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### 166 | 167 | // Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact 168 | // but saves pins for other functions. It is best not to connect MISO as some displays 169 | // do not tristate that line wjen chip select is high! 170 | // On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode 171 | // On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK 172 | // In ESP8266 overlap mode the following must be defined 173 | 174 | //#define TFT_SPI_OVERLAP 175 | 176 | // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 177 | //#define TFT_CS PIN_D3 178 | //#define TFT_DC PIN_D5 // Data Command control pin 179 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 180 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 181 | 182 | 183 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### 184 | 185 | // For ESP32 Dev board (only tested with ILI9341 display) 186 | // The hardware SPI can be mapped to any pins 187 | 188 | #define TFT_MISO 12 189 | #define TFT_MOSI 13 190 | #define TFT_SCLK 14 191 | #define TFT_CS 15 // Chip select control pin 192 | #define TFT_DC 33 // Data Command control pin 193 | //#define TFT_RST 26 // Reset pin (could connect to RST pin) 194 | #define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST 195 | 196 | #define TOUCH_CS 35 // Chip select pin (T_CS) of touch screen 197 | 198 | //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only 199 | 200 | // For the M5Stack module use these #define lines 201 | //#define TFT_MISO 19 202 | //#define TFT_MOSI 23 203 | //#define TFT_SCLK 18 204 | //#define TFT_CS 14 // Chip select control pin 205 | //#define TFT_DC 27 // Data Command control pin 206 | //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) 207 | //#define TFT_BL 32 // LED back-light (required for M5Stack) 208 | 209 | // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### 210 | 211 | // The library supports 8 bit parallel TFTs with the ESP32, the pin 212 | // selection below is compatible with ESP32 boards in UNO format. 213 | // Wemos D32 boards need to be modified, see diagram in Tools folder. 214 | // Only ILI9481 and ILI9341 based displays have been tested! 215 | 216 | // Parallel bus is only supported for the STM32 and ESP32 217 | // Example below is for ESP32 Parallel interface with UNO displays 218 | 219 | // Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) 220 | //#define TFT_PARALLEL_8_BIT 221 | 222 | // The ESP32 and TFT the pins used for testing are: 223 | //#define TFT_CS 33 // Chip select control pin (library pulls permanently low 224 | //#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 225 | //#define TFT_RST 32 // Reset pin, toggles on startup 226 | 227 | //#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 228 | //#define TFT_RD 2 // Read strobe control pin 229 | 230 | //#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus 231 | //#define TFT_D1 13 // so a single register write sets/clears all bits. 232 | //#define TFT_D2 26 // Pins can be randomly assigned, this does not affect 233 | //#define TFT_D3 25 // TFT screen update performance. 234 | //#define TFT_D4 17 235 | //#define TFT_D5 16 236 | //#define TFT_D6 27 237 | //#define TFT_D7 14 238 | 239 | 240 | // ################################################################################## 241 | // 242 | // Section 3. Define the fonts that are to be used here 243 | // 244 | // ################################################################################## 245 | 246 | // Comment out the #defines below with // to stop that font being loaded 247 | // The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not 248 | // normally necessary. If all fonts are loaded the extra FLASH space required is 249 | // about 17Kbytes. To save FLASH space only enable the fonts you need! 250 | 251 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 252 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 253 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 254 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 255 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 256 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 257 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 258 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 259 | 260 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 261 | // this will save ~20kbytes of FLASH 262 | #define SMOOTH_FONT 263 | 264 | 265 | // ################################################################################## 266 | // 267 | // Section 4. Other options 268 | // 269 | // ################################################################################## 270 | 271 | // Define the SPI clock frequency, this affects the graphics rendering speed. Too 272 | // fast and the TFT driver will not keep up and display corruption appears. 273 | // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails 274 | // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) 275 | // With an ILI9163 display 27 MHz works OK. 276 | 277 | // #define SPI_FREQUENCY 1000000 278 | // #define SPI_FREQUENCY 5000000 279 | // #define SPI_FREQUENCY 10000000 280 | // #define SPI_FREQUENCY 20000000 281 | #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 282 | // #define SPI_FREQUENCY 40000000 283 | // #define SPI_FREQUENCY 80000000 284 | 285 | // Optional reduced SPI frequency for reading TFT 286 | #define SPI_READ_FREQUENCY 20000000 287 | 288 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 289 | #define SPI_TOUCH_FREQUENCY 2500000 290 | 291 | // The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. 292 | // If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) 293 | // then uncomment the following line: 294 | //#define USE_HSPI_PORT 295 | 296 | // Comment out the following #define if "SPI Transactions" do not need to be 297 | // supported. When commented out the code size will be smaller and sketches will 298 | // run slightly faster, so leave it commented out unless you need it! 299 | 300 | // Transaction support is needed to work with SD library but not needed with TFT_SdFat 301 | // Transaction support is required if other SPI devices are connected. 302 | 303 | // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) 304 | // so changing it here has no effect 305 | 306 | // #define SUPPORT_TRANSACTIONS 307 | -------------------------------------------------------------------------------- /user_setup.h Examples/README.md: -------------------------------------------------------------------------------- 1 | # TFT_eSPI configuration (User_Setup.h) 2 | 3 | Before compiling and uploading the FreeTouchDeck.ino sketch, you will have to edit the `User_Setup.h` file included with the TFT_eSPI library to match the setup you have. This can be found in your Arduino skechtbook "libraries" folder. If you have not renamed the TFT_eSPI library folder, the file `User_Setup.h` can be found in **/Documents/Arduino/libraries/TFT_eSPI-master/**. 4 | 5 | You can use the files in this repository as an example for different boards and screens. Simply rename the file that matches your combination of board and screen to `User_Setup.h` and replace the file that is in the library folder. You can also copy the contents of a file and replace the contenst in the existing `User_Setup.h`. Make sure to copy all the contents and don't leave any of the original content! 6 | 7 | If there is a wiring diagram available, it is a .png image with the same name. 8 | 9 | # Configuration in FreeTouchDeck.ino 10 | 11 | Some screens also need **FreeTouchDeck.ino** to be modified. By default it is setup for the ILI9488 with resistive touch and an ESP32. 12 | 13 | ## For the ESP32 TouchDown you will need to change the following things: 14 | 15 | - Uncomment: `//#define USECAPTOUCH` 16 | - Uncomment: `//#define speakerPin 26` 17 | 18 | # Help 19 | 20 | You can join my Discord server where I have a dedicated #freetouchdeck channel. https://discord.gg/RE3XevS 21 | --------------------------------------------------------------------------------