Arduino Touch Screen Project – Tutorial45

The AZ-Delivery 2.4” TFT LCD Touch Display boasts 320x 240 pixels with 16-bit color. It has Touch capabilities, a built-in SD card drive, and plugs straight onto the top of an Arduino UNO or Mega. Amazon charges less than £11 for this device. It offers a major step up from the tiny SSD1306 128×64 monochrome display.

The TFT screen is much larger than the SSD1306 128×64 and much more colourful. The package includes an SD card reader on the underside and a stylus for accurate touch-screen control.

AZ-Delivery usually supply an e-book (pdf document for download) with their boards. The German version comes first followed by other languages. This has just become available and provides setup instructions and a demonstration graphics only sketch.

The underside of the board has labels on the pins. As the board is an Arduino shield, it will only fit on a UNO in one position. The SD card reader sits between USB and the power socket. It will also plug into and Arduino MEGA 2560. J1 and J2 fit into the digital pins, covering D0 to D13, while J3 and J4 fit into the analog and power pins.

LCD_D0 to LCD_D7 provide an 8-bit bus with the other LCD pins on J3.

The card reader uses the SPI pins, D11, D12, and D13 with chip select on D10. (Not D4 as used in all the Arduino example sketches.)

This picture shows the underside of the board with an SD card inserted in the card reader.

I searched the Web for drivers and examples and found a great deal of praise for the TFT graphics, reports of problems with the Touch control and nothing about the SD card reader on this board.

In the end I installed several libraries (with all dependencies): Adafruit GFX, Adafruit TFTLCD, Adafruit TouchScreen, Adafruit ILI9341, MCUFRIEND_kbv and SPFD5408-master. (The last 2 are not essential but include some interesting examples). The SD library is included in the basic Arduino set.

I’ve used GFX with mono displays such as SSD1306 and soon got the TFT display working. The following script gives some idea about what it can do. I’ve included pixels, text (of varying sizes), lines, rectangles, triangles, squares, graphs, screen rotation, and text on a path. I was very impressed with the clarity, speed, brightness, and colors produced.

Here is my first graphics sketch:

//  AZ-Delivery 2.4" TFT LCD touch shield Demo
//          Shield on Arduino UNO
//  +++ Tony Goodhew 17th September 2020 +++
//  Tutorial45.com

// Essential Adafruit GFX Libraries
#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>

#define LCD_CS A3     // Chip Select goes to Analog 3
#define LCD_CD A2     // Command/Data goes to Analog 2
#define LCD_WR A1     // LCD Write goes to Analog 1
#define LCD_RD A0     // LCD Read goes to Analog 0
#define LCD_RESET A4  // Can alternately just connect to Arduino's reset pin

// Colour definitions for 64k colour mode
// Bits 0..4 ->   Blue 0..4
// Bits 5..10 ->  Green 0..5
// Bits 11..15 -> Red 0..4
#define BLACK 0x0000
#define WHITE 0xFFFF
#define GREY 0x7BEF
#define LT_GREY 0xC618
#define GREEN 0x07E0
#define LIME 0x87E0
#define BLUE 0x001F
#define RED 0xF800
#define AQUA 0x5D1C
#define YELLOW 0xFFE0
#define MAGENTA 0xF81F
#define CYAN 0x07FF
#define DK_CYAN 0x03EF
#define ORANGE 0xFCA0
#define PINK 0xF97F
#define BROWN 0x8200
#define VIOLET 0x9199
#define SILVER 0xA510
#define GOLD 0xA508
#define NAVY 0x000F
#define MAROON 0x7800
#define PURPLE 0x780F
#define OLIVE 0x7BE0
// Put colours in an array for random colour access
int tftColours[] = {GREY,LT_GREY,GREEN,LIME,BLUE,RED,AQUA,YELLOW,MAGENTA,CYAN,DK_CYAN,
                   ORANGE,PINK,BROWN,VIOLET,SILVER,GOLD,NAVY,MAROON,PURPLE,OLIVE, WHITE};

