Successfully got the touch feature on the Lilygo T-Display-S3 Touch to work

I recently bought a Lilygo T-Display-S3 Touch module. It’s neat, it’s pretty, and it’ll be fun to play with.

I made a couple of screens in SquareLine, exported the project and UI files (for Arduino), and swapped out the LVGL and TFT_eSPI folders with the ones from the device’s GitHub. I compiled the UI files, and the screen worked. AWESOME! However, the touch did not.

I did a lot of searching to get the touch feature to work with SquareLine, and there was nada. There wasn’t a lot of support for getting touch to work on this module in general (aside from using TFT_eSPI sprites and TouchLib). I then tried to see if I could make TouchLib play nice with Squareline, but the core libraries are just too different.

I then searched CST816 (the touch chipset) and LVGL, and I found one forum where someone said they got them to play nice. Though it was for a different module, I was able to apply it to the T-Display_S3 and make it work. So without further ado, here is how to get Lilygo’s T-Display-S3 touch features to work with SquareLine.

  1. Follow the normal SquareLine export process, and replace the library files with the module’s libraries from the Lilygo GitHub (namely, the lvgl and TFT_eSPI folders and lv_conf.h in the main Library folder).
  2. Download the CST816S library from GitHub and add it to your project library. The CST816S library works for the CST816 chipset.
  3. Include the new library with the others at the top of the .ino file.
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <ui.h>
#include "CST816S.h" //Add this
  1. Declare your CST816S after the TFT_eSPI declaration:
TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight); /* TFT instance */
CST816S myTouch(18, 17, 21, 16); //Add this line
  1. Find the “my_touchpad_read” function in ui.ino and replace it with the following
void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
    uint16_t touchX = 0, touchY = 0;

  if (myTouch.available()) {
    touchX = myTouch.data.x;
    touchY = myTouch.data.y;
    data->state = LV_INDEV_STATE_PR;
  } else {
    data->state = LV_INDEV_STATE_REL;
   }

  /*Set the coordinates*/
  data->point.x = touchX;
  data->point.y = touchY;

//I added these conditions just to keep my serial monitor from going crazy
  if (touchX != 0) {
    Serial.print("Data x ");
    Serial.println(touchX);
  }

  if (touchY != 0) {
    Serial.print("Data y ");
    Serial.println(touchY);
  }
}
  1. In setup(), add the following after the TFT init stuff:
    tft.begin();          /* TFT init */
    tft.setRotation( 0 ); /* Landscape orientation, flipped */

    myTouch.begin(); //Add this after the TFT init
  1. The part of setup that initializes the (dummy) input device driver should be fine. Just ensure the indev.drv.read_cb part matches the aforementioned functions name:
    /*Initialize the (dummy) input device driver*/
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init( &indev_drv );
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read; //Since the function was my_touch_read(), this should equal my_touch_read.
    lv_indev_drv_register( &indev_drv );

And that should do it. Enjoy!

One issue I have now is that image buttons do not work right. I have a button set up with an image, and I set an event to change screens when long press repeat is set up. I know this function works because if I set the same trigger and action up for the screen, the action takes place. So it is the image buttons I am having trouble with. Any thoughts here?

Update 1: Alright, I fixed this by using a button instead and adding the images.

Update 2: Actually, the issue seems to be trigger object placement. Even my buttons stopped working if placed along the bottom. I moved them to 35px from the bottom and all worked fine.

#define TOUCH_MODULES_CST_SELF
#include <lvgl.h>
#include <TFT_eSPI.h>
#include “ui.h”
#include “TouchLib.h”
#include “Arduino.h”
#include “Wire.h”

#define TOUCH_READ_FROM_INTERRNUPT 0
/Change to your screen resolution/
static const uint16_t screenWidth = 320;
static const uint16_t screenHeight = 240;

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

TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight); /* TFT instance */
TouchLib myTouch(Wire, 18, 17, CTS820_SLAVE_ADDRESS, 21);
//Add this line

#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(const char *buf) {
Serial.printf(buf);
Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, 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.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();

lv_disp_flush_ready(disp);
}

/Read the touchpad/
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {
uint16_t touchX = 0, touchY = 0;

if (myTouch.read()) {
TP_Point t = myTouch.getPoint(0);
touchY = (180- t.x);
touchX = (t.y);
data->state = LV_INDEV_STATE_PR;
} else {
data->state = LV_INDEV_STATE_REL;
}

/Set the coordinates/
data->point.x = touchX;
data->point.y = touchY;

if (touchX != 0) {
Serial.print("Data x ");
Serial.println(touchX);
}

if (touchY != 0) {
Serial.print("Data y ");
Serial.println(touchY);
}
}

void setup() {
Serial.begin(115200); /* prepare for possible serial debug /
pinMode(21, OUTPUT);
digitalWrite(21, LOW);
delay(500);
digitalWrite(21, HIGH);
tft.init();
tft.begin(); /
TFT init /
tft.setRotation(1); /
Landscape orientation, flipped */
Wire.begin(18, 17);
scan_i2c_device(Wire);
myTouch.init();
//myTouch.begin(); //Add this after the TFT init

String LVGL_Arduino = "Hello Arduino! ";
LVGL_Arduino += String(‘V’) + lv_version_major() + “.” + lv_version_minor() + “.” + lv_version_patch();

Serial.println(LVGL_Arduino);
Serial.println(“I am LVGL_Arduino”);

lv_init();

#if LV_USE_LOG != 0
lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

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

/Initialize the display/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
/Change the following line to your display resolution/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);

/Initialize the (dummy) input device driver/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);

ui_init();

Serial.println(“Setup done”);
}

void loop() {
lv_timer_handler(); /* let the GUI do its work */
delay(5);
}

void scan_i2c_device(TwoWire &i2c)
{
Serial.println(“Scanning for I2C devices …”);
Serial.print(" “);
for (int i = 0; i < 0x10; i++)
{
Serial.printf(“0x%02X|”, i);
}
uint8_t error;
for (int j = 0; j < 0x80; j += 0x10)
{
Serial.println();
Serial.printf(“0x%02X |”, j);
for (int i = 0; i < 0x10; i++)
{
Wire.beginTransmission(i | j);
error = Wire.endTransmission();
if (error == 0)
Serial.printf(“0x%02X|”, i | j);
else
Serial.print(” – |");
}
}
Serial.println();
}

Oh, nice for the TouchLib. I will give this a run soon just to see how the performance is compared to the driver library. Thanks!

Hey there, created an account to say thanks for typing this all up. Would have taken me ages to get it working. I did run into one problem, so I wanted to share. If anybody else is running down this path too and noticed the rotation for touch input isn’t rotated, I added this section to translate the coordinates:

touchX = myTouch.data.x;
    touchY = myTouch.data.y;
    // Adjust the touch coordinates based on the TFT rotation
    uint8_t rotation = tft.getRotation();
    if (rotation == 1)
    {
      std::swap(touchX, touchY);
      touchY = tft.height() - touchY;
    }
    else if (rotation == 2)
    {
      touchX = tft.width() - touchX;
      touchY = tft.height() - touchY;
    }
    else if (rotation == 3)
    {
      std::swap(touchX, touchY);
      touchX = tft.width() - touchX;
    }

    data->state = LV_INDEV_STATE_PR;

    /*Set the coordinates*/
    data->point.x = touchX;
    data->point.y = touchY;

Awesome! I’m glad it was helpful

2 Likes