├── .gitignore ├── Triple_TFT_Displays.jpg ├── ESP32 - TFT Pin connections.xls ├── Triple_TFT ├── .vscode │ ├── arduino.json │ ├── settings.json │ └── c_cpp_properties.json ├── src │ ├── QueueItem.h │ ├── GlobalDefinitions.h │ ├── DisplayHandler │ │ ├── GUI │ │ │ ├── Pong_Functions.h │ │ │ ├── Matrix_Screen_Functions.h │ │ │ ├── HS_ScreenBase.cpp │ │ │ ├── HS_ScreenBase.h │ │ │ ├── SplashScreen_Functions.h │ │ │ ├── HS_Matrix_Screen.h │ │ │ ├── HS_SplashScreen.h │ │ │ ├── Pong.h │ │ │ ├── HS_Matrix_Screen.cpp │ │ │ ├── Pong.cpp │ │ │ └── HS_SplashScreen.cpp │ │ ├── DisplayHandler.h │ │ └── DisplayHandler.cpp │ └── QueueItem.cpp └── Triple_TFT.ino ├── README.md ├── Basic_Double_TFT └── Basic_Double_TFT.ino └── User_Setup.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /Triple_TFT_Displays.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitecraft/Multiple_TFT_Displays/HEAD/Triple_TFT_Displays.jpg -------------------------------------------------------------------------------- /ESP32 - TFT Pin connections.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitecraft/Multiple_TFT_Displays/HEAD/ESP32 - TFT Pin connections.xls -------------------------------------------------------------------------------- /Triple_TFT/.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "board": "esp32:esp32:esp32", 3 | "configuration": "PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none", 4 | "sketch": "Triple_TFT.ino", 5 | "output": ".vscode/build", 6 | "port": "COM6" 7 | } -------------------------------------------------------------------------------- /Triple_TFT/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "functional": "cpp", 4 | "optional": "cpp", 5 | "system_error": "cpp", 6 | "type_traits": "cpp", 7 | "xtr1common": "cpp", 8 | "ratio": "cpp", 9 | "array": "cpp", 10 | "tuple": "cpp", 11 | "utility": "cpp" 12 | } 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multiple_TFT_Displays 2 | Using multiple TFT displays with Bodmers TFT_ESPI library for the ESP32 3 | https://github.com/Bodmer/TFT_eSPI 4 | 5 | There are 2 examples here. 6 | One for 2 displays, and one for 3 displays. 7 | 8 | Youtube: 9 | https://youtu.be/WL8Xs5D2VUA 10 | 11 | 12 | 13 | NOTE: The Triple_TFT example requires the use of 'ArduinoQueue' library by Einar Arnason. 14 | It can be found in the Arduino library manager, or here: https://github.com/EinarArnason/ArduinoQueue -------------------------------------------------------------------------------- /Triple_TFT/src/QueueItem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "freertos/portmacro.h" 5 | 6 | 7 | struct DISPLAY_QUEUE_ITEM { 8 | char key; 9 | String value; 10 | int screen; 11 | }; 12 | 13 | class Queues 14 | { 15 | private: 16 | 17 | public: 18 | Queues(); 19 | ~Queues(); 20 | ArduinoQueue displayQueue; 21 | 22 | portMUX_TYPE displayQueueMux; 23 | 24 | void AddItemToDisplayQueue(char key, String value, int screenID); 25 | }; -------------------------------------------------------------------------------- /Triple_TFT/src/GlobalDefinitions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #define TFT_GREY 0x5AEB 5 | 6 | #define NUMBER_OF_TFT_DISPLAYS 3 7 | 8 | #define FIRST_SCREEN_CS 15 9 | #define SECOND_SCREEN_CS 21 10 | #define THIRD_SCREEN_CS 22 11 | 12 | #define FIRST_SCREEN 0 13 | #define SECOND_SCREEN 1 14 | #define THIRD_SCREEN 2 15 | 16 | enum DisplayCommands { 17 | ChangeScreen = 0, 18 | UpdateValue, 19 | }; 20 | 21 | enum ScreenTypes { 22 | SplashScreen = 0, 23 | Pong_ScreenType, 24 | Matrix_ScreenType, 25 | }; 26 | -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/Pong_Functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Pong.h" 3 | 4 | 5 | HS_PongScreen* pongScreen = NULL; 6 | 7 | void Create_PongScreen(Queues *newQueues, TFT_eSPI* TFT, int csPin) { 8 | pongScreen = new HS_PongScreen(newQueues, TFT, csPin); 9 | } 10 | 11 | void Destroy_PongScreen() 12 | { 13 | if (pongScreen != NULL) 14 | { 15 | delete(pongScreen); 16 | pongScreen = NULL; 17 | } 18 | } 19 | 20 | void Update_Pong_OnInterval() 21 | { 22 | pongScreen->UpdateScreenOnInterval(); 23 | } -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/Matrix_Screen_Functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "HS_Matrix_Screen.h" 3 | 4 | 5 | HS_Matrix_Screen* matrixScreen = NULL; 6 | 7 | void Create_MatrixScreen(Queues *newQueues, TFT_eSPI* TFT, int csPin) { 8 | matrixScreen = new HS_Matrix_Screen(newQueues, TFT, csPin); 9 | } 10 | 11 | void Destroy_MatrixScreen() 12 | { 13 | if (matrixScreen != NULL) 14 | { 15 | delete(matrixScreen); 16 | matrixScreen = NULL; 17 | } 18 | } 19 | 20 | void Update_MatrixStream_OnInterval() 21 | { 22 | matrixScreen->UpdateScreenOnInterval(); 23 | } -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/HS_ScreenBase.cpp: -------------------------------------------------------------------------------- 1 | #include "HS_ScreenBase.h" 2 | 3 | HS_ScreenBase::HS_ScreenBase(Queues *newQueues, TFT_eSPI *newTFT, int chipSelectPin) 4 | { 5 | TFT = newTFT; 6 | allQueues = newQueues; 7 | cs_pin = chipSelectPin; 8 | 9 | } 10 | 11 | HS_ScreenBase::~HS_ScreenBase() 12 | { 13 | TFT = NULL; 14 | } 15 | 16 | void HS_ScreenBase::PrepDisplayForUpdate() 17 | { 18 | digitalWrite(cs_pin,LOW); 19 | TFT->setRotation(screenRotation); 20 | } 21 | 22 | void HS_ScreenBase::CompleteDisplayUpdate() 23 | { 24 | digitalWrite(cs_pin,HIGH); 25 | } 26 | -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/HS_ScreenBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "FS.h" 3 | #include "SPIFFS.h" 4 | #include 5 | #include 6 | #include "../../GlobalDefinitions.h" 7 | #include "../../QueueItem.h" 8 | 9 | 10 | class HS_ScreenBase 11 | { 12 | private: 13 | int cs_pin; 14 | 15 | public: 16 | HS_ScreenBase(Queues *newQueues, TFT_eSPI *newTFT, int chipSelectPin); 17 | ~HS_ScreenBase(); 18 | void PrepDisplayForUpdate(); 19 | void CompleteDisplayUpdate(); 20 | int screenRotation = 0; 21 | 22 | TFT_eSPI *TFT; 23 | Queues *allQueues; 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /Triple_TFT/src/QueueItem.cpp: -------------------------------------------------------------------------------- 1 | #include "QueueItem.h" 2 | 3 | 4 | Queues::Queues() 5 | { 6 | ArduinoQueue displayQueue(20); 7 | 8 | displayQueueMux = portMUX_INITIALIZER_UNLOCKED; 9 | } 10 | 11 | Queues::~Queues() 12 | { 13 | 14 | } 15 | 16 | void Queues::AddItemToDisplayQueue(char key, String value, int screenID) 17 | { 18 | DISPLAY_QUEUE_ITEM qi; 19 | qi.key = key; 20 | qi.value = value; 21 | qi.screen = screenID; 22 | 23 | portENTER_CRITICAL(&displayQueueMux); 24 | displayQueue.enqueue(qi); 25 | portEXIT_CRITICAL(&displayQueueMux); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/SplashScreen_Functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "HS_SplashScreen.h" 3 | 4 | 5 | HS_SplashScreen* HS_Current_SplashScreen = NULL; 6 | 7 | void Create_SplashScreen(Queues *newQueues, TFT_eSPI* TFT, int csPin) { 8 | HS_Current_SplashScreen = new HS_SplashScreen(newQueues, TFT, csPin); 9 | } 10 | 11 | void Destroy_SplashScreen() 12 | { 13 | if (HS_Current_SplashScreen != NULL) 14 | { 15 | delete(HS_Current_SplashScreen); 16 | HS_Current_SplashScreen = NULL; 17 | } 18 | } 19 | 20 | void Update_SplashScreen_OnInterval() 21 | { 22 | HS_Current_SplashScreen->UpdateScreenOnInterval(); 23 | } -------------------------------------------------------------------------------- /Triple_TFT/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "ESP32", 5 | "includePath": [ 6 | "${workspaceFolder}\\src\\**", 7 | "C:\\Workspace\\Arduino\\libraries\\**", 8 | "C:\\Users\\Terry\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\**", 9 | "C:\\Users\\Terry\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.4\\**" 10 | ], 11 | "forcedInclude": [], 12 | "intelliSenseMode": "msvc-x64", 13 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe", 14 | "cStandard": "c11", 15 | "cppStandard": "c++17" 16 | } 17 | ], 18 | "version": 4 19 | } -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/DisplayHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | #include 4 | #include 5 | #include 6 | #include "../GlobalDefinitions.h" 7 | #include "../QueueItem.h" 8 | 9 | 10 | struct ScreenDefinition 11 | { 12 | int cs_pin; 13 | int screenRotation; 14 | void(*UpdateCurentScreen)(String value) = NULL; 15 | void(*UpdateCurentScreenOnInterval)() = NULL; 16 | void(*DestroyCurrentScreen)() = NULL; 17 | }; 18 | 19 | class DisplayHandler 20 | { 21 | private: 22 | TFT_eSPI tftDisplay; 23 | ScreenDefinition screenPositions[NUMBER_OF_TFT_DISPLAYS]; 24 | Queues *allQueues; 25 | 26 | void DispatchCommand(); 27 | void LoadNewScreen(ScreenTypes newScreenType, int screenLocation); 28 | 29 | public: 30 | DisplayHandler(); 31 | ~DisplayHandler(); 32 | void Init(Queues *newQueues); 33 | void Run(); 34 | 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/HS_Matrix_Screen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | Based on, and mostly copied from, the tft_espi example called: 5 | TFT_Matrix 6 | */ 7 | 8 | #include "HS_ScreenBase.h" 9 | 10 | 11 | #define TEXT_HEIGHT 8 // Height of text to be printed and scrolled 12 | #define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen) 13 | #define TOP_FIXED_AREA 0 // Number of lines in top fixed area (lines counted from top of screen) 14 | 15 | class HS_Matrix_Screen : 16 | public HS_ScreenBase 17 | { 18 | private: 19 | unsigned long lastUpdate = 0; 20 | int updateScreenInterval = 5; 21 | 22 | uint16_t yStart = TOP_FIXED_AREA; 23 | uint16_t yArea = 320 - TOP_FIXED_AREA - BOT_FIXED_AREA; 24 | uint16_t yDraw = 320 - BOT_FIXED_AREA - TEXT_HEIGHT; 25 | byte pos[42]; 26 | uint16_t xPos = 0; 27 | 28 | int j = 0; 29 | int i = 0; 30 | 31 | bool buildingTheDisplay = true; 32 | 33 | void SetupScrollArea(uint16_t TFA, uint16_t BFA); 34 | void ScrollAddress(uint16_t VSP); 35 | int Scroll_Slow(int lines, int wait); 36 | 37 | public: 38 | HS_Matrix_Screen(Queues *newQueues, TFT_eSPI* newTFT, int chipSelectPin); 39 | ~HS_Matrix_Screen(); 40 | void UpdateScreenOnInterval(); 41 | }; -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/HS_SplashScreen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | Based on, and mostly copied from, the tft_espi example called: 4 | TFT_Meter_linear 5 | */ 6 | 7 | #include "HS_ScreenBase.h" 8 | 9 | #define M_SIZE 1.3333 10 | 11 | #define TEXT_COLOR 0xCE59 12 | #define PANEL_HCOLOR 0x5AEB 13 | #define PANEL_BGCOLOR 0x738E 14 | #define BOX_BORDER_COLOR 0x9CD3 15 | #define BOX_DROP_SHADOW 0x4208 16 | 17 | #define NET_PANEL_X 0 18 | #define NET_PANEL_Y 40 19 | 20 | #define SERVER_PANEL_X 0 21 | #define SERVER_PANEL_Y 85 22 | 23 | class HS_SplashScreen : 24 | public HS_ScreenBase 25 | { 26 | private: 27 | 28 | unsigned long lastUpdate = 0; 29 | int updateScreenInterval = 30; 30 | 31 | float ltx = 0; // Saved x coord of bottom of needle 32 | uint16_t osx = M_SIZE*120, osy = M_SIZE*120; // Saved x & y coords 33 | uint32_t updateTime = 0; // time for next update 34 | 35 | int old_analog = -999; // Value last displayed 36 | 37 | int value[6] = {0, 0, 0, 0, 0, 0}; 38 | int old_value[6] = { -1, -1, -1, -1, -1, -1}; 39 | int d = 0; 40 | 41 | void AnalogMeter(); 42 | void PlotNeedle(int value, byte ms_delay); 43 | 44 | 45 | public: 46 | HS_SplashScreen(Queues *newQueues, TFT_eSPI* newTFT, int chipSelectPin); 47 | ~HS_SplashScreen(); 48 | void UpdateScreenOnInterval(); 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /Basic_Double_TFT/Basic_Double_TFT.ino: -------------------------------------------------------------------------------- 1 | /* 2 | In the TFT_ESPI user_setup.h file 3 | be sure to comment out the line for TFT_CS 4 | //#define TFT_CS 21 // Chip select control pin 5 | */ 6 | 7 | #include 8 | #include // Hardware-specific library 9 | 10 | #define firstScreenCS 21 11 | #define secondScreenCS 22 12 | TFT_eSPI tft = TFT_eSPI(); // Invoke custom library 13 | 14 | 15 | void setup() { 16 | pinMode(firstScreenCS, OUTPUT); 17 | digitalWrite(firstScreenCS, HIGH); 18 | 19 | pinMode(secondScreenCS, OUTPUT); 20 | digitalWrite(secondScreenCS, HIGH); 21 | 22 | 23 | // We need to 'init' both displays 24 | // at the same time. so set both cs pins low 25 | digitalWrite(firstScreenCS, LOW); 26 | digitalWrite(secondScreenCS, LOW); 27 | 28 | tft.init(); 29 | tft.setTextSize(2); 30 | 31 | // Set both cs pins HIGH, or 'inactive' 32 | digitalWrite(firstScreenCS, HIGH); 33 | digitalWrite(secondScreenCS, HIGH); 34 | 35 | 36 | 37 | /* 38 | Update the first screen 39 | all 'tft.' call must be done 40 | after setting the cs pin low 41 | */ 42 | digitalWrite(firstScreenCS, LOW); 43 | tft.setRotation(1); 44 | tft.fillScreen(TFT_RED); 45 | tft.setCursor(0,10); 46 | tft.print("Hello World.\nI'm display #1"); 47 | // set the cs pin back to HIGH when finished 48 | // with this particular display 49 | digitalWrite(firstScreenCS, HIGH); 50 | 51 | 52 | // Update the second screen 53 | digitalWrite(secondScreenCS, LOW); 54 | tft.setRotation(2); 55 | tft.fillScreen(TFT_GREEN); 56 | tft.setCursor(0,10); 57 | tft.print("Hello World.\nI'm display #2"); 58 | digitalWrite(secondScreenCS, HIGH); 59 | 60 | 61 | } 62 | 63 | void loop() { 64 | // put your main code here, to run repeatedly: 65 | delay(100); 66 | } 67 | -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/Pong.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | Based on, and mostly copied from, the tft_espi example called: 5 | TFT_Pong 6 | */ 7 | #include "HS_ScreenBase.h" 8 | 9 | #define BLACK 0x0000 10 | #define WHITE 0xFFFF 11 | #define GREY 0x5AEB 12 | 13 | class HS_PongScreen : 14 | public HS_ScreenBase 15 | { 16 | private: 17 | //HS_Theme pongTheme; 18 | 19 | unsigned long lastUpdate = 0; 20 | int updateScreenInterval = 0; 21 | 22 | 23 | int16_t h = 240; 24 | int16_t w = 320; 25 | 26 | int dly = 5; 27 | 28 | int16_t paddle_h = 30; 29 | int16_t paddle_w = 4; 30 | 31 | int16_t lpaddle_x = 0; 32 | int16_t rpaddle_x = w - paddle_w; 33 | 34 | int16_t lpaddle_y = 0; 35 | int16_t rpaddle_y = h - paddle_h; 36 | 37 | int16_t lpaddle_d = 1; 38 | int16_t rpaddle_d = -1; 39 | 40 | int16_t lpaddle_ball_t = w - w / 4; 41 | int16_t rpaddle_ball_t = w / 4; 42 | 43 | int16_t target_y = 0; 44 | 45 | int16_t ball_x = 2; 46 | int16_t ball_y = 2; 47 | int16_t oldball_x = 2; 48 | int16_t oldball_y = 2; 49 | 50 | int16_t ball_dx = 1; 51 | int16_t ball_dy = 1; 52 | 53 | int16_t ball_w = 6; 54 | int16_t ball_h = 6; 55 | 56 | int16_t dashline_h = 4; 57 | int16_t dashline_w = 2; 58 | int16_t dashline_n = h / dashline_h; 59 | int16_t dashline_x = w / 2 - 1; 60 | int16_t dashline_y = dashline_h / 2; 61 | 62 | int16_t lscore = 12; 63 | int16_t rscore = 4; 64 | 65 | void Initgame(); 66 | void midline(); 67 | void lpaddle(); 68 | void rpaddle(); 69 | void calc_target_y(); 70 | void ball(); 71 | 72 | public: 73 | HS_PongScreen(Queues *newQueues, TFT_eSPI* newTFT, int chipSelectPin); 74 | ~HS_PongScreen(); 75 | //void UpdateScreen(String value); 76 | void UpdateScreenOnInterval(); 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /Triple_TFT/Triple_TFT.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This is an example of using 3 ILI9341 based 3 | TFT displays with Bodmers TFT_ESPI library 4 | https://github.com/Bodmer/TFT_eSPI 5 | 6 | 7 | The screens displayed in this example are pulled from examples 8 | that come with the TFt_ESPI library. I've just adpated them 9 | to my DisplayManager as an example. 10 | 11 | The ChipSelect pin for each display in this example 12 | is defined in the GlobalDefinitions.h file. 13 | #define LEFT_SCREEN_CS 15 14 | #define CENTER_SCREEN_CS 21 15 | #define RIGHT_SCREEN_CS 22 16 | 17 | In the TFT_ESPI user_setup.h file 18 | be sure to comment out the line for TFT_CS 19 | //#define TFT_CS 21 // Chip select control pin 20 | 21 | You can still use this sketch with only two displays. 22 | In the GlobalDefinitions.h file, change NUMBER_OF_TFT_DISPLAYS to 2 23 | #define NUMBER_OF_TFT_DISPLAYS 2 24 | 25 | You won't be able to use third_screen items of course. 26 | */ 27 | 28 | 29 | #include "src/DisplayHandler/DisplayHandler.h" 30 | #include "src/QueueItem.h" 31 | 32 | 33 | TaskHandle_t Display_Core_Task_Handle; 34 | 35 | DisplayHandler displayHandler; 36 | Queues allQueues; 37 | 38 | 39 | void setup() { 40 | displayHandler.Init(&allQueues); 41 | 42 | xTaskCreatePinnedToCore( 43 | TFT_Core_Proc, /* pvTaskCode */ 44 | "DisplayHandler", /* pcName */ 45 | 3000, /* usStackDepth */ 46 | NULL, /* pvParameters */ 47 | 1, /* uxPriority */ 48 | &Display_Core_Task_Handle, /* pxCreatedTask */ 49 | 0); 50 | 51 | allQueues.AddItemToDisplayQueue(DisplayCommands::ChangeScreen, String(ScreenTypes::Matrix_ScreenType), FIRST_SCREEN); 52 | allQueues.AddItemToDisplayQueue(DisplayCommands::ChangeScreen, String(ScreenTypes::Pong_ScreenType), SECOND_SCREEN); 53 | allQueues.AddItemToDisplayQueue(DisplayCommands::ChangeScreen, String(ScreenTypes::SplashScreen), THIRD_SCREEN); 54 | } 55 | 56 | 57 | void loop() 58 | { 59 | delay(20); 60 | } 61 | 62 | void TFT_Core_Proc(void* parameter) 63 | { 64 | displayHandler.Run(); 65 | } -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/HS_Matrix_Screen.cpp: -------------------------------------------------------------------------------- 1 | #include "HS_Matrix_Screen.h" 2 | 3 | HS_Matrix_Screen::HS_Matrix_Screen(Queues *newQueues, TFT_eSPI* newTFT, int chipSelectPin) : HS_ScreenBase(newQueues, newTFT, chipSelectPin) 4 | { 5 | screenRotation = 0; 6 | 7 | randomSeed(analogRead(A0)); 8 | PrepDisplayForUpdate(); 9 | 10 | SetupScrollArea(TOP_FIXED_AREA, BOT_FIXED_AREA); 11 | 12 | CompleteDisplayUpdate(); 13 | } 14 | 15 | HS_Matrix_Screen::~HS_Matrix_Screen() 16 | { 17 | 18 | } 19 | 20 | 21 | void HS_Matrix_Screen::UpdateScreenOnInterval() 22 | { 23 | if (millis() - lastUpdate > updateScreenInterval) 24 | { 25 | PrepDisplayForUpdate(); 26 | if(buildingTheDisplay) 27 | { 28 | if (pos[i] > 20) pos[i] -= 4; // Rapid fade initially brightness values 29 | if (pos[i] > 0) pos[i] -= 1; // Slow fade later 30 | if ((random(20) == 1) && (j<400)) pos[i] = 63; // ~1 in 20 probability of a new character 31 | TFT->setTextColor(pos[i] << 5, ILI9341_BLACK); // Set the green character brightness 32 | if (pos[i] == 63) TFT->setTextColor(ILI9341_WHITE, ILI9341_BLACK); // Draw white character 33 | xPos += TFT->drawChar(random(32, 128), xPos, yDraw, 1); // Draw the character 34 | 35 | i++; 36 | if(i >= 40) 37 | { 38 | i=0; 39 | j += TEXT_HEIGHT; 40 | yDraw = Scroll_Slow(TEXT_HEIGHT, 14); // Scroll, 14ms per pixel line 41 | xPos = 0; 42 | } 43 | if(j >= 330) 44 | { 45 | buildingTheDisplay = false; 46 | } 47 | } else 48 | { 49 | if (millis() - lastUpdate > updateScreenInterval) 50 | { 51 | yDraw = Scroll_Slow(1,0); 52 | lastUpdate = millis(); 53 | } 54 | } 55 | CompleteDisplayUpdate(); 56 | } 57 | } 58 | 59 | 60 | void HS_Matrix_Screen::SetupScrollArea(uint16_t TFA, uint16_t BFA) 61 | { 62 | TFT->writecommand(ILI9341_VSCRDEF); // Vertical scroll definition 63 | TFT->writedata(TFA >> 8); 64 | TFT->writedata(TFA); 65 | TFT->writedata((320 - TFA - BFA) >> 8); 66 | TFT->writedata(320 - TFA - BFA); 67 | TFT->writedata(BFA >> 8); 68 | TFT->writedata(BFA); 69 | } 70 | 71 | int HS_Matrix_Screen::Scroll_Slow(int lines, int wait) 72 | { 73 | int yTemp = yStart; 74 | for (int i = 0; i < lines; i++) { 75 | yStart++; 76 | if (yStart == 320 - BOT_FIXED_AREA) yStart = TOP_FIXED_AREA; 77 | ScrollAddress(yStart); 78 | delay(wait); 79 | } 80 | return yTemp; 81 | } 82 | 83 | void HS_Matrix_Screen::ScrollAddress(uint16_t VSP) 84 | { 85 | TFT->writecommand(ILI9341_VSCRSADD); // Vertical scrolling start address 86 | TFT->writedata(VSP >> 8); 87 | TFT->writedata(VSP); 88 | } -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/DisplayHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "DisplayHandler.h" 2 | #include "FS.h" 3 | #include "SPIFFS.h" 4 | 5 | #include "GUI//SplashScreen_Functions.h" 6 | #include "GUI//Pong_Functions.h" 7 | #include "GUI//Matrix_Screen_Functions.h" 8 | 9 | DisplayHandler::DisplayHandler() 10 | { 11 | screenPositions[FIRST_SCREEN].cs_pin = FIRST_SCREEN_CS; 12 | screenPositions[SECOND_SCREEN].cs_pin = SECOND_SCREEN_CS; 13 | screenPositions[THIRD_SCREEN].cs_pin = THIRD_SCREEN_CS; 14 | } 15 | 16 | DisplayHandler::~DisplayHandler() 17 | { 18 | 19 | } 20 | 21 | void DisplayHandler::Init(Queues *newQueues) 22 | { 23 | allQueues = newQueues; 24 | int displayLoopCounter; 25 | 26 | // Setup the Chip Select pins for the displays 27 | for(int displayLoopCounter = 0; displayLoopCounter < NUMBER_OF_TFT_DISPLAYS; displayLoopCounter++) 28 | { 29 | pinMode(screenPositions[displayLoopCounter].cs_pin, OUTPUT); 30 | digitalWrite(screenPositions[displayLoopCounter].cs_pin, LOW); 31 | } 32 | 33 | tftDisplay.init(); 34 | tftDisplay.setRotation(0); 35 | tftDisplay.fillScreen(TFT_BLACK); 36 | 37 | for(int displayLoopCounter = 0; displayLoopCounter < NUMBER_OF_TFT_DISPLAYS; displayLoopCounter++) 38 | { 39 | digitalWrite(screenPositions[displayLoopCounter].cs_pin, HIGH); 40 | } 41 | 42 | } 43 | 44 | void DisplayHandler::Run() 45 | { 46 | int counter = 0; 47 | while (true) 48 | { 49 | DispatchCommand(); 50 | for(counter = 0; counter < NUMBER_OF_TFT_DISPLAYS; counter++) 51 | { 52 | if (screenPositions[counter].UpdateCurentScreenOnInterval != NULL) 53 | { 54 | screenPositions[counter].UpdateCurentScreenOnInterval(); 55 | } 56 | } 57 | vTaskDelay(pdMS_TO_TICKS(1)); 58 | } 59 | } 60 | 61 | void DisplayHandler::LoadNewScreen(ScreenTypes newScreenType, int screenLocation) 62 | { 63 | if(screenPositions[screenLocation].DestroyCurrentScreen != NULL){ 64 | screenPositions[screenLocation].DestroyCurrentScreen(); 65 | screenPositions[screenLocation].DestroyCurrentScreen = NULL; 66 | } 67 | 68 | screenPositions[screenLocation].UpdateCurentScreen = NULL; 69 | screenPositions[screenLocation].UpdateCurentScreenOnInterval = NULL; 70 | 71 | switch (newScreenType) { 72 | case ScreenTypes::SplashScreen: 73 | screenPositions[screenLocation].DestroyCurrentScreen = Destroy_SplashScreen; 74 | screenPositions[screenLocation].UpdateCurentScreenOnInterval = Update_SplashScreen_OnInterval; 75 | Create_SplashScreen(allQueues, &tftDisplay, screenPositions[screenLocation].cs_pin); 76 | break; 77 | case ScreenTypes::Pong_ScreenType: 78 | screenPositions[screenLocation].DestroyCurrentScreen = Destroy_PongScreen; 79 | screenPositions[screenLocation].UpdateCurentScreenOnInterval = Update_Pong_OnInterval; 80 | Create_PongScreen(allQueues, &tftDisplay, screenPositions[screenLocation].cs_pin); 81 | break; 82 | case ScreenTypes::Matrix_ScreenType: 83 | screenPositions[screenLocation].DestroyCurrentScreen = Destroy_MatrixScreen; 84 | screenPositions[screenLocation].UpdateCurentScreenOnInterval = Update_MatrixStream_OnInterval; 85 | Create_MatrixScreen(allQueues, &tftDisplay, screenPositions[screenLocation].cs_pin); 86 | break; 87 | default: 88 | break; 89 | } 90 | } 91 | 92 | void DisplayHandler::DispatchCommand() 93 | { 94 | while (!allQueues->displayQueue.isEmpty()) 95 | { 96 | portENTER_CRITICAL(&allQueues->displayQueueMux); 97 | DISPLAY_QUEUE_ITEM currItem = allQueues->displayQueue.dequeue(); 98 | portEXIT_CRITICAL(&allQueues->displayQueueMux); 99 | 100 | switch (currItem.key) { 101 | case DisplayCommands::ChangeScreen: 102 | if (currItem.value.length() == 1) 103 | { 104 | LoadNewScreen(static_cast(currItem.value.toInt()),currItem.screen); 105 | } 106 | break; 107 | case DisplayCommands::UpdateValue: 108 | if (screenPositions[currItem.screen].UpdateCurentScreen != NULL && currItem.value.length() >= 3) 109 | { 110 | screenPositions[currItem.screen].UpdateCurentScreen(String(currItem.value)); 111 | } 112 | break; 113 | default: 114 | break; 115 | } 116 | yield(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/Pong.cpp: -------------------------------------------------------------------------------- 1 | #include "Pong.h" 2 | 3 | HS_PongScreen::HS_PongScreen(Queues *newQueues, TFT_eSPI* newTFT, int chipSelectPin) : HS_ScreenBase(newQueues, newTFT, chipSelectPin) 4 | { 5 | screenRotation = 3; 6 | PrepDisplayForUpdate(); 7 | TFT->fillScreen(TFT_BLACK); 8 | 9 | Initgame(); 10 | TFT->setTextColor(WHITE, BLACK); 11 | 12 | CompleteDisplayUpdate(); 13 | } 14 | 15 | HS_PongScreen::~HS_PongScreen() 16 | { 17 | 18 | } 19 | 20 | 21 | void HS_PongScreen::UpdateScreenOnInterval() 22 | { 23 | if (millis() - lastUpdate > updateScreenInterval) 24 | { 25 | PrepDisplayForUpdate(); 26 | 27 | lpaddle(); 28 | rpaddle(); 29 | midline(); 30 | ball(); 31 | 32 | CompleteDisplayUpdate(); 33 | lastUpdate = millis(); 34 | } 35 | } 36 | 37 | void HS_PongScreen::Initgame() 38 | { 39 | lpaddle_y = random(0, h - paddle_h); 40 | rpaddle_y = random(0, h - paddle_h); 41 | 42 | // ball is placed on the center of the left paddle 43 | ball_y = lpaddle_y + (paddle_h / 2); 44 | 45 | calc_target_y(); 46 | 47 | midline(); 48 | 49 | TFT->fillRect(0,h-26,w,239,GREY); 50 | 51 | TFT->setTextDatum(TC_DATUM); 52 | TFT->setTextColor(WHITE,GREY); 53 | TFT->drawString("TFT_eSPI example", w/2, h-26 , 4); 54 | } 55 | 56 | void HS_PongScreen::midline() { 57 | 58 | // If the ball is not on the line then don't redraw the line 59 | if ((ball_x dashline_x+dashline_w)) return; 60 | 61 | TFT->startWrite(); 62 | 63 | // Quick way to draw a dashed line 64 | TFT->setAddrWindow(dashline_x, 0, dashline_w, h); 65 | 66 | for(int16_t i = 0; i < dashline_n; i+=2) { 67 | TFT->pushColor(WHITE, dashline_w*dashline_h); // push dash pixels 68 | TFT->pushColor(BLACK, dashline_w*dashline_h); // push gap pixels 69 | } 70 | 71 | TFT->endWrite(); 72 | } 73 | 74 | void HS_PongScreen::lpaddle() { 75 | 76 | if (lpaddle_d == 1) { 77 | TFT->fillRect(lpaddle_x, lpaddle_y, paddle_w, 1, BLACK); 78 | } 79 | else if (lpaddle_d == -1) { 80 | TFT->fillRect(lpaddle_x, lpaddle_y + paddle_h - 1, paddle_w, 1, BLACK); 81 | } 82 | 83 | lpaddle_y = lpaddle_y + lpaddle_d; 84 | 85 | if (ball_dx == 1) lpaddle_d = 0; 86 | else { 87 | if (lpaddle_y + paddle_h / 2 == target_y) lpaddle_d = 0; 88 | else if (lpaddle_y + paddle_h / 2 > target_y) lpaddle_d = -1; 89 | else lpaddle_d = 1; 90 | } 91 | 92 | if (lpaddle_y + paddle_h >= h && lpaddle_d == 1) lpaddle_d = 0; 93 | else if (lpaddle_y <= 0 && lpaddle_d == -1) lpaddle_d = 0; 94 | 95 | TFT->fillRect(lpaddle_x, lpaddle_y, paddle_w, paddle_h, WHITE); 96 | } 97 | 98 | void HS_PongScreen::rpaddle() { 99 | 100 | if (rpaddle_d == 1) { 101 | TFT->fillRect(rpaddle_x, rpaddle_y, paddle_w, 1, BLACK); 102 | } 103 | else if (rpaddle_d == -1) { 104 | TFT->fillRect(rpaddle_x, rpaddle_y + paddle_h - 1, paddle_w, 1, BLACK); 105 | } 106 | 107 | rpaddle_y = rpaddle_y + rpaddle_d; 108 | 109 | if (ball_dx == -1) rpaddle_d = 0; 110 | else { 111 | if (rpaddle_y + paddle_h / 2 == target_y) rpaddle_d = 0; 112 | else if (rpaddle_y + paddle_h / 2 > target_y) rpaddle_d = -1; 113 | else rpaddle_d = 1; 114 | } 115 | 116 | if (rpaddle_y + paddle_h >= h && rpaddle_d == 1) rpaddle_d = 0; 117 | else if (rpaddle_y <= 0 && rpaddle_d == -1) rpaddle_d = 0; 118 | 119 | TFT->fillRect(rpaddle_x, rpaddle_y, paddle_w, paddle_h, WHITE); 120 | } 121 | 122 | void HS_PongScreen::calc_target_y() { 123 | int16_t target_x; 124 | int16_t reflections; 125 | int16_t y; 126 | 127 | if (ball_dx == 1) { 128 | target_x = w - ball_w; 129 | } 130 | else { 131 | target_x = -1 * (w - ball_w); 132 | } 133 | 134 | y = abs(target_x * (ball_dy / ball_dx) + ball_y); 135 | 136 | reflections = floor(y / h); 137 | 138 | if (reflections % 2 == 0) { 139 | target_y = y % h; 140 | } 141 | else { 142 | target_y = h - (y % h); 143 | } 144 | } 145 | 146 | void HS_PongScreen::ball() { 147 | ball_x = ball_x + ball_dx; 148 | ball_y = ball_y + ball_dy; 149 | 150 | if (ball_dx == -1 && ball_x == paddle_w && ball_y + ball_h >= lpaddle_y && ball_y <= lpaddle_y + paddle_h) { 151 | ball_dx = ball_dx * -1; 152 | dly = random(5); // change speed of ball after paddle contact 153 | calc_target_y(); 154 | } else if (ball_dx == 1 && ball_x + ball_w == w - paddle_w && ball_y + ball_h >= rpaddle_y && ball_y <= rpaddle_y + paddle_h) { 155 | ball_dx = ball_dx * -1; 156 | dly = random(5); // change speed of ball after paddle contact 157 | calc_target_y(); 158 | } else if ((ball_dx == 1 && ball_x >= w) || (ball_dx == -1 && ball_x + ball_w < 0)) { 159 | dly = 5; 160 | } 161 | 162 | if (ball_y > h - ball_w || ball_y < 0) { 163 | ball_dy = ball_dy * -1; 164 | ball_y += ball_dy; // Keep in bounds 165 | } 166 | 167 | //TFT->fillRect(oldball_x, oldball_y, ball_w, ball_h, BLACK); 168 | TFT->drawRect(oldball_x, oldball_y, ball_w, ball_h, BLACK); // Less TFT refresh aliasing than line above for large balls 169 | TFT->fillRect( ball_x, ball_y, ball_w, ball_h, WHITE); 170 | oldball_x = ball_x; 171 | oldball_y = ball_y; 172 | } -------------------------------------------------------------------------------- /Triple_TFT/src/DisplayHandler/GUI/HS_SplashScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "HS_SplashScreen.h" 2 | 3 | HS_SplashScreen::HS_SplashScreen(Queues *newQueues, TFT_eSPI* newTFT, int chipSelectPin) : HS_ScreenBase(newQueues, newTFT, chipSelectPin) 4 | { 5 | screenRotation = 1; 6 | 7 | PrepDisplayForUpdate(); 8 | AnalogMeter(); 9 | CompleteDisplayUpdate(); 10 | 11 | updateTime = millis(); // Next update time 12 | } 13 | 14 | HS_SplashScreen::~HS_SplashScreen() 15 | { 16 | 17 | } 18 | void HS_SplashScreen::UpdateScreenOnInterval() 19 | { 20 | if (millis() - lastUpdate > updateScreenInterval) 21 | { 22 | PrepDisplayForUpdate(); 23 | 24 | // Create a Sine wave for testing 25 | d += 4; if (d >= 360) d = 0; 26 | value[0] = 50 + 50 * sin((d + 0) * 0.0174532925); 27 | 28 | PlotNeedle(value[0], 0); // It takes between 2 and 12ms to replot the needle with zero delay 29 | 30 | CompleteDisplayUpdate(); 31 | 32 | lastUpdate = millis(); 33 | } 34 | } 35 | 36 | void HS_SplashScreen::AnalogMeter() 37 | { 38 | // Meter outline 39 | TFT->fillRect(0, 0, M_SIZE*239, M_SIZE*126, TFT_GREY); 40 | 41 | //TFT->fillRect(5, 3, M_SIZE*230, M_SIZE*119, TFT_BLUE); 42 | TFT->fillRect(5, 3, M_SIZE*230, M_SIZE*119, TFT_WHITE); 43 | 44 | TFT->setTextColor(TFT_BLACK); // Text colour 45 | 46 | // Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing) 47 | for (int i = -50; i < 51; i += 5) 48 | { 49 | // Long scale tick length 50 | int tl = 15; 51 | 52 | // Coodinates of tick to draw 53 | float sx = cos((i - 90) * 0.0174532925); 54 | float sy = sin((i - 90) * 0.0174532925); 55 | uint16_t x0 = sx * (M_SIZE*100 + tl) + M_SIZE*120; 56 | uint16_t y0 = sy * (M_SIZE*100 + tl) + M_SIZE*140; 57 | uint16_t x1 = sx * M_SIZE*100 + M_SIZE*120; 58 | uint16_t y1 = sy * M_SIZE*100 + M_SIZE*140; 59 | 60 | // Coordinates of next tick for zone fill 61 | float sx2 = cos((i + 5 - 90) * 0.0174532925); 62 | float sy2 = sin((i + 5 - 90) * 0.0174532925); 63 | int x2 = sx2 * (M_SIZE*100 + tl) + M_SIZE*120; 64 | int y2 = sy2 * (M_SIZE*100 + tl) + M_SIZE*140; 65 | int x3 = sx2 * M_SIZE*100 + M_SIZE*120; 66 | int y3 = sy2 * M_SIZE*100 + M_SIZE*140; 67 | 68 | // Yellow zone limits 69 | //if (i >= -50 && i < 0) { 70 | // TFT->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_YELLOW); 71 | // TFT->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_YELLOW); 72 | //} 73 | 74 | // Green zone limits 75 | if (i >= 0 && i < 25) { 76 | TFT->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN); 77 | TFT->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN); 78 | } 79 | 80 | // Orange zone limits 81 | if (i >= 25 && i < 50) { 82 | TFT->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_ORANGE); 83 | TFT->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_ORANGE); 84 | } 85 | 86 | // Short scale tick length 87 | if (i % 25 != 0) tl = 8; 88 | 89 | // Recalculate coords incase tick lenght changed 90 | x0 = sx * (M_SIZE*100 + tl) + M_SIZE*120; 91 | y0 = sy * (M_SIZE*100 + tl) + M_SIZE*140; 92 | x1 = sx * M_SIZE*100 + M_SIZE*120; 93 | y1 = sy * M_SIZE*100 + M_SIZE*140; 94 | 95 | // Draw tick 96 | TFT->drawLine(x0, y0, x1, y1, TFT_BLACK); 97 | 98 | // Check if labels should be drawn, with position tweaks 99 | if (i % 25 == 0) { 100 | // Calculate label positions 101 | x0 = sx * (M_SIZE*100 + tl + 10) + M_SIZE*120; 102 | y0 = sy * (M_SIZE*100 + tl + 10) + M_SIZE*140; 103 | switch (i / 25) { 104 | case -2: TFT->drawCentreString("0", x0, y0 - 12, 2); break; 105 | case -1: TFT->drawCentreString("25", x0, y0 - 9, 2); break; 106 | case 0: TFT->drawCentreString("50", x0, y0 - 7, 2); break; 107 | case 1: TFT->drawCentreString("75", x0, y0 - 9, 2); break; 108 | case 2: TFT->drawCentreString("100", x0, y0 - 12, 2); break; 109 | } 110 | } 111 | 112 | // Now draw the arc of the scale 113 | sx = cos((i + 5 - 90) * 0.0174532925); 114 | sy = sin((i + 5 - 90) * 0.0174532925); 115 | x0 = sx * M_SIZE*100 + M_SIZE*120; 116 | y0 = sy * M_SIZE*100 + M_SIZE*140; 117 | // Draw scale arc, don't draw the last part 118 | if (i < 50) TFT->drawLine(x0, y0, x1, y1, TFT_BLACK); 119 | } 120 | 121 | TFT->drawString("%RH", M_SIZE*(5 + 230 - 40), M_SIZE*(119 - 20), 2); // Units at bottom right 122 | TFT->drawCentreString("%RH", M_SIZE*120, M_SIZE*70, 4); // Comment out to avoid font 4 123 | TFT->drawRect(5, 3, M_SIZE*230, M_SIZE*119, TFT_BLACK); // Draw bezel line 124 | 125 | PlotNeedle(0, 0); // Put meter needle at 0 126 | 127 | } 128 | 129 | void HS_SplashScreen::PlotNeedle(int value, byte ms_delay) 130 | { 131 | TFT->setTextColor(TFT_BLACK, TFT_WHITE); 132 | char buf[8]; dtostrf(value, 4, 0, buf); 133 | TFT->drawRightString(buf, M_SIZE*40, M_SIZE*(119 - 20), 2); 134 | if (value < -10) 135 | { 136 | value = -10; // Limit value to emulate needle end stops 137 | } 138 | if (value > 110) 139 | { 140 | value = 110; 141 | } 142 | 143 | // Move the needle until new value reached 144 | while (!(value == old_analog)) 145 | { 146 | if (old_analog < value) 147 | { 148 | old_analog++; 149 | } 150 | else 151 | { 152 | old_analog--; 153 | } 154 | 155 | if (ms_delay == 0) 156 | { 157 | old_analog = value; // Update immediately if delay is 0 158 | } 159 | 160 | float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle 161 | // Calcualte tip of needle coords 162 | float sx = cos(sdeg * 0.0174532925); 163 | float sy = sin(sdeg * 0.0174532925); 164 | 165 | // Calculate x delta of needle start (does not start at pivot point) 166 | float tx = tan((sdeg + 90) * 0.0174532925); 167 | 168 | // Erase old needle image 169 | //TFT->drawLine(M_SIZE*(120 + 20 * ltx - 1), M_SIZE*(140 - 20), osx - 1, osy, TFT_BLUE); 170 | //TFT->drawLine(M_SIZE*(120 + 20 * ltx), M_SIZE*(140 - 20), osx, osy, TFT_BLUE); 171 | //TFT->drawLine(M_SIZE*(120 + 20 * ltx + 1), M_SIZE*(140 - 20), osx + 1, osy, TFT_BLUE); 172 | TFT->drawLine(M_SIZE*(120 + 20 * ltx - 1), M_SIZE*(140 - 20), osx - 1, osy, TFT_WHITE); 173 | TFT->drawLine(M_SIZE*(120 + 20 * ltx), M_SIZE*(140 - 20), osx, osy, TFT_WHITE); 174 | TFT->drawLine(M_SIZE*(120 + 20 * ltx + 1), M_SIZE*(140 - 20), osx + 1, osy, TFT_WHITE); 175 | 176 | // Re-plot text under needle 177 | TFT->setTextColor(TFT_BLACK); 178 | TFT->drawCentreString("%RH", M_SIZE*120, M_SIZE*70, 4); // // Comment out to avoid font 4 179 | 180 | // Store new needle end coords for next erase 181 | ltx = tx; 182 | osx = M_SIZE*(sx * 98 + 120); 183 | osy = M_SIZE*(sy * 98 + 140); 184 | 185 | // Draw the needle in the new postion, magenta makes needle a bit bolder 186 | // draws 3 lines to thicken needle 187 | TFT->drawLine(M_SIZE*(120 + 20 * ltx - 1), M_SIZE*(140 - 20), osx - 1, osy, TFT_RED); 188 | TFT->drawLine(M_SIZE*(120 + 20 * ltx), M_SIZE*(140 - 20), osx, osy, TFT_MAGENTA); 189 | TFT->drawLine(M_SIZE*(120 + 20 * ltx + 1), M_SIZE*(140 - 20), osx + 1, osy, TFT_RED); 190 | 191 | // Slow needle down slightly as it approaches new postion 192 | if (abs(old_analog - value) < 10) 193 | { 194 | ms_delay += ms_delay / 5; 195 | } 196 | // Wait before next update 197 | delay(ms_delay); 198 | } 199 | } 200 | 201 | -------------------------------------------------------------------------------- /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 | // Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) 27 | //#define TFT_PARALLEL_8_BIT 28 | 29 | // Display type - only define if RPi display 30 | //#define RPI_DISPLAY_TYPE // 20MHz maximum SPI 31 | 32 | // Only define one driver, the other ones must be commented out 33 | #define ILI9341_DRIVER 34 | //#define ST7735_DRIVER // Define additional parameters below for this display 35 | //#define ILI9163_DRIVER // Define additional parameters below for this display 36 | //#define S6D02A1_DRIVER 37 | //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI 38 | //#define HX8357D_DRIVER 39 | //#define ILI9481_DRIVER 40 | //#define ILI9486_DRIVER 41 | //#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) 42 | //#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display 43 | //#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display 44 | //#define R61581_DRIVER 45 | //#define RM68140_DRIVER 46 | //#define ST7796_DRIVER 47 | 48 | // Some displays support SPI reads via the MISO pin, other displays have a single 49 | // bi-directional SDA pin and the library will try to read this via the MOSI line. 50 | // To use the SDA line for reading data from the TFT uncomment the following line: 51 | 52 | // #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only 53 | 54 | // For ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display 55 | // Try ONE option at a time to find the correct colour order for your display 56 | 57 | // #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue 58 | // #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red 59 | 60 | // For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below 61 | 62 | // #define M5STACK 63 | 64 | // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation 65 | // #define TFT_WIDTH 80 66 | // #define TFT_WIDTH 128 67 | // #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 68 | // #define TFT_HEIGHT 160 69 | // #define TFT_HEIGHT 128 70 | // #define TFT_HEIGHT 240 // ST7789 240 x 240 71 | // #define TFT_HEIGHT 320 // ST7789 240 x 320 72 | 73 | // For ST7735 ONLY, define the type of display, originally this was based on the 74 | // colour of the tab on the screen protector film but this is not always true, so try 75 | // out the different options below if the screen does not display graphics correctly, 76 | // e.g. colours wrong, mirror images, or tray pixels at the edges. 77 | // Comment out ALL BUT ONE of these options for a ST7735 display driver, save this 78 | // this User_Setup file, then rebuild and upload the sketch to the board again: 79 | 80 | // #define ST7735_INITB 81 | // #define ST7735_GREENTAB 82 | // #define ST7735_GREENTAB2 83 | // #define ST7735_GREENTAB3 84 | // #define ST7735_GREENTAB128 // For 128 x 128 display 85 | // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) 86 | // #define ST7735_REDTAB 87 | // #define ST7735_BLACKTAB 88 | // #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset 89 | 90 | // If colours are inverted (white shows as black) then uncomment one of the next 91 | // 2 lines try both options, one of the options should correct the inversion. 92 | 93 | // #define TFT_INVERSION_ON 94 | // #define TFT_INVERSION_OFF 95 | 96 | // If a backlight control signal is available then define the TFT_BL pin in Section 2 97 | // below. The backlight will be turned ON when tft.begin() is called, but the library 98 | // needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be 99 | // driven with a PWM signal or turned OFF/ON then this must be handled by the user 100 | // sketch. e.g. with digitalWrite(TFT_BL, LOW); 101 | 102 | // #define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options 103 | 104 | // ################################################################################## 105 | // 106 | // Section 2. Define the pins that are used to interface with the display here 107 | // 108 | // ################################################################################## 109 | 110 | // We must use hardware SPI, a minimum of 3 GPIO pins is needed. 111 | // Typical setup for ESP8266 NodeMCU ESP-12 is : 112 | // 113 | // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) 114 | // Display LED to NodeMCU pin VIN (or 5V, see below) 115 | // Display SCK to NodeMCU pin D5 116 | // Display SDI/MOSI to NodeMCU pin D7 117 | // Display DC (RS/AO)to NodeMCU pin D3 118 | // Display RESET to NodeMCU pin D4 (or RST, see below) 119 | // Display CS to NodeMCU pin D8 (or GND, see below) 120 | // Display GND to NodeMCU pin GND (0V) 121 | // Display VCC to NodeMCU 5V or 3.3V 122 | // 123 | // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin 124 | // 125 | // The DC (Data Command) pin may be labeled AO or RS (Register Select) 126 | // 127 | // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more 128 | // SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS 129 | // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin 130 | // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. 131 | // 132 | // The NodeMCU D0 pin can be used for RST 133 | // 134 | // 135 | // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin 136 | // If 5V is not available at a pin you can use 3.3V but backlight brightness 137 | // will be lower. 138 | 139 | 140 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### 141 | 142 | // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation 143 | //#define TFT_CS PIN_D8 // Chip select control pin D8 144 | #define TFT_DC 4 // Data Command control pin 145 | #define TFT_RST 2 // Reset pin (could connect to NodeMCU RST, see next line) 146 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 147 | 148 | //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) 149 | 150 | //#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen 151 | 152 | //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only 153 | 154 | 155 | // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### 156 | 157 | // Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact 158 | // but saves pins for other functions. It is best not to connect MISO as some displays 159 | // do not tristate that line wjen chip select is high! 160 | // On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode 161 | // On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK 162 | // In ESP8266 overlap mode the following must be defined 163 | 164 | //#define TFT_SPI_OVERLAP 165 | 166 | // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 167 | //#define TFT_CS PIN_D3 168 | //#define TFT_DC PIN_D5 // Data Command control pin 169 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 170 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 171 | 172 | 173 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### 174 | 175 | // For ESP32 Dev board (only tested with ILI9341 display) 176 | // The hardware SPI can be mapped to any pins 177 | 178 | //#define TFT_MISO 19 179 | //#define TFT_MOSI 23 180 | //#define TFT_SCLK 18 181 | //#define TFT_CS 15 // Chip select control pin 182 | //#define TFT_DC 2 // Data Command control pin 183 | //#define TFT_RST 4 // Reset pin (could connect to RST pin) 184 | //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST 185 | 186 | //#define TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) 187 | 188 | //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen 189 | 190 | //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only 191 | 192 | // For the M5Stack module use these #define lines 193 | //#define TFT_MISO 19 194 | //#define TFT_MOSI 23 195 | //#define TFT_SCLK 18 196 | //#define TFT_CS 14 // Chip select control pin 197 | //#define TFT_DC 27 // Data Command control pin 198 | //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) 199 | //#define TFT_BL 32 // LED back-light (required for M5Stack) 200 | 201 | // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### 202 | 203 | // The library supports 8 bit parallel TFTs with the ESP32, the pin 204 | // selection below is compatible with ESP32 boards in UNO format. 205 | // Wemos D32 boards need to be modified, see diagram in Tools folder. 206 | // Only ILI9481 and ILI9341 based displays have been tested! 207 | 208 | // Parallel bus is only supported on ESP32 209 | // Uncomment line below to use ESP32 Parallel interface instead of SPI 210 | 211 | //#define ESP32_PARALLEL 212 | 213 | // The ESP32 and TFT the pins used for testing are: 214 | //#define TFT_CS 33 // Chip select control pin (library pulls permanently low 215 | //#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 216 | //#define TFT_RST 32 // Reset pin, toggles on startup 217 | 218 | //#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 219 | //#define TFT_RD 2 // Read strobe control pin 220 | 221 | //#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus 222 | //#define TFT_D1 13 // so a single register write sets/clears all bits. 223 | //#define TFT_D2 26 // Pins can be randomly assigned, this does not affect 224 | //#define TFT_D3 25 // TFT screen update performance. 225 | //#define TFT_D4 17 226 | //#define TFT_D5 16 227 | //#define TFT_D6 27 228 | //#define TFT_D7 14 229 | 230 | 231 | // ################################################################################## 232 | // 233 | // Section 3. Define the fonts that are to be used here 234 | // 235 | // ################################################################################## 236 | 237 | // Comment out the #defines below with // to stop that font being loaded 238 | // The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not 239 | // normally necessary. If all fonts are loaded the extra FLASH space required is 240 | // about 17Kbytes. To save FLASH space only enable the fonts you need! 241 | 242 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 243 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 244 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 245 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 246 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 247 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 248 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 249 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 250 | 251 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 252 | // this will save ~20kbytes of FLASH 253 | #define SMOOTH_FONT 254 | 255 | 256 | // ################################################################################## 257 | // 258 | // Section 4. Other options 259 | // 260 | // ################################################################################## 261 | 262 | // Define the SPI clock frequency, this affects the graphics rendering speed. Too 263 | // fast and the TFT driver will not keep up and display corruption appears. 264 | // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails 265 | // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) 266 | // With an ILI9163 display 27 MHz works OK. 267 | 268 | // #define SPI_FREQUENCY 1000000 269 | // #define SPI_FREQUENCY 5000000 270 | // #define SPI_FREQUENCY 10000000 271 | // #define SPI_FREQUENCY 20000000 272 | #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 273 | // #define SPI_FREQUENCY 40000000 274 | // #define SPI_FREQUENCY 80000000 275 | 276 | // Optional reduced SPI frequency for reading TFT 277 | #define SPI_READ_FREQUENCY 20000000 278 | 279 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 280 | #define SPI_TOUCH_FREQUENCY 2500000 281 | 282 | // The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. 283 | // If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) 284 | // then uncomment the following line: 285 | //#define USE_HSPI_PORT 286 | 287 | // Comment out the following #define if "SPI Transactions" do not need to be 288 | // supported. When commented out the code size will be smaller and sketches will 289 | // run slightly faster, so leave it commented out unless you need it! 290 | 291 | // Transaction support is needed to work with SD library but not needed with TFT_SdFat 292 | // Transaction support is required if other SPI devices are connected. 293 | 294 | // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) 295 | // so changing it here has no effect 296 | 297 | // #define SUPPORT_TRANSACTIONS 298 | --------------------------------------------------------------------------------