// Create the TFT object using all pin assignments
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// =========== SETUP ================
void setup() {
  Serial.begin(9600); // Useful for debugging
  tft.reset();
  tft.begin(0x9341);  // SDFP5408
  tft.setRotation(3); // 320 wide by 240 high - reset button on left
  tft.fillScreen(BLACK);
  tft.setCursor(30, 50);
  tft.setTextColor(RED);
  tft.setTextSize(3);
  tft.println("AZ-Delivery");
  tft.setTextColor(YELLOW);
  tft.setCursor(30, 90);
  tft.println("2.4 TFT LCD");
  tft.setCursor(30, 120);
  tft.println("Touch Display");
  tft.setCursor(30, 150);
  tft.println("& SD Drive");
  tft.setCursor(30, 200);
  tft.setTextColor(RED);
  tft.setTextSize(2);
  tft.println("Tutorial45.com");
  delay(4000);
  tft.fillScreen(BLACK); // Clears the screen
  
  // Display some Pixels
  for (int m = 0; m <= 4000; m++)  {     // 4000 random pixels
    int xx = random(320);
    int yy = random(240);
    tft.drawPixel(xx, yy, tftColours[random(22)]);   
  }
  delay(2000);
  tft.fillScreen(BLACK);

  int x = 1;
  int y = 50;
  int h = 50;
  int w = 50;
  int xj = 160;
  int yj = 170;
  int t = RED;

  // Quilt
  for (int xx=0; xx < 320; xx = xx + 32){
    for (int yy = 0; yy < 240; yy = yy + 24){
      tft.fillRect(xx,yy,32,24,tftColours[random(22)]);
    }
  }

  delay(2000);
  tft.fillScreen(BLACK);
  // Grid
  tft.fillScreen(BLACK);
  for (int x =0; x < 320; x=x +10) {
    tft.drawFastVLine(x+5, 0, 240, RED);
  }
  for (int y = 0; y < 240; y = y + 10) {
    tft.drawFastHLine(0, y+3, 320 , BLUE);
  }
  delay(2000);
  tft.fillScreen(BLACK);
  for (int x = 60; x < 200; x=x +25) {
    int yy = x-10;
    tft.drawRect(x,yy,40,45,tftColours[random(22)]); 
    //drawRect( x,  y,  w,  h, c)
  }
  delay(2000);
  // Rectangles
  tft.fillScreen(BLACK);
  for (int x = 60; x < 200; x=x +25) {
    int yy = x-10;
    tft.fillRect(x,yy,40,45,tftColours[random(22)]); 
    //fillRect( x,  y,  w,  h, c)
  }
  delay(2000);
  tft.fillScreen(BLACK);
  int r = 5;
  for (int x = 60; x < 200; x=x +25) {
    int yy = x-10;
    tft.drawRoundRect(x,yy,40,45,r,tftColours[random(22)]);
    r = r + 3; 
    //drawRoundRect( x,  y,  w,  h, r, c)
  }
  delay(2000);
  tft.fillScreen(BLACK);
  r = 5;
  for (int x = 60; x < 200; x=x +25) {
    int yy = x-10;
    tft.fillRoundRect(x,yy,40,45,r,tftColours[random(22)]); 
    //fillRoundRect( x,  y,  w,  h, r, c)
    r = r + 3;
  }
  delay(2000);
  // A few circles
  tft.fillScreen(BLACK);
  for (int r = 120;r > 5; r=r-10){
    tft.fillCircle(80, 40, r, tftColours[random(22)]);
    delay(50);
  }
  for (int r = 5;r <120; r=r+10){
    tft.fillCircle(random(200), random(120), r, tftColours[random(22)]);
    delay(50);
  } 
  delay(2000);
  tft.fillScreen(BLACK);
  for (int r = 120;r > 5; r=r-10){
    tft.drawCircle(80 + random(60), 40 + random(70), r, tftColours[random(22)]);
  }
  delay(1000);
  for (int r = 5;r <120; r=r+10){
    tft.drawCircle(random(200), random(120), r, tftColours[random(22)]);
  } 
  delay(2000);
  
  // Draw Sine and Cosine curves  
  float sine[300];
  float cosine[300];
  tft.fillScreen(BLACK);
  tft.setTextColor(YELLOW);
  tft.setCursor(10,220);
  tft.setTextSize(2);
  tft.println("Sine Curve");
  tft.setTextColor(RED);
  tft.setCursor(125,20);
  tft.println("Cosine Curve");
  tft.drawFastHLine(9, 120, 300 ,BLUE);
  for (int i = 0; i < 300; i++){
    sine[i] = (sin(radians(i *1.2))) * -1; 
    tft.drawPixel(i+9,int(sine[i]*100+120),YELLOW);
    cosine[i] = (cos(radians(i *1.2))) * -1; 
    tft.drawPixel(i+9,int(cosine[i]*100+120),RED);
  }
  delay(2000);
  // Screen rotation - 4 0ptions 0, 90 180 and 270 degrees 
  tft.fillScreen(BLACK);
  tft.setCursor(25,32);   // Title splash
  tft.print("Rotate Screen");
  delay(1000);
  tft.fillScreen(BLACK);
  for (int rt = 0; rt <= 3  ; rt++){
    tft.setRotation(rt % 4);               // Values 0..3
    tft.fillScreen(BLACK);
    int cw = WHITE;
    tft.fillTriangle(0,0,0,30,30,0,cw);     // (x,y,x1,y1,x2,y2,colour)
    tft.fillTriangle(0,15,40,50,15,0,cw);
    tft.fillTriangle(15,0,50,40,0,15,cw);
    tft.fillTriangle(40,50,50,40,20,20,cw);
    tft.setTextSize(2);                     // Middle size
    tft.setTextColor(BLACK); 
    tft.setCursor(5,5);             
    tft.println(rt % 4);
    tft.setTextColor(RED);
    tft.setCursor(50,50);
    tft.setTextSize(3);
    tft.println("TFT LCD");
    delay(1000);  
  }
  tft.setRotation(3);
  tft.fillScreen(BLACK);
  // Text Sizes

  tft.setTextSize(1);                // Normal 1:1 pixel scale
  tft.setTextColor(WHITE);           // Draw white text
  tft.setCursor(0,0);                // Start at top-left corner
  tft.println("Text Sizesn");
  tft.setTextSize(2);                // Medium
  tft.println("AZ-Deliveryn");
  tft.setTextSize(3);                // Large - rather blocky  
  tft.println("TFT LCD 320x240n");
  tft.setTextSize(4);                // Larger - very blocky  
  tft.println("Touchn");
  tft.setTextSize(5);                // Largest - very blocky  
  tft.println("2.4 inch");
  tft.setTextSize(1);
  tft.setTextColor(BLUE);
  tft.setCursor(220,220);
  tft.println("Tutorial45.com");
  delay(2000);
  
  // Text along a path
  tft.fillScreen(BLACK);
  char msg2[] = "+ TFT LCD+Arduino=MAGIC +";
  for (int n = 0; n < 25; n++){
    char ch = msg2[n];
    int p = n*12; // was 9
    float y1 = (cos(radians(p *2.8125)) * -1);
    int yy = int(y1 *25.0 + 100);
    tft.drawChar(p + 5, yy, ch, RED, BLACK, 2);
  }
  delay(4000);
  tft.fillScreen(BLACK);
  char msg[] =  "+++ Tutorial45.com +++";
  tft.fillScreen(BLACK);
  for (int n = 0; n < 22; n++){
    char ch = msg[n];
    int p = n*9;
    float y1 = (sin(radians(p *2.8125)) * -1);
    int yy = int(y1 *25.0 + 100);
    tft.drawChar(p + 60, yy, ch, GREEN, BLACK, 1);
  }
  tft.setTextSize(1);
  tft.setTextColor(BLUE);
  tft.setCursor(220,120);
  tft.println("Finished");
}

