Last Updated: 10/01/2022

ESP32 with GT911 Capacitive Touch Screen

Components >> ESP32 with GT911 Capacitive Touch Screen

ESP32 with GT911 Capacitive Touch Screen

Capacitative touch screens are found on modern mobile phones and car sat navs. They are capable of tracking multiple touches allowing gestures to be programmed.

However for this tutorial I will just be going through how to track the touches.

This is a support titorial for a DCC modular model railway controller.

The GT911 screens are available in a number of sizes, just type in GT911 touch screen into AliExpress and you will see they are realily available and cheap.

Most of the ones for sale are being sold as replacement screens for in car Sat navs but they open up a world of opportunities to Arduino/ESP32 programmers.

WARNING

GT911 screen are 3.3V, so if you are using an Arduino you will need a 3.3v model such as an Arduino Due

Pins

GT911 screen only have 6 pins.

In this tutorial I will be using an ESP32 and the copnnections used are as follows.

RST > GPIO 15
VCC > 3.3v
GND > GND
INT > GPIO 2
SDA > SDA (GPIO 21)
SCL > SCL (GPIO 22)

Change the pins to suit your board.

To connect to the ribbon cable you will need a 2.54mm FFC FPC 6P adapter.
The cable plugs into the adapter and then connect the adapter to your board.



GT911 Touch Screen and Connector

GT911 Connector Close Up

Prototype Model railway control panel built using 2 x GT911 touch screens and neopixels. The touch screens in this prototype are controlled by a single Arduino Due.

Model railway control panel

 

 

GT911 Libraries

There are a few GT911 libraries for Arduino/ESP32, however for my sketches I have not used one. All it requires is the "Wire.h"

The sketch I used is based on someone elses work, sadly I can't find where I got the sketch from but whoever wrote the original deserves credit for their work.

Despite it working well on an Arduino Due when I came to load it onto the ESP32 it came up with an error and eventually I tracked it down to a bug in the code that the arduino overlooks. I have corrected the bug in the scripts suppiled.

Although the touch screen is capable of tracking 5 touches at the same time my main use is for single touches as part of a model railway control panel.

As such I have included three scripts, a single touch version and a multi touch.

Then as a final script I have shown how to create virtual buttons for those building model railway control panels.

Example 1: GT911_ESP32v1.ino

Basic script to get a single X/Y touch co ordinate

Click to Download code: GT911_ESP32v1.ino

Simple test sketch, red background, white outline box and some text.
Touch points written to Serial monitor.

 

 
/*  GT911_ESP32v1
 *   05/04/2024
     Bug fixed 05/04/2024
     Working for single point touch

     Screen > Pins Used ESP32 Dev Module

     RST > GPIO 15
     VCC > 3.3v
     GND > GND
     INT > GPIO 2
     SDA > SDA (GPIO 21)
     SCL > SCL (GPIO 22)




*/

#include "Wire.h"

//Screen address for GT911
uint8_t addr  = 0x5d;  //CTP IIC ADDRESS
//Pins
const int GT911_RESET = 15;   //CTP RESET
const int GT911_INT =   2;   //CTP  INT


//I have included debounce as it makes the output easier to read and means it is set up for those building model railway control panels
unsigned long captouchbounce = 0;//like button bounce for touch
int captouched = 0;//1 means there has been a touch
//X and Y positions that will be used in further programming
int captouchx = 0;
int captouchy = 0;
int lastpixeltouched = 1000;//keeps track of the last touched pixel to prevent excess bounce

//This is some data that is needed to configure the GT911 touch screen...not my work.
unsigned char  GTP_CFG_DATA[] =
{
  0x5A, 0x20, 0x03, 0xE0, 0x01, 0x05, 0x0D, 0x00,
  0x01, 0x08, 0x28, 0x08, 0x50, 0x32, 0x03, 0x05,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x88, 0x29, 0x0A, 0x35, 0x37,
  0xD3, 0x07, 0x00, 0x00, 0x01, 0x81, 0x02, 0x1D,
  0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x64, 0x32,
  0x00, 0x00, 0x00, 0x28, 0x5A, 0x94, 0xC5, 0x02,
  0x00, 0x00, 0x00, 0x00, 0x98, 0x2B, 0x00, 0x84,
  0x33, 0x00, 0x74, 0x3C, 0x00, 0x67, 0x46, 0x00,
  0x5C, 0x53, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
  0x12, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
  0x04, 0x06, 0x08, 0x0F, 0x10, 0x12, 0x16, 0x18,
  0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x25, 0x01
};

