XPT2046 touch calibration problems with ESP32 and ILI9341 display (coordinates out of phase)

Hi everyone,
I'm working with an ESP32 and a 2.8" TFT display with an ILI9341 chip and an XPT2046 touch screen.
I use Arduino IDE and the following libraries:

  • Adafruit_GFX
  • Adafruit_ILI9341
  • XPT2046_Touchscreen

The display works correctly, but the touch screen is having trouble calibrating:

  • It only detects touch on one part of the screen (a sort of parallelogram);
  • The further I move to the right, the more the point drawn moves relative to my finger;

Here are my connections:
VCC = 3.3V GND = GND TFT_LED = 3.3V TFT_CS = 15 TFT_DC = 2 TFT_RST = 4 TFT_MOSI = 23 TFT_CLK = 18 TFT_MISO = 19 TOUCH_CS = 13 (TOUCH_CLK=18, TOUCH_DIN=23, TOUCH_DO=19 share SPI pins)

And this is the code for the program in the video:

#include "SPI.h"

#include "Adafruit_GFX.h"

#include "Adafruit_ILI9341.h"

#include "XPT2046_Touchscreen.h"

#include "Math.h"

// For the Adafruit shield, these are the default.

#define TFT_CS 15

#define TFT_DC 2

#define TFT_MOSI 23

#define TFT_CLK 18

#define TFT_RST 4

#define TFT_MISO 19

#define TS_CS 13

#define ROTATION 3

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC/RST

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

XPT2046_Touchscreen ts(TS_CS);

// calibration values

float xCalM = 0.0, yCalM = 0.0; // gradients

float xCalC = 0.0, yCalC = 0.0; // y axis crossing points

int8_t blockWidth = 20; // block size

int8_t blockHeight = 20;

int16_t blockX = 0, blockY = 0; // block position (pixels)

class ScreenPoint {

public:

int16_t x;

int16_t y;

// default constructor

ScreenPoint(){

}

ScreenPoint(int16_t xIn, int16_t yIn){

x = xIn;

y = yIn;

}

};

ScreenPoint getScreenCoords(int16_t x, int16_t y){

int16_t xCoord = round((x * xCalM) + xCalC);

int16_t yCoord = round((y * yCalM) + yCalC);

if(xCoord < 0) xCoord = 0;

if(xCoord >= tft.width()) xCoord = tft.width() - 1;

if(yCoord < 0) yCoord = 0;

if(yCoord >= tft.height()) yCoord = tft.height() - 1;

return(ScreenPoint(xCoord, yCoord));

}

void calibrateTouchScreen(){

TS_Point p;

int16_t x1,y1,x2,y2;

tft.fillScreen(ILI9341_BLACK);

// wait for no touch

while(ts.touched());

tft.drawFastHLine(10,20,20,ILI9341_RED);

tft.drawFastVLine(20,10,20,ILI9341_RED);

while(!ts.touched());

delay(50);

p = ts.getPoint();

x1 = p.x;

y1 = p.y;

tft.drawFastHLine(10,20,20,ILI9341_BLACK);

tft.drawFastVLine(20,10,20,ILI9341_BLACK);

delay(500);

while(ts.touched());

tft.drawFastHLine(tft.width() - 30,tft.height() - 20,20,ILI9341_RED);

tft.drawFastVLine(tft.width() - 20,tft.height() - 30,20,ILI9341_RED);

while(!ts.touched());

delay(50);

p = ts.getPoint();

x2 = p.x;

y2 = p.y;

tft.drawFastHLine(tft.width() - 30,tft.height() - 20,20,ILI9341_BLACK);

tft.drawFastVLine(tft.width() - 20,tft.height() - 30,20,ILI9341_BLACK);

int16_t xDist = tft.width() - 40;

int16_t yDist = tft.height() - 40;

// translate in form pos = m x val + c

// x

xCalM = (float)xDist / (float)(x2 - x1);

xCalC = 20.0 - ((float)x1 * xCalM);

// y

yCalM = (float)yDist / (float)(y2 - y1);

yCalC = 20.0 - ((float)y1 * yCalM);

Serial.print("x1 = ");Serial.print(x1);

Serial.print(", y1 = ");Serial.print(y1);

Serial.print("x2 = ");Serial.print(x2);

Serial.print(", y2 = ");Serial.println(y2);

Serial.print("xCalM = ");Serial.print(xCalM);

Serial.print(", xCalC = ");Serial.print(xCalC);

Serial.print("yCalM = ");Serial.print(yCalM);

Serial.print(", yCalC = ");Serial.println(yCalC);

}