// ------------ LOOP NOT NEEDED --------------------

void loop() {}

Color picking

Normally, when setting the colour of an RGB LED you have a range of 0-255 (0-FF hex) for each RGB component which gives white = FFFFFF, red = FF000, green FF00 and blue = FF. This is 24-bit colour and takes 3 bytes.  224 gives 16,777,216 different colours. The TFT screen is a 16-bit colour device which can display 65,536 different colours – more than enough. Here the range is limited to 5 bits each for red and blue and 6 bits for green. (Our eyes are more sensitive to green so It gets the extra bit of accuracy.)

R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0

To convert a 24-bit colour to a 16-bit colour held in integers r, g and b, each with range 0 – 255.

// Convert 24-bit colours to 16-bit colours
// Tony Goodhew 22 Sept 2020
// Tutorial45.com
void setup() {
  Serial.begin(9600);
  int r = 255;
  int g = 255;
  int b = 255;
  unsigned int n = r/8 * 2048 + g/4 * 32 + b/8;
  Serial.println(n,HEX);
}
void loop() {}

The following sketch gives an indication of the colours available by converting an array of 24-bit colour values into their 16-bit equivalent and displaying them on the screen with the data. There are not enough pixels on the screen to display all the colours at once so the last part of the sketch takes out the least significant green bit and displays half the available colours six ways.

//  Almost all the colours - several ways
//  AZ-Delivery 2.4" TFT LCD touch shield Demo
//          Shield on Arduino UNO
//  +++ Tony Goodhew 25th September 2020 +++
//  Tutorial45.com

#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>

#define LCD_CS A3     // Chip Select goes to Analog 3
#define LCD_CD A2     // Command/Data goes to Analog 2
#define LCD_WR A1     // LCD Write goes to Analog 1
#define LCD_RD A0     // LCD Read goes to Analog 0
#define LCD_RESET A4  // Can alternately just connect to Arduino's reset pin

#define RED 0xF800
#define YELLOW 0xFFE0
#define BLUE 0x001F
// Create the TFT object with all pin assignments
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// =========== SETUP ================
int title(String s){
    tft.fillScreen(0); // Clears the screen
    tft.setTextSize(3);
    tft.setCursor(60, 200);
    tft.setTextColor(0xC618); // Lt_Grey
    tft.println("32768 pixels");
    tft.setCursor(120, 5);
    tft.println(s);   
  }