struct TouchLocation
{
  uint16_t x;
  uint16_t y;
};


//Various variables required for the GT911 screen

TouchLocation touchLocations[5];

void inttostr(uint16_t value, uint8_t *str);
uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len);
void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt);
uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len );
uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num );
uint32_t dist(const TouchLocation & loc);
uint32_t dist(const TouchLocation & loc1, const TouchLocation & loc2);
bool sameLoc( const TouchLocation & loc, const TouchLocation & loc2 );
uint8_t buf[80];

//Function to initialise the GT911 touch screen
void gt911setup() {

  delay(300);
  pinMode(GT911_RESET, OUTPUT);
  pinMode     (GT911_INT, OUTPUT);
  digitalWrite(GT911_RESET, LOW);
  delay(20);
  digitalWrite(GT911_INT, LOW);
  delay(50);
  digitalWrite(GT911_RESET, HIGH);
  delay(100);
  pinMode     (GT911_RESET, INPUT);
  delay(100);
  uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  pinMode(GT911_RESET, OUTPUT);
  pinMode     (GT911_INT, OUTPUT);
  digitalWrite(GT911_RESET, LOW);
  delay(20);
  digitalWrite(GT911_INT, LOW);
  delay(50);
  digitalWrite(GT911_RESET, HIGH);
  delay(100);
  pinMode     (GT911_INT, INPUT);
  delay(100);
  re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  uint8_t bb[2];
  readGT911TouchAddr(0x8047, bb, 2);
  while (bb[1] != 32) {
    Serial.println("Capacitive touch screen initialized failure");
    pinMode(GT911_RESET, OUTPUT);
    pinMode     (GT911_INT, OUTPUT);
    digitalWrite(GT911_RESET, LOW);
    delay(20);
    digitalWrite(GT911_INT, LOW);
    delay(50);
    digitalWrite(GT911_RESET, HIGH);
    delay(100);
    pinMode     (GT911_INT, INPUT);
    delay(100);
    uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  }
  Serial.println("Capacitive touch screen  initialized success");

}

//Not required, just in sketch to test connections
void scani2c() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for (address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error == 4)
    {
      Serial.print("Unknown error at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
}



uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len)
{
  uint8_t retry = 0;
  for (retry = 0; retry < 5; retry++)
  {
    writeGT911TouchRegister(0x8047, buf, cfg_len);
    //if(ret==0)break;
    delay(10);
  }
  return retry;
}

//function that writes to the GT911...do not edit
void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt)
{
  uint16_t i = 0;

  Wire.beginTransmission(addr);
  Wire.write( regAddr >> 8 ); // register 0
  Wire.write( regAddr);  // register 0
  for (i = 0; i < cnt; i++, val++) //data
  {
    Wire.write( *val );  // value
  }
  uint8_t retVal = Wire.endTransmission();

}

//function that reads from the GT911...do not edit
uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len )
{
  uint8_t i;
  uint8_t returned;
  uint8_t retVal;
  Wire.beginTransmission(addr);
  Wire.write( regAddr >> 8 ); // register 0
  Wire.write( regAddr);  // register 0
  retVal = Wire.endTransmission();

  returned = Wire.requestFrom(addr, len);    // request 1 bytes from slave device #2
  for (i = 0; (i < len) && Wire.available(); i++)

  {
    pBuf[i] = Wire.read();
  }
  return i;
}



