XPT2046 erratic behavior

I have an 320x240 TFT lcd (ILI9341) with XPT2046 touch driver. There is a weird problem with touch inconsistency. I have two large buttons, aligned vertically next to each other. Touch callbacks work ok most of the time. However sometimes when I touch the upper button, a callback from the lower button is invoked. There is no consistency about this behavior; I can get a lower button callback after previously getting an upper button callback and touching at the same place or even above the previously touched location, so I don’t think calibration is the problem. Any idea where to look, besides a hardware problem? LVGL version is 7.x and MCU is esp32. Thanks.

How do you read your touch?

Here is our code from our project, which works.

#include <XPT2046_Touchscreen.h>

// This is calibration data for the raw touch data to the screen coordinates
#define TS_MINX 150
#define TS_MINY 130
#define TS_MAXX 3800
#define TS_MAXY 4000

void initMyTouch() {

  if (!ts.begin()) {
    Serial.println("Couldn't start touchscreen controller");
  Serial.println("Touchscreen started");

bool touchpad_read(lv_indev_drv_t * drv, lv_indev_data_t*data)
  static int16_t last_x = 0;
  static int16_t last_y = 0;

  if (!ts.bufferEmpty() && ts.touched()) {
    TS_Point p = ts.getPoint();
    // Calculate position 1:1 with display size
    p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
    p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
    last_x = p.x;
    last_y = p.y;
    data->point.x = last_x;
    data->point.y = last_y;
    data->state = LV_INDEV_STATE_PR;
    Serial.print("Touch detected, ");
    Serial.print("("); Serial.print(last_x);
    Serial.print(", "); Serial.print(last_y);
    return false;

  data->point.x = last_x;
  data->point.y = last_y;
  data->state = LV_INDEV_STATE_REL;

  return false;

Thank you for sharing.

I am fairly new to lvgl, and I used lv_port_esp32 facilities;

during initial setup, I have

lv_indev_drv_t indev_drv;
indev_drv.read_cb = touch_driver_read;

bool touch_driver_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
return xpt2046_read(drv, data);

bool xpt2046_read(lv_indev_drv_t * drv, lv_indev_data_t * data)
static int16_t last_x = 0;
static int16_t last_y = 0;
bool valid = false;

int16_t x = last_x;
int16_t y = last_y;
if (xpt2048_is_touch_detected() == TOUCH_DETECTED)
    valid = true;

    x = xpt2046_cmd(CMD_X_READ);
    y = xpt2046_cmd(CMD_Y_READ);
    //ESP_LOGI(TAG, "P(%d,%d)", x, y);

    /*Normalize Data back to 12-bits*/
    x = x >> 4;
    y = y >> 4;
    //ESP_LOGI(TAG, "P_norm(%d,%d)", x, y);
    xpt2046_corr(&x, &y);
    xpt2046_avg(&x, &y);
    last_x = x;
    last_y = y;

    //ESP_LOGI(TAG, "x = %d, y = %d", x, y);
    avg_last = 0;

data->point.x = x;
data->point.y = y;
data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR;

return false;


Sorry, what platform are you using?

Code which I provided is for Arduino.

Just esp-idf for ESP32. Not using arduino libs. Maybe I should.

I traced it to

static int16_t xpt2046_cmd(uint8_t cmd)
uint8_t data[2];
tp_spi_read_reg(cmd, data, 2);
int16_t val = (data[0] << 8) | data[1];
return val;

returning 0 for the y-coordinate. This gets averaged later in the code, resulting in a wrong button detected.

The file is lv_port_esp32/components/lvgl_esp32_drivers/lvgl_touch/xpt2046.c.
This is on ESP32.

Any comments from lvgl developers? Thanks.

To be honest, resistive touchscreens and of course XPT2046 are not precise especially if you touch corners. First things first, check out the place returned by the touch routine in the log. Second, try not to use the 12 bits, but 10 bits or lesser. My experience tells me resistive touchscreens are not a good choice but for special cases. As you see, today mobile devices like cellphones, tablets, and so on aren’t using the resistive but the capacitive touchscreen. Otherwise, you need some filtering techniques to make the touchscreen output as precise as you need.

Thank you All for the replies.