void setup() {
  Serial.begin(9600); // Useful for debugging
  tft.reset();
  tft.begin(0x9341);  // SDFP5408 - start the display
  tft.setRotation(3); // 320 wide by 240 high - reset button on left
  tft.fillScreen(0); // Clears the screen
  tft.setCursor(30, 50);
  tft.setTextColor(RED);
  tft.setTextSize(3);
  tft.println("AZ-Delivery");
  tft.setTextColor(YELLOW);
  tft.setCursor(30, 90);
  tft.println("16-bit colours");
  tft.setCursor(30, 120);
  tft.println("from");
  tft.setCursor(30, 150);
  tft.println("24-bit colours");
  tft.setTextColor(BLUE);
  tft.setCursor(30, 185);
  tft.setTextSize(2);
  tft.println("Tutorial45.com");
  delay(4000);
  // A few interesting 24-bit colours - some dim - some bright!
  unsigned long c1[] = {
        0x000000,0xFFFFFF,0x333333,0x666666,0x999999,0xCCCCCC,0xCCCC99,0x9999CC,0x666699,
        0x660000,0x663300,0x996633,0x003300,0x003333,0x003399,0x000066,0x330066,0x660066,
        0x990000,0x993300,0xCC9900,0x006600,0x336666,0x0033FF,0x000099,0x660099,0x990066,
        0xCC0000,0xCC3300,0xFFCC00,0x009900,0x006666,0x0066FF,0x0000CC,0x663399,0xCC0099,
        0xFF0000,0xFF3300,0xFFFF00,0x00CC00,0x009999,0x0099FF,0x0000FF,0x9900CC,0xFF0099,
        0xCC3333,0xFF6600,0xFFFF33,0x00FF00,0x00CCCC,0x00CCFF,0x3366FF,0x9933FF,0xFF00FF,
        0xFF6666,0xFF6633,0xFFFF66,0x66FF66,0x66CCCC,0x00FFFF,0x3399FF,0x9966FF,0xFF66FF,
        0xFF9999,0xFF9966,0xFFFF99,0x99FF99,0x66FFCC,0x99FFFF,0x66CCFF,0x9999FF,0xFF99FF,
        0xFFCCCC,0xFFCC99,0xFFFFCC,0xCCFFCC,0x99FFCC,0xCCFFFF,0x99CCFF,0xCCCCFF,0xFFCCFF};
  unsigned int c2[80];      
  
  int l = 81;
  tft.fillScreen(0); // Clears the screen
  for (int i = 0; i < l; i++){
    int r = c1[i] >> 16;              // Get red 24-bit component - 0 to 255  
    int g = (c1[i] & 0x00ff00) >> 8;  // Get green
    int b = (c1[i] & 0x0000ff);       // Get blue
    unsigned long col2 = r/8 * 2048 + g/4 * 32 + b/8;  // Create 16-bit value
    // int division removes least significant bits 
    // multiplication shifts bits left 
    tft.fillScreen(0);
    tft.fillRect(40,20,240,120,col2);
    tft.setTextSize(3);
    tft.setCursor(37, 145);
    tft.setTextColor(0xC618); // Lt_Grey
    tft.print("24-bit: ");   
    tft.println(c1[i] & 0xFFFFFF,HEX);
    tft.println(" RGB: "+String(r) +" "+ String(g) +" "+String(b));
    int col3 = col2 & 0xFFFF;
    c2[i] = col3;
    String q =String(c2[i], HEX);
    q.toUpperCase();
//    String s = String("   16-bit: "+String(c2[i], HEX));
    String s = String("   16-bit: "+q);
    tft.println(s);
    delay(800);
  }
  int x = 0;
  int y = 0;
  unsigned int c = 0;
  String s = "R G B";
  title(s); 
  for (int r = 0; r <32; r = r + 1){
    for (int g = 0; g < 64; g=g+2){
      for (int b = 0; b < 32; b = b + 1){
        unsigned int c = r * 2048 + g * 32 + b;  // Create 16-bit value
        tft.drawPixel(x +30, y+45, c);
        x = x + 1;
          if (x > 255){
            x = 0;
            y = y + 1; } } } }
  delay(1000);
  s = "B G R";
  title(s);
  x = 0; y = 0;    
  for (int b = 0; b <32; b = b + 1){
    for (int g = 0; g < 64; g = g + 2){
      for (int r = 0; r < 32; r = r + 1){
        unsigned int c = r * 2048 + g * 32 + b;  // Create 16-bit value
        tft.drawPixel(x +30, y+45, c);
        x = x + 1;
          if (x > 255){
            x = 0;
            y = y + 1; } } } }
  delay(1000);
  s = "G B R";
  title(s);
  x = 0; y = 0; 
  for (int g = 0; g < 64; g = g + 2){    
    for (int b = 0; b <32; b = b + 1){ 
      for (int r = 0; r < 32; r = r + 1){
        unsigned int c = r * 2048 + g * 32 + b;  // Create 16-bit value
        tft.drawPixel(x +30, y+45, c);
        x = x + 1;
          if (x > 255){
            x = 0;
            y = y + 1; } } } }
  delay(1000);
  s = "R B G";
  title(s);
  x = 0; y = 0;
  for (int r = 0; r < 32; r = r + 1){    
    for (int b = 0; b <32; b = b + 1){ 
      for (int g = 0; g < 64; g = g + 2){
        unsigned int c = r * 2048 + g * 32 + b;  // Create 16-bit value
        tft.drawPixel(x +30, y+45, c);
        x = x + 1;
          if (x > 255){
            x = 0;
            y = y + 1; } } } }
  delay(1000);
  s = "B R G";
  title(s);
  x = 0; y = 0; 
  for (int b = 0; b <32; b = b + 1){
     for (int r = 0; r < 32; r = r + 1){
        for (int g = 0; g < 64; g = g + 2){ 
        unsigned int c = r * 2048 + g * 32 + b;  // Create 16-bit value
        tft.drawPixel(x +30, y+45, c);
        x = x + 1;
          if (x > 255){
            x = 0;
            y = y + 1; } } } }
  delay(1000);
  s = "G R B";
  title(s);
  x = 0; y = 0;
  for (int g = 0; g < 64; g = g + 2){
    for (int r = 0; r < 32; r = r + 1){ 
      for (int b = 0; b <32; b = b + 1){
        unsigned int c = r * 2048 + g * 32 + b;  // Create 16-bit value
        tft.drawPixel(x +30, y+45, c);
        x = x + 1;
          if (x > 255){
            x = 0;
            y = y + 1; } } } }
}   
void loop() {} //-- LOOP NOT NEEDED --