////function that works out the touch coordinates for GT911...do not edit
uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num )
{
  uint8_t retVal;
  uint8_t i;
  uint8_t k;
  uint8_t  ss[1];
  do
  {
    if (!pLoc) break; // must have a buffer
    if (!num)  break; // must be able to take at least one
    ss[0] = 0;

    readGT911TouchAddr( 0x814e, ss, 1);
    uint8_t status = ss[0];

    if ((status & 0x0f) == 0) break; // no points detected
    uint8_t hitPoints = status & 0x0f;
    //Serial.print("number of hit points = ");
    //  Serial.println( hitPoints );
    
    uint8_t tbuf[40];//changed to 40 as that is number called for in  readGT911TouchAddrTest( 0x8150, tbuf, 40);
    uint8_t tbuf1[8];
    readGT911TouchAddr( 0x8150, tbuf, 40);
    readGT911TouchAddr( 0x8150 + 32, tbuf1, 8);

    for (k = 0, i = 0; (i <  4 * 8) && (k < num); k++, i += 8)
    {
      pLoc[k].x = tbuf[i + 1] << 8 | tbuf[i + 0];
      pLoc[k].y = tbuf[i + 3] << 8 | tbuf[i + 2];
    }
    pLoc[k].x = tbuf1[1] << 8 | tbuf1[0];
    pLoc[k].y = tbuf1[3] << 8 | tbuf1[2];

    retVal = hitPoints;

  } while (0);

  ss[0] = 0;
  writeGT911TouchRegister( 0x814e, ss, 1);

  delay(2);
  return retVal;
}


//Function that uses the functions above to give a simple X/Y position to be used in your code
void checkfortouchscreen() {

  captouched = 0;
  pinMode     (GT911_INT, INPUT);
  uint8_t st = digitalRead(GT911_INT);
  if (!st)  //Hardware touch interrupt
  {
    //Serial.println("Touch: ");

    //This line gives the number of touch points
    //Screen can deal with 5 touches at once
    uint8_t count = readGT911TouchLocation( touchLocations, 5 );
    /*
      if(count > 0){
      Serial.print("Touches: ");
      Serial.println(count);
      }
    */

    if (count) {
      static TouchLocation caplastTouch = touchLocations[0];// only take single touch, not dealing with multitouch
      caplastTouch = touchLocations[0];
      if (millis() > captouchbounce) { //cuts out multitouch
        captouchbounce = millis() + 250;//250 is the debounce, lower makes more sensitive but more false touches

        //only using first touch for now
        if (touchLocations[0].x > 0 && touchLocations[0].y > 0) { //only do something if there is an x and Y value
          captouched = 1;
          captouchx = touchLocations[0].x;
          captouchy = touchLocations[0].y;
          //X and Y positions sent to serial monitor
          Serial.print(captouchx);
          Serial.print(" : ");
          Serial.println(captouchy);

        }
      }
    }

  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("GT911_ESP32v1");

  Wire.begin();

  scani2c();//Not required, just in sketch to test connections

  //set up the screen
  gt911setup();

}

void loop() {
  checkfortouchscreen();//keep looking for screen touches

}

Example 2: GT911_ESP32v2.ino

Basic script to get multiplee X/Y touch co ordinates

Click to Download code: GT911_ESP32v2.ino

 
/*  GT911_ESP32v2
 *    05/04/2024
 Bug fixed effecting certain boards
     Basic sketch to see if I ca get ESP32 to work with GT911 touch screen
     working for multiple touch

     Debounce needs to be removed for multiple touches but is included
     to make the output easier to read

  Screen > Pins Used ESP32 Dev Module

     RST > GPIO 15
     VCC > 3.3v
     GND > GND
     INT > GPIO 2
     SDA > SDA (GPIO 21)
     SCL > SCL (GPIO 22)
*/
//Used for GT911
#include "Wire.h"

uint8_t addr  = 0x5d;  //CTP IIC ADDRESS
const int GT911_RESET = 15;   //CTP RESET
const int GT911_INT =   2;   //CTP  INT



unsigned long captouchbounce = 0;//like button bounce for touch
int captouched = 0;//1 means there has been a touch
int captouchx = 0;
int captouchy = 0;
int lastpixeltouched = 1000;//keeps track of the last touched pixel to prevent excess bounce

unsigned char  GTP_CFG_DATA[] =
{
  0x5A, 0x20, 0x03, 0xE0, 0x01, 0x05, 0x0D, 0x00,
  0x01, 0x08, 0x28, 0x08, 0x50, 0x32, 0x03, 0x05,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x88, 0x29, 0x0A, 0x35, 0x37,
  0xD3, 0x07, 0x00, 0x00, 0x01, 0x81, 0x02, 0x1D,
  0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x64, 0x32,
  0x00, 0x00, 0x00, 0x28, 0x5A, 0x94, 0xC5, 0x02,
  0x00, 0x00, 0x00, 0x00, 0x98, 0x2B, 0x00, 0x84,
  0x33, 0x00, 0x74, 0x3C, 0x00, 0x67, 0x46, 0x00,
  0x5C, 0x53, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
  0x12, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
  0x04, 0x06, 0x08, 0x0F, 0x10, 0x12, 0x16, 0x18,
  0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x25, 0x01
};

struct TouchLocation
{
  uint16_t x;
  uint16_t y;
};



TouchLocation touchLocations[5];

void inttostr(uint16_t value, uint8_t *str);
uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len);
void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt);
uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len );
uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num );
uint32_t dist(const TouchLocation & loc);
uint32_t dist(const TouchLocation & loc1, const TouchLocation & loc2);
bool sameLoc( const TouchLocation & loc, const TouchLocation & loc2 );

