ESP32 + Adafruit FT6206 Cap Touch - touchpoint mismatch


I am trying to get LVGL running with my ESP32S3 along with the 2.8 capacitive touchscreen from Adafruit: Adafruit Capacitive Touchscreen. Since this screen uses the FT6206 driver, I have modified the LVGL_Arduino.ino file to take into account the touchpoints from the Adafruit FT6206 driver. So far, I have tried running some very simple examples. As referenced in the below code, I have tried running the lv_example_btn_1. The screen renders perfectly. I am getting touch accurately mapped to my display, however, it seems like the coordinates for the rendered buttons don’t align. i.e. I touch a button centered on the screen, my logging shows correct touch at ~160, 120 but I don’t get the button click event. It seems as thought the touch event coordinates are located elsewhere on the screen. Fairly new with lvgl and display drivers in general but hoping someone may have seen/fixed this in the past.

What MCU/Processor/Board and compiler are you using?

ESP32-S3-DevKitC-1-N32R8V with Adafruit Cap Touch ILI9341 with FT6206

What do you want to achieve?

I want the the click event touchpoints to be mapped to the rendered button on screen so that when I touch the button (or whatever element on screen), it recognizes it.

What have you tried so far?

Messing around with screen rotation settings, button offsets in the arduino widget examples.

Code to reproduce

Add the relevant code snippets here.

The code block(s) should be between ```c and ``` tags:

#include "/Users/Documents/Arduino/libraries/lvgl/examples/lv_examples.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "lvgl.h"
#include <TFT_eSPI.h>
#include <Adafruit_FT6206.h>

/*Change to your screen resolution*/
/*Change to your screen resolution*/
static const uint16_t screenWidth  = 320;
static const uint16_t screenHeight = 240;

static lv_disp_draw_buf_t disp_buf;
static lv_color_t buf[ screenWidth * screenHeight / 10 ];

// The FT6206 uses hardware I2C (SCL/SDA)
Adafruit_FT6206 touch = Adafruit_FT6206();
TFT_eSPI tft = TFT_eSPI(screenWidth,screenHeight); /* TFT instance */

void my_disp_flush( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p )
    uint32_t w = ( area->x2 - area->x1 + 1 );
    uint32_t h = ( area->y2 - area->y1 + 1 );

    tft.setAddrWindow( area->x1, area->y1, w, h );
    tft.pushColors( ( uint16_t * )&color_p->full, w * h, true );

    lv_disp_flush_ready( disp_drv );

void my_touchpad_read(lv_indev_drv_t * drv, lv_indev_data_t * data)
    bool touched = touch.touched();
    if (touched)
        TS_Point point = touch.getPoint();

        // Print raw touch coordinates
        Serial.print("Raw X: ");
        Serial.print(", Y: ");

        // Map the touch coordinates to match the screen resolution and orientation
        uint16_t touchX = point.x;
        uint16_t touchY = point.y;
        //uint16_t touchX = map(point.x, 0, 320, 0, 320);
        //uint16_t touchY = map(point.y, 0, 240, 0, 240);

        data->point.x = touchX;
        data->point.y = touchY;

        Serial.print("Touched at X: ");
        Serial.print(", Y: ");

    if (!touched)
        data->state = LV_INDEV_STATE_REL;
        data->state = LV_INDEV_STATE_PR;

void setup()
  Serial.begin(115200); /* prepare for possible serial debug */

  if (!touch.begin(40)) {  // pass in 'sensitivity' coefficient
    Serial.println("Couldn't start FT6206 touchscreen controller");
  } else {
    Serial.println("FT6206 touchscreen controller CONNECTED!");

  lv_disp_draw_buf_init(&disp_buf, buf, NULL, screenWidth*10);
  //lv_disp_draw_buf_init(&disp_buf, buf, NULL, screenWidth * screenHeight / 10);

  static lv_disp_drv_t disp_drv;          /*A variable to hold the drivers. Must be static or global.*/
  lv_disp_drv_init(&disp_drv);            /*Basic initialization*/
  disp_drv.draw_buf = &disp_buf;          /*Set an initialized buffer*/
  disp_drv.flush_cb = my_disp_flush;        /*Set a flush callback to draw to the display*/
  disp_drv.hor_res = screenWidth;                 /*Set the horizontal resolution in pixels*/
  disp_drv.ver_res = screenHeight;                 /*Set the vertical resolution in pixels*/
  lv_disp_t * disp;
  disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/

  static lv_indev_drv_t indev_drv;
  lv_indev_drv_init(&indev_drv);      /*Basic initialization*/
  indev_drv.type = LV_INDEV_TYPE_POINTER;                 /*See below.*/
  indev_drv.read_cb = my_touchpad_read;              /*See below.*/
  /*Register the driver in LVGL and save the created input device object*/
  lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);
//Check vertical and horizontal resolution of what lvgl sees
  lv_coord_t hor_res = lv_disp_get_hor_res(NULL);
  Serial.print("LVGL Horizontal Resolution: ");

  //Check vertical and horizontal resolution of what lvgl sees
  lv_coord_t ver_res = lv_disp_get_ver_res(NULL);
  Serial.print("LVGL Vertical Resolution: ");

  lv_coord_t disp_dpi = lv_disp_get_dpi(NULL);
  Serial.print("LVGL Display DPI: ");


void loop()
    lv_task_handler(); /* let the GUI do its work */
    delay( 5 );

Screenshot and/or video

the pictures below show where the touch events are getting recognized for both the btn_1 and btn_2 examples:

You may be a few steps ahead of me… but I stumbled across this while trying to get LVGL to work with Wokwi. With some success thanks to your example.

Anyway, for my desired rotation, I got the touch inputs working using the following mapping. Note the switched X and Y.

    uint16_t touchY = map(point.x, 0, 240, 0, 240);
    uint16_t touchX = map(point.y, 0, 320, 320, 0);

It was easier to figure out with LVGL logging turned on.