Using the SD card reader

Looking at the bottom of the board there are 4 pins used for communication with the SD card reader:

SD_SS pin 10-SS = Chip Select
SD_DI pin 11- MOSI
SD_DO pin 12-MISO
SD_SCK pin 13-CLOCK

This is a standard SD card reader and uses the SPI pins on the UNO and pin 10 as the Chip Select.

The Arduino.cc site lists the following SPI pins on their different boards

Arduino / Genuino Board MOSI MISO SCK SS (slave) SS (master) Level
Uno or Duemilanove 11 or ICSP-4 12 or ICSP-1 13 or ICSP-3 10 5V
Mega1280 or Mega2560 51 or ICSP-4 50 or ICSP-1 52 or ICSP-3 53 5V
Leonardo ICSP-4 ICSP-1 ICSP-3 5V

If you need to use the SD card with a MEGA you will need to run the board via jumper leads.

The SD card reader library is included with the basic setup, so we do not need to load a fresh library. The examples cover the simple tasks of creating, writing to, reading from and deleting files at a very basic level, all with strings.

I used a 16GB SD-HC class 4 card. It is easy to insert into the reader with a gentle push. Push in again to extract if necessary.

To check that everything is in order you can run the “CardInfo” sketch from the Examples tab of the File muenu. (File =>> Examples =>> SD =>> CardInfo.)  Make sure you set:

const int chipSelect = 10;

on the line before the setup routine. I got the following with my card, which I had been using earlier.

At this point it is worth running the other example sketches to get a feel for how the card reader works. 

You may have noticed that:

  • This is a very basic serial file device.
  • You can only have one file open at a time.
  • You write strings to the file (so numbers have to be converted to strings before they are saved.)
  • You can make up a record of several strings which are in turn made up of characters.
  • You concatenate the sub-strings to make the record string using a comma as a delimiter.
  • Each line in the file is a record which terminates with “n”.
  • If you write to an existing file the new data is appended at the end.
  • In the examples the chip select pin is 4. We have to use pin 10 when the shield is plugged into an UNO.

An obvious use for the SD reader is to log readings from sensors and display the results on the TFT display. Unfortunately,  the shield covers and uses most of the pins. The solution is to connect just the SD reader and power pins with jump leads which leaves plenty of pins to collect data from sensors.  

Most Arduino users seldom use string manipulation. The documentation and a few simple examples of how to use strings are well scattered over the Web and difficult to find. The first sketch demonstrates how to create a file of 5 records/lines, each made up from an integer, a string, and a floating-point variable. The file is called datalog6.txt.

// Create a log file sketch
// Write demo log file: 'datalog6.txt' with 3 fields per
//      record: Int, String and Float
// The SD_Read_File sketch can process it.
// Tony Goodhew 21 Sept 2020
// SD reader on TFT shield – Output to Serial Monitor
// Tutorial45.com
#include <SPI.h>
#include <SD.h>
const int chipSelect = 10;
void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);{}
  Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // Halt
    while (1);
  }
  Serial.println("card initialized.");
  File dataFile = SD.open("datalog6.txt", FILE_WRITE);
  // Set up test data in 3 arrays
  int i[] = {1, 2222, 3, -4, 5};
  String s[] = {"Tony", "Fred", "Anne", "Mags", "William"};
  float f[] = {3.14, 2.2, 3.3333, 10.66, -5.5};
  String j = "";  // record string
  Serial.println("Writing datalog6.txt");
  // Loop for 5 data records
  for (int p=0; p < 5; p++){
    // Pack the data up into a string, j
    // Build long string with 3 data items as strings
    //    separated with commas
    j = String(i[p]) + "," + s[p]+ "," + String(f[p]); // One record
    // Display the long string
    Serial.println(j);
    // Save j to SD Card
    // if the file is available, write to it:
    if (dataFile) {
      dataFile.println(j);  // Write j to the file
    }
    // if the file isn't open, pop up an error:
    else {
    Serial.println("error opening datalog6.txt");
    }
  }
  dataFile.close(); 
  Serial.println("nFile datalog6.txt written - FINISHED!n");  
}
void loop() {}