uint8_t buf[80];

void gt911setup() {

  delay(300);
  pinMode(GT911_RESET, OUTPUT);
  pinMode     (GT911_INT, OUTPUT);
  digitalWrite(GT911_RESET, LOW);
  delay(20);
  digitalWrite(GT911_INT, LOW);
  delay(50);
  digitalWrite(GT911_RESET, HIGH);
  delay(100);
  pinMode     (GT911_RESET, INPUT);
  delay(100);
  uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  pinMode(GT911_RESET, OUTPUT);
  pinMode     (GT911_INT, OUTPUT);
  digitalWrite(GT911_RESET, LOW);
  delay(20);
  digitalWrite(GT911_INT, LOW);
  delay(50);
  digitalWrite(GT911_RESET, HIGH);
  delay(100);
  pinMode     (GT911_INT, INPUT);
  delay(100);
  re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  uint8_t bb[2];
  readGT911TouchAddr(0x8047, bb, 2);
  while (bb[1] != 32) {
    Serial.println("Capacitive touch screen initialized failure");
    pinMode(GT911_RESET, OUTPUT);
    pinMode     (GT911_INT, OUTPUT);
    digitalWrite(GT911_RESET, LOW);
    delay(20);
    digitalWrite(GT911_INT, LOW);
    delay(50);
    digitalWrite(GT911_RESET, HIGH);
    delay(100);
    pinMode     (GT911_INT, INPUT);
    delay(100);
    uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  }
  Serial.println("Capacitive touch screen  initialized success");

}

void scani2c() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for (address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error == 4)
    {
      Serial.print("Unknown error at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
}



uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len)
{
  uint8_t retry = 0;
  for (retry = 0; retry < 5; retry++)
  {
    writeGT911TouchRegister(0x8047, buf, cfg_len);
    //if(ret==0)break;
    delay(10);
  }
  return retry;
}

void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt)
{
  uint16_t i = 0;

  Wire.beginTransmission(addr);
  Wire.write( regAddr >> 8 ); // register 0
  Wire.write( regAddr);  // register 0
  for (i = 0; i < cnt; i++, val++) //data
  {
    Wire.write( *val );  // value
  }
  uint8_t retVal = Wire.endTransmission();

}

uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len )
{
  uint8_t i;
  uint8_t returned;
  uint8_t retVal;
  Wire.beginTransmission(addr);
  Wire.write( regAddr >> 8 ); // register 0
  Wire.write( regAddr);  // register 0
  retVal = Wire.endTransmission();
  
  returned = Wire.requestFrom(addr, len);    // request 1 bytes from slave device #2
  for (i = 0; (i < len) && Wire.available(); i++)

  {
    pBuf[i] = Wire.read();
  }
   return i;
}




uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num )
{
  uint8_t retVal;
  uint8_t i;
  uint8_t k;
  uint8_t  ss[1];

  do
  {
    
    if (!pLoc) break; // must have a buffer
    if (!num)  break; // must be able to take at least one
    ss[0] = 0;
    
    readGT911TouchAddr( 0x814e, ss, 1);
    uint8_t status = ss[0];
    
    
    if ((status & 0x0f) == 0) break; // no points detected
    uint8_t hitPoints = status & 0x0f;
      //Serial.print("number of hit points = ");
     //  Serial.println( hitPoints );
    //uint8_t tbuf[32];//original code, buffer is too small
    uint8_t tbuf[40];//changed to 40 as that is number called for in  readGT911TouchAddrTest( 0x8150, tbuf, 40); 
    uint8_t tbuf1[8];
    readGT911TouchAddr( 0x8150, tbuf, 40);
    readGT911TouchAddr( 0x8150 + 32, tbuf1, 8);

    for (k = 0, i = 0; (i <  4 * 8) && (k < num); k++, i += 8)
    {
      pLoc[k].x = tbuf[i + 1] << 8 | tbuf[i + 0];
      pLoc[k].y = tbuf[i + 3] << 8 | tbuf[i + 2];
    }
    pLoc[k].x = tbuf1[1] << 8 | tbuf1[0];
    pLoc[k].y = tbuf1[3] << 8 | tbuf1[2];
    
    retVal = hitPoints;
    
  } while (0);

  ss[0] = 0;
  writeGT911TouchRegister( 0x814e, ss, 1);
  
  delay(2);
  return retVal;
}