void setup() {

Serial.begin(9600);

// avoid chip select contention

pinMode(TS_CS, OUTPUT);

digitalWrite(TS_CS, HIGH);

pinMode(TFT_CS, OUTPUT);

digitalWrite(TFT_CS, HIGH);

tft.begin();

tft.setRotation(ROTATION);

tft.fillScreen(ILI9341_BLACK);

ts.begin();

ts.setRotation(ROTATION);

calibrateTouchScreen();

}

void moveBlock(){

int16_t newBlockX, newBlockY;

ScreenPoint sp = ScreenPoint();

if (ts.touched()) {

TS_Point p = ts.getPoint();

sp = getScreenCoords(p.x, p.y);

newBlockX = sp.x - (blockWidth / 2);

newBlockY = sp.y - (blockHeight / 2);

if (newBlockX < 0) newBlockX = 0;

if (newBlockX >= (tft.width() - blockWidth)) newBlockX = tft.width() - 1 - blockWidth;

if (newBlockY < 0) newBlockY = 0;

if (newBlockY >= (tft.height() - blockHeight)) newBlockY = tft.height() - 1 - blockHeight;

}

if ((abs(newBlockX - blockX) > 2) || (abs(newBlockY - blockY) > 2)){

tft.fillRect(blockX, blockY, blockWidth, blockHeight,ILI9341_BLACK);

blockX = newBlockX;

blockY = newBlockY;

tft.fillRect(blockX, blockY, blockWidth, blockHeight,ILI9341_RED);

}

}

unsigned long lastFrame = millis();

void loop(void) {

// limit frame rate

while((millis() - lastFrame) < 20);

lastFrame = millis();

if (ts.touched()) {

moveBlock();

}

}

Photo and Video:

If you leave the rotation of both the screen and touchscreen at 0, does it work then?

I ended up rewriting both the analog and XPT2046 touchscreen libraries because I was dissatisfied with what I found in the wild.

Please read the pinned post re 'How to get the most from the forum'. That will teach you how to post code and any error logs as well as any wiring diagrams.

Hi, thanks for your reply!

I tried setting both the screen and touchscreen rotation to 0, and it definitely improved the behavior, now the touch roughly follows the movement on screen.
However, there’s still a noticeable distortion: instead of matching perfectly, the drawn points form a sort of parallelogram.

Interesting. I've had a similar experience with some low quality touch screens with 2.8" TFT SPI 240*320 V1.2 boards and I ended up hacking this library: GitHub - PaulStoffregen/XPT2046_Touchscreen: Touchscreen Arduino Library for XPT2046 Touch Controller Chip

I added this "incident" to library incase it helps others: Erratic touchscreen performance for some samples fixed by reducing the SPI bus speed defined in the XPT2046_Touchscreen library. · Issue #60 · PaulStoffregen/XPT2046_Touchscreen · GitHub

1 Like

Thanks a lot , that completely solved the issue!

After reading your note, I edited the "XPT2046_Touchscreen.cpp" file and changed this line:
#define SPI_SETTING SPISettings(2000000, MSBFIRST, SPI_MODE0)
to:
#define SPI_SETTING SPISettings(1000000, MSBFIRST, SPI_MODE0)

Recompiled, uploaded, and now the touchscreen is stable and accurate across the entire display.

Thanks again for sharing this fix!

1 Like