The second sketch reads the data we have saved in the datalog6.txt file. It splits each line/record into the 3 strings and then converts one to an Integer and the another to Floating Point. Just to prove that they are now stored as numbers it multiplies them together and displays the result.

// Read the data back from a log file: 'datalog6.txt'
// Tony Goodhew 21 Sept 2020
// SD reader on TFT shield
// Tutorial45.com

#include <SPI.h>
#include <SD.h>

const int chipSelect = 10;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");

  // Open the file
  File dataFile = SD.open("datalog6.txt");
  Serial.println("Reading datalog6.txt");
  String j = ""; // Holds a line from the data file (3 fields = 1 record)
  
  if (dataFile) {
    while (dataFile.available()) {        // Not reached the End Of File     
      j = dataFile.readStringUntil('n'); // Get single line from file
      Serial.println("n"+j);
      int comma1 = j.indexOf(','); 
      int comma2 = j.indexOf(',', comma1 + 1); 
      Serial.println("Commas: " + String(comma1) + " "+ String(comma2));
      // Separate back into 3 strings at the commas
      String ii = j.substring(0,comma1);              // integer
      String sname = j.substring(comma1 +1, comma2);  // string
      String ff = j.substring(comma2 +1);             // float
      // Print out the strings
      Serial.println(ii);
      Serial.println(sname);
      Serial.println(ff);
      int iOut = ii.toInt(); // Convert back to integer
      float fOut = ff.toFloat();  // Convert back to Floating Point
      float res = iOut * fOut;    // Calculation with both numbers
      Serial.println(sname +": " + String(res)+"n");  // Output the result
    }
    dataFile.close();
  }
  // if the file isn't open, print up an error message
  else {
    Serial.println("error opening datalog6.txt");
  }
}
void loop() {}

Using the Touch Layer

This is the part that often causes the most trouble with many owners giving up at this point. It may be because there are several different configurations of the pins used to connect to the touch layers of the screen on the many varied breakout boards and shields using this display. In this case four of the pins are used, at different times, to control both the graphics or the touch elements of the screen.

How does a touch screen sense position?

This is a resistive touch screen, rather than a capacitive one. Above the graphics, layers are two transparent resistive layers held apart by tiny dimples. One is connected at the top and bottom and the other at the sides. A potential difference is applied across them and when the stylus or a finger presses on the screen an electrical connection is made between the resistive layers.

The Analog pins are used to measure the voltages at that point on the two resistive layers, one at a time, in the same manner as we read the voltage from the wiper of a potentiometer – a potential divider. Using these values, it is possible to calculate, quite accurately, the coordinates of the point on the screen where the pressure has been applied. Calibration is often needed to improve accuracy. Adafruit suggests reading the resistance across the X plate (XP = D8 and XM = A2). On my board, I got 341 Ohms. Use this value as SENSITIVITY.

// Simple Drawing Script for AZ-Delivery TFT Touch 240x320 Shield
// Tony Goodhew 30 Sept 2020
// Modified from Adafruit examples for a similar shield
// Tutorial45.com
// Adafruit GFX Libraries
#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>
#include <TouchScreen.h>

#define LCD_CS A3     // Chip Select goes to Analog 3
#define LCD_CD A2     // Command/Data goes to Analog 2
#define LCD_WR A1     // LCD Write goes to Analog 1
#define LCD_RD A0     // LCD Read goes to Analog 0
#define LCD_RESET A4  // Can alternately just connect to Arduino's reset pin

// 16-bit colour values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
unsigned int c[] = {BLACK,RED,GREEN,BLUE,CYAN,MAGENTA,YELLOW,WHITE};
int cc = c[1];

// Create the TFT (Graphics) object - named pins
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// Set up the touch screen element of the TFT screen
#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8   // can be a digital pin
#define SENSITIVITY 341                   // Was 300 = Resistance A2 to D8                                                                         
#define MINPRESSURE 10
#define MAXPRESSURE 1000
#define TS_MINX  150
#define TS_MINY  120
#define TS_MAXX  920
#define TS_MAXY  940
#define MINPRESSURE 10
#define MAXPRESSURE 1000
// Create Touch Screen object
TouchScreen ts = TouchScreen(XP, YP, XM, YM, SENSITIVITY);