void checkfortouchscreen() {
  int q;
  captouched = 0;
  pinMode     (GT911_INT, INPUT);
  uint8_t st = digitalRead(GT911_INT);
  if (!st)  //Hardware touch interrupt
  {
    //Serial.println("Touch: ");
    
    uint8_t count = readGT911TouchLocation( touchLocations, 5 );
    
       
    
    if (count > 0) {
      
      static TouchLocation caplastTouch = touchLocations[0];// only take single touch, not dealing with multitouch
      caplastTouch = touchLocations[0];
      if (millis() > captouchbounce) { //cuts out multitouch
        captouchbounce = millis() + 250;//debounce value, probably best to remove when trying to capture multi touches
        //as two fingers may touch at sligtly different timings.
        //left in to make the output easier to read.
        Serial.print("Touches: ");
        Serial.println(count);
        //works through the aray of x/y locations
        //If touch gestures are being worked out you would need to store these values and compare with the next set.
        //for gestures you will probably need to remove the debounce
        for(q=0;q < count;q++){
          captouched = 1;
          captouchx = touchLocations[q].x;
          captouchy = touchLocations[q].y;
          Serial.print(captouchx);
          Serial.print(" : ");
          Serial.println(captouchy);  
        }
        //only using first touch for now
        /*
        if (touchLocations[0].x > 0 && touchLocations[0].y > 0) { //only do somethng if there is an x and Y value
          captouched = 1;
          captouchx = touchLocations[0].x;
          captouchy = touchLocations[0].y;
          Serial.print(captouchx);
          Serial.print(" : ");
          Serial.println(captouchy);
         
        }
        */
      }
    }
    
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("GT911_ESP32v2 Multi Touch");

  Wire.begin();
  scani2c();

  gt911setup();

}

void loop() {
  checkfortouchscreen();

}

Example 3: GT911_ESP32v3.ino

Working with virtual buttons

I have created my buttons as shown in this photo.

GT911 virtual buttons

Click to Download code: GT911_ESP32v3.ino

 
/*  GT911_ESP32v3
     05/04/2024 bug fix
     Simple sketch showing how to use the GT911 as a control panel with virtual buttons.

     Screen > Pins Used ESP32 Dev Module

     RST > GPIO 15
     VCC > 3.3v
     GND > GND
     INT > GPIO 2
     SDA > SDA (GPIO 21)
     SCL > SCL (GPIO 22)




*/

#include "Wire.h"

//Screen address for GT911
uint8_t addr  = 0x5d;  //CTP IIC ADDRESS
//Pins
const int GT911_RESET = 15;   //CTP RESET
const int GT911_INT =   2;   //CTP  INT


//I have included debounce as it makes the output easier to read and means it is set up for those building model railway control panels
unsigned long captouchbounce = 0;//like button bounce for touch
int captouched = 0;//1 means there has been a touch
//X and Y positions that will be used in further programming
int captouchx = 0;
int captouchy = 0;
int lastpixeltouched = 1000;//keeps track of the last touched pixel to prevent excess bounce

int numButtons = 3;//Change this to the number of buttons you are using
int MyButtons[3][2] { //3 needs to match numButtons, x y
  {191, 331}, //cordinates found by using GT911_ESP32v1 and pressing on virtual button
  {398, 336},
  {550, 350}
};

//This is some data that is needed to configure the GT911 touch screen...not my work.
unsigned char  GTP_CFG_DATA[] =
{
  0x5A, 0x20, 0x03, 0xE0, 0x01, 0x05, 0x0D, 0x00,
  0x01, 0x08, 0x28, 0x08, 0x50, 0x32, 0x03, 0x05,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x88, 0x29, 0x0A, 0x35, 0x37,
  0xD3, 0x07, 0x00, 0x00, 0x01, 0x81, 0x02, 0x1D,
  0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x64, 0x32,
  0x00, 0x00, 0x00, 0x28, 0x5A, 0x94, 0xC5, 0x02,
  0x00, 0x00, 0x00, 0x00, 0x98, 0x2B, 0x00, 0x84,
  0x33, 0x00, 0x74, 0x3C, 0x00, 0x67, 0x46, 0x00,
  0x5C, 0x53, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
  0x12, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
  0x04, 0x06, 0x08, 0x0F, 0x10, 0x12, 0x16, 0x18,
  0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x25, 0x01
};

struct TouchLocation
{
  uint16_t x;
  uint16_t y;
};


//Various variables required for the GT911 screen

TouchLocation touchLocations[5];

void inttostr(uint16_t value, uint8_t *str);
uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len);
void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt);
uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len );
uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num );
uint32_t dist(const TouchLocation & loc);
uint32_t dist(const TouchLocation & loc1, const TouchLocation & loc2);
bool sameLoc( const TouchLocation & loc, const TouchLocation & loc2 );
uint8_t buf[80];