void palette(){       // Create colour 'buttons' at top of screen
   int w = 30;
   int h = 40;
   tft.fillRect(0, 0, w, h, BLACK);
   tft.fillRect(w, 0, w, h, RED);
   tft.fillRect(w*2, 0, w, h, GREEN);
   tft.fillRect(w*3, 0, w, h, BLUE);
   tft.fillRect(w*4, 0, w, h, CYAN);
   tft.fillRect(w*5, 0, w, h, MAGENTA);
   tft.fillRect(w*6, 0, w, h, YELLOW);
   tft.fillRect(w*7, 0, w, h, WHITE); 
}
//---------SETUP--------------------
void setup() {
  Serial.begin(9600);
  tft.reset();
  tft.begin(0x9341);  // SDFP5408
  tft.fillRect(0,0, 240,320, BLACK);  // Drawing area
  palette();  // Drawing colour buttons
}
//---------Main Loop------------
void loop(){
  TSPoint p = ts.getPoint(); // Read the Touch Screen
  // Shared pins - fix direction
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);

  // Check for pressure on screen
  if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
/*  // Debugging     
    Serial.print("X = "); Serial.print(p.x);
    Serial.print("tY = "); Serial.print(p.y);
    Serial.print("tPressure = "); Serial.println(p.z);
*/      
    // Scale Touch values to fit TFT screen 
    // Adjust final number to get drawn dot under the stylus point (fudge factors!)
    p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0) -10;
    p.y = map(p.y, TS_MINY, TS_MAXY, tft.height(), 0)-14; 
    
/*  // Debugging   
    Serial.print("("); Serial.print(p.x);
    Serial.print(", "); Serial.print(p.y);
    Serial.println(")");
*/    
    if (p.y < 40){                              // In the palette area?
      if (p.x <30){                             // Black to clear drawing area
        tft.fillRect(0, 40, 240,299, BLACK);}
      else{                                     // Change colour
        cc = c[p.x / 30];
      }
    }
    else{
      tft.fillCircle(p.x, p.y, 2, cc);         // Draw dot at stylus point
    }
  }
}

Try running the sketch to draw on the screen. The BLACK palette ‘button’ clears the screen and the others change the ‘ink’ colour. If the dot drawn is not directly under the stylus you can adjust the ‘fudge factors’ in the scaling section. 

16-bit Colour Mixing Project

As a final example here is a sketch which shows off the Touch screen with buttons, bar graphs and colours. The buttons allow the user to adjust the RGB mix to display all the possible colours available. If you find one you particularly like it displays the hex value of the 16-bit colour. 

Gently pressing on the buttons at the bottom with the stylus changes the RGB values within their allowed ranges. The bars move to show the fraction of maximum possible for each of the red, green and blue values.

// Project Sketch for AZ-Delivery TFT Touch 240x320 Shield
// Tony Goodhew 2 October 2020
// Tutorial45.com
// Adafruit GFX Libraries
#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>
#include <TouchScreen.h>

#define LCD_CS A3     // Chip Select goes to Analog 3
#define LCD_CD A2     // Command/Data goes to Analog 2
#define LCD_WR A1     // LCD Write goes to Analog 1
#define LCD_RD A0     // LCD Read goes to Analog 0
#define LCD_RESET A4  // Can alternately just connect to Arduino's reset pin

// 16-bit colour values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define WHITE 0xFFFF

// Create the TFT (Graphics) object - named pins
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// Set up the touch screen element of the TFT screen
#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8   // can be a digital pin
#define SENSITIVITY 341                   // Was 300 = Resistance A2 to D8                                                                         
#define MINPRESSURE 10
#define MAXPRESSURE 1000
#define TS_MINX  150
#define TS_MINY  120
#define TS_MAXX  920
#define TS_MAXY  940
#define MINPRESSURE 10
#define MAXPRESSURE 1000
// Create Touch Screen object
TouchScreen ts = TouchScreen(XP, YP, XM, YM, SENSITIVITY);

int left[] ={5,45,85,125,165,205};    // left edge of buttons
int right[]={35,75,115,155,195,235};  // right edge of buttons
int red = 15;  // Colours set at mid-points
int green = 31;
int blue = 15;
int old_r = 0;
int old_g = 0;
int old_b = 0;

void graph(int xx, int yy, int p, unsigned int col){
  // Draw a bar graph
  tft.fillRect(xx, yy, 200, 20, BLACK);  // Overwrite to background colour
  tft.fillRect(xx, yy, p * 2, 20, col);  // New bar graph
}
  
void palette(){
  // Create 'buttons' near bottom of screen
  int w = 30;
  int h = 30;
  int y = 280;
  for (int i=0; i<6;i++){
    unsigned int bc = GREEN;
    if (i < 2){bc = RED;}
    if (i > 3){bc = BLUE;}
    tft.fillRect(5 + i *(w+10), y, w, h, bc);
  }
  tft.setTextColor(WHITE);
  tft.setTextSize(3);
  for (int i=0; i<6; i = i + 2){
    tft.setCursor(12 + i *(w+10), y +4);
    tft.println("+");}
  for (int i=1; i<6; i = i + 2){
      tft.setCursor(12 + i *(w+10), y +4);
      tft.println("-");}
}
void show(){
  // Display values and bar graphs
  unsigned int n = red * 2048 + green * 32 + blue;
  tft.fillRect(0, 0, 240, 60, n);
  tft.drawFastVLine(19, 95, 90, 0x7BEF); // Graph axis  
  tft.drawFastVLine(18, 95, 90, 0x7BEF);
  tft.setTextSize(3);
  tft.setTextColor(WHITE);
  if (n > 0xF2BF){tft.setTextColor(BLACK);}  // Black on bright colours
  tft.setCursor(85,20);
  tft.println(n,HEX);
  tft.fillRect(0, 230, 240, 46, BLACK);
  String rS = String(red);
  if (red < 10){rS = " " + rS;}  // Left alignment
  tft.setTextColor(WHITE);
  tft.setCursor(25,230);
  tft.println(rS);
  String gS = String(green);
  if (green < 10){gS = " " + gS;}
  tft.setCursor(105,230);
  tft.println(gS);
  String bS = String(blue);
  if (blue < 10){bS = " " + bS;}
  tft.setCursor(186,230);
  tft.println(bS);
  tft.setTextSize(2);
  int rp = red * 100 / 31;
  int gp = green * 100 / 63;
  int bp = blue * 100 / 31;
  String rz = String(rp) + "%";
  String gz = String(gp) + "%";
  String bz = String(bp) + "%";
  if (rp < 100){rz = " " + rz;}
  if (rp < 10){rz = " " + rz;}
  tft.setCursor(13,260);
  tft.println(rz);
  if (gp < 100){gz = " " + gz;}
  if (gp < 10){gz = " " + gz;}
  tft.setCursor(92,260);
  tft.println(gz);
  if (bp < 100){bz = " " + bz;}
  if (bp < 10){bz = " " + bz;}
  tft.setCursor(174,260);
  tft.println(bz);
  if (old_r != red){                // red value changed?
    graph(20,100,rp,RED);           // Update graph
    old_r = red;
    }
  if (old_g != green){              // green value changed? 
    graph(20,130,gp,GREEN);
    old_g = green;   
  }
  if(old_b != blue){               // blue value change  
    graph(20,160,bp,BLUE);
    old_b = blue;
  }   
}
//---------SETUP------------------
void setup() {
  Serial.begin(9600);
  tft.reset();
  tft.begin(0x9341);  // SDFP5408
  tft.fillRect(0,0, 240,320, BLACK);    // Drawing area
  palette();  // Drawing buttons
  show();  // Display the inital values  
}
//---------Main Loop------------
void loop(){
  TSPoint p = ts.getPoint(); // Read the Touch Screen
  // Shared pins - fix direction
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);

  // Check for pressure on screen
  if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
      
    // Scale Touch values to fit TFT screen 
    // Adjust final values to improve accuracy
    p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0) -10;
    p.y = map(p.y, TS_MINY, TS_MAXY, tft.height(), 0)-14; 
    
    if ((p.y >= 300) & (p.y <= 330)){               // In the button area?
      if ((p.x >= left[0]) & (p.x <= right[0])){    // Process buttons
        red = red +1;                               //   as necessary
        if (red > 31){red = 31;}
      }
      else if ((p.x >= left[1]) & (p.x <= right[1])){ 
        red = red -1;
        if (red < 0){red = 0;}
      }
      else if ((p.x >= left[2]) & (p.x <= right[2])){ 
        green = green + 1;
        if (green > 63){green = 63;} 
      }
      else if ((p.x >= left[3]) & (p.x <= right[3])){ 
        green = green -1;
        if (green < 0){green = 0;}
      }
      else if ((p.x >= left[4]) & (p.x <= right[4])){ 
        blue = blue + 1;
        if (blue > 31){blue = 31;}
      }
      else if ((p.x >= left[5]) & (p.x <= right[5])){ 
        blue = blue - 1;
        if (blue < 0){blue = 0;}
      }
    }
    // Update Display
    show();
  }
}

Conclusion

There is a small amount of jitter as the bar graph re-draws but overall, the shield works quickly and very well. After the screen has updated and waiting for a touch the image is steady, sharp, and bright. Once you have calibrated the touch device it is very accurate as demonstrated with the small (30×30 pixel buttons) and provides excellent, colorful graphics on a usefully large display.

The SD card reader is a bonus, and could always be used, via jump wires, to record values from sensors on the other pins. These values could then be displayed graphically on the display with a different sketch.

I was very pleased with the quality of the display and the accuracy of the Touch device. It sits neatly and securely on a UNO or a MEGA 2560. With an SD card reader included it was excellent value and I will be making good use of it in the future.


Source link