//Function to initialise the GT911 touch screen
void gt911setup() {

  delay(300);
  pinMode(GT911_RESET, OUTPUT);
  pinMode     (GT911_INT, OUTPUT);
  digitalWrite(GT911_RESET, LOW);
  delay(20);
  digitalWrite(GT911_INT, LOW);
  delay(50);
  digitalWrite(GT911_RESET, HIGH);
  delay(100);
  pinMode     (GT911_RESET, INPUT);
  delay(100);
  uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  pinMode(GT911_RESET, OUTPUT);
  pinMode     (GT911_INT, OUTPUT);
  digitalWrite(GT911_RESET, LOW);
  delay(20);
  digitalWrite(GT911_INT, LOW);
  delay(50);
  digitalWrite(GT911_RESET, HIGH);
  delay(100);
  pinMode     (GT911_INT, INPUT);
  delay(100);
  re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  uint8_t bb[2];
  readGT911TouchAddr(0x8047, bb, 2);
  while (bb[1] != 32) {
    Serial.println("Capacitive touch screen initialized failure");
    pinMode(GT911_RESET, OUTPUT);
    pinMode     (GT911_INT, OUTPUT);
    digitalWrite(GT911_RESET, LOW);
    delay(20);
    digitalWrite(GT911_INT, LOW);
    delay(50);
    digitalWrite(GT911_RESET, HIGH);
    delay(100);
    pinMode     (GT911_INT, INPUT);
    delay(100);
    uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
  }
  Serial.println("Capacitive touch screen  initialized success");

}

//Not required, just in sketch to test connections
void scani2c() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for (address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error == 4)
    {
      Serial.print("Unknown error at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
}



uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len)
{
  uint8_t retry = 0;
  for (retry = 0; retry < 5; retry++)
  {
    writeGT911TouchRegister(0x8047, buf, cfg_len);
    //if(ret==0)break;
    delay(10);
  }
  return retry;
}

//function that writes to the GT911...do not edit
void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt)
{
  uint16_t i = 0;

  Wire.beginTransmission(addr);
  Wire.write( regAddr >> 8 ); // register 0
  Wire.write( regAddr);  // register 0
  for (i = 0; i < cnt; i++, val++) //data
  {
    Wire.write( *val );  // value
  }
  uint8_t retVal = Wire.endTransmission();

}

//function that reads from the GT911...do not edit
uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len )
{
  uint8_t i;
  uint8_t returned;
  uint8_t retVal;
  Wire.beginTransmission(addr);
  Wire.write( regAddr >> 8 ); // register 0
  Wire.write( regAddr);  // register 0
  retVal = Wire.endTransmission();

  returned = Wire.requestFrom(addr, len);    // request 1 bytes from slave device #2
  for (i = 0; (i < len) && Wire.available(); i++)

  {
    pBuf[i] = Wire.read();
  }
  return i;
}



////function that works out the touch coordinates for GT911...do not edit
uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num )
{
  uint8_t retVal;
  uint8_t i;
  uint8_t k;
  uint8_t  ss[1];
  do
  {
    if (!pLoc) break; // must have a buffer
    if (!num)  break; // must be able to take at least one
    ss[0] = 0;

    readGT911TouchAddr( 0x814e, ss, 1);
    uint8_t status = ss[0];

    if ((status & 0x0f) == 0) break; // no points detected
    uint8_t hitPoints = status & 0x0f;
    //Serial.print("number of hit points = ");
    //  Serial.println( hitPoints );

    uint8_t tbuf[40];//changed to 40 as that is number called for in  readGT911TouchAddrTest( 0x8150, tbuf, 40);
    uint8_t tbuf1[8];
    readGT911TouchAddr( 0x8150, tbuf, 40);
    readGT911TouchAddr( 0x8150 + 32, tbuf1, 8);

    for (k = 0, i = 0; (i <  4 * 8) && (k < num); k++, i += 8)
    {
      pLoc[k].x = tbuf[i + 1] << 8 | tbuf[i + 0];
      pLoc[k].y = tbuf[i + 3] << 8 | tbuf[i + 2];
    }
    pLoc[k].x = tbuf1[1] << 8 | tbuf1[0];
    pLoc[k].y = tbuf1[3] << 8 | tbuf1[2];

    retVal = hitPoints;

  } while (0);

  ss[0] = 0;
  writeGT911TouchRegister( 0x814e, ss, 1);

  delay(2);
  return retVal;
}


//Function that uses the functions above to give a simple X/Y position to be used in your code
void checkfortouchscreen() {

  captouched = 0;
  pinMode     (GT911_INT, INPUT);
  uint8_t st = digitalRead(GT911_INT);
  if (!st)  //Hardware touch interrupt
  {
    //Serial.println("Touch: ");

    //This line gives the number of touch points
    //Screen can deal with 5 touches at once
    uint8_t count = readGT911TouchLocation( touchLocations, 5 );
    
      if(count > 0){
      Serial.print("Touches: ");
      Serial.println(count);
      }
    

    if (count) {
      static TouchLocation caplastTouch = touchLocations[0];// only take single touch, not dealing with multitouch
      caplastTouch = touchLocations[0];
      if (millis() > captouchbounce) { //cuts out multitouch
        captouchbounce = millis() + 250;//250 is the debounce, lower makes more sensitive but more false touches

        //only using first touch for now
        if (touchLocations[0].x > 0 && touchLocations[0].y > 0) { //only do something if there is an x and Y value
          captouched = 1;
          captouchx = touchLocations[0].x;
          captouchy = touchLocations[0].y;
          //X and Y positions sent to serial monitor
          Serial.print(captouchx);
          Serial.print(" : ");
          Serial.println(captouchy);
          ProcessButton(captouchx, captouchy); //function to see if a button has been pressed
        }
      }
    }
  }
}

//function that checks if a button has been touched
void ProcessButton(int myx,int myy) {
  int q;
  //my screen is 153mm wide and 1011 touch points wide (x far left to x far right)
  //This works out at about 6.6 points per mm.
  //I allow 5mm around the centre touch point so approx 33
  int touchError = 33;
  for (q = 0; q < numButtons; q++) {
    //work through the array to see if a button has been touched
    if (myx > (MyButtons[q][0] - touchError) && myx < (MyButtons[q][0] + touchError) && myy > (MyButtons[q][1] - touchError) && myy < (MyButtons[q][1] + touchError)) {
      buttonAction(q);
    }
  }

}

//function that creates actions depending on the button pressed
//This could change an LED, send a DCC instruction etc.
void buttonAction(int q) {
  switch (q) {
    case 0:
      Serial.println("button A pressed");
      break;
    case 1:
      Serial.println("button B pressed");
      break;
    case 2:
      Serial.println("button C pressed");
      break;
    default:
      break;
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("GT911_ESP32v1");

  Wire.begin();

  scani2c();//Not required, just in sketch to test connections

  //set up the screen
  gt911setup();

}

void loop() {
  checkfortouchscreen();//keep looking for screen touches

}

Additional Resource Links

Comments


This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site.
To add a comment or ask a question please email the address in this image: and use ESP32 with GT911 Capacitive Touch Screen as a reference.