Interfacing Waveshare 2.8 Inch Capacitive Touch LCD with Arduino

Learn how to interface the beautiful 2.8 inch capacitive touch LCD panel from Waveshare to Arduino microcontroller boards.

Interfacing Waveshare 2.8 Inch Capacitive Touch LCD with Arduino Featured Image CIRCUITSTATE Electronics

When blinking LEDs are not enough to display information, we switch to LCDs. In fact you are staring at one right now. They are so ubiquitous and there are so many types of them. Have you ever wondered how you can control one yourself? If you have worked with Arduino boards before, we assume you have. We recently got our hands on a beautiful 2.8 Inch Capacitive Touch LCD screen from Waveshare. It is a bit costlier than the typical non-name LCDs you can buy from the market, but Waveshare products have their standards when it comes to quality. This time also, it is not different. The LCD comes with 320 x 240 TFT panel with rich colors and large viewing angles, and an integrated capacitive touch panel. The PCB has two types of connectors and four metallic standoff mounting points. This makes it extremely easy to interface with any of your microcontroller boards and even integrate into your product designs. In this post, we will take a closer look at the LCD and learn how to interface it with your favourite Arduino-supported microcontroller boards.

Features

  • 240 × 320 resolution TFT panel with 8-bit, 262K colors.
  • Capacitive touch panel with 5 point simultaneous touch tracking.
  • ST7789T3 LCD driver and fast SPI port for interfacing with external microcontroller.
  • CST328 touch panel controller and fast I2C interface.
  • Dual 13-pin JST-GH and 18-pin FFC for easy interconnection. Cables provided.
  • Onboard voltage translator for both 3.3V and 5V signal interfaces.
  • Four metal standoff mounting points.

The Waveshare 2.8 inch capacitive touch LCD with SKU ID 27579 has a 320 x 240 TFT (Thin-Film Transistor) panel. This is a regular TFT panel with 8-bit color support and therefore 262 thousand colors . This is not best compared to IPS (In-Plane Switching) or OLED. But for most use cases, this panel will work just fine. In fact, the TFT used in the Waveshare display module is significantly better at color reproduction and viewing angles compared to the very cheap ones. The 2.8 inch (5.28 cm) indicates the diagonal size of the display. Check the dimensional drawing of the module for other dimensions.

The display panel is driven by the ST7789T3 driver from Sitronix. This controller supports parallel, RGB and SPI interfaces. The display module breaks out only the SPI port. The touch panel is a separate thing glued on top of the TFT panel. A capacitive touch panel is a better alternative to the inferior resistive touch panels. A capacitive panel does not bend or flex when touched and is much accurate. The touch panel on the Waveshare display module is controlled by CST328 from Hynitron Microelectronics. This controller has an I2C interface. The touch panel can detect and track up to 5 touches simultaneously.

The display comes with a 13-pin JST-GH to 2.54 mm pin sockets (female) and one 18-pin, 0.5 mm pitch and 200 mm long FPC/FFC cable (same sided). The display can be directly connected to Waveshare’s own ESP32 boards using the FFC. One JST-GH cable, and one 15 cm, 18-pin same-sided FFC are also included in the product package.

Waveshare 2.8 Inch Capacitive Touch LCD Module Cables by CIRCUITSTATE Electronics
13-pin JST-GH and 18-pin FFC cables.

The display can be powered by either a 3.3V or 5V supply. There is an internal 3.3V regulator (ME6217C33M5G) that converts the VCC input to 3.3V. Additionally, the display has a voltage translator (TXS0108EPWR) that converts 3.3V/5V interface signals to 3.3V bidirectionally. This means, you can interface the display with both 3.3V and 5v microcontroller boards without worries. Exceeding the specified voltages will damage your display permanently. You can buy the Waveshare 2.8 inch capacitive LCD module from places like Robu for INR 1600 if you are in India.

Specifications

SpecificationValue
Operating Voltage3.3V / 5V
Display DriverST7789T3
Display Interface4-wire SPI
Display PanelTFT
Pixel Pitch0.18 × 0.18 mm
Display Size43.20 × 57.60 mm
Resolution240 × 320 pixels
Touch DriverCST328
Touch InterfaceI2C
Touch TypeCapacitive
Touch Points5-point touch
Module Size50.54 × 73.06 mm

Dimensions

Waveshare 2.8 Inch Capacitive Touch LCD Dimensions
Waveshare 2.8 inch capacitive touch LCD module dimensions

Schematic

Waveshare has provided the schematic for the display module. This makes it easy to find the pinouts and design your project around it. The main voltage regulator is ME6217C33M5G with an input voltage range of 2~6.5V and maximum output current of 800 mA. NDC7002N is a dual N-Channel Mosfet for reset controls. TXS0108EPWR is an 8-channel bidirectional voltage translator. This converts 3.3V signals from and to 5V signals.

Waveshare 2.8 Inch Capacitive Touch LCD Schematic

Pinout

Waveshare 2.8 Inch Capacitive Touch LCD Pinout
LCD module pinout diagram. Unlabelled pins are not connected.
SignalDescription13-Pin JST #18-Pin FFC #
VCCPower (3.3V / 5V input)11
GNDGround23
MISOSPI MISO pin36
MOSISPI MOSI pin45
SCLKSPI CLK pin54
LCD_CSLCD Chip Selection, low active69
LCD_DCLCD Data/Command selection (high for data, low for command)77
LCD_RSTLCD Reset pin, low active88
LCD_BLLCD Backlight pin92
TP_SDATouch panel Data pin1014
TP_SCLTouch panel Clock pin1113
TP_INTTouch panel Interrupt pin1215
TP_RSTTouch panel Reset pin, low active1312

The unused pins of the FFC are not shown in the table. You can leave them unconnected.

Wiring

There are two connectors on the display module; a 13-pin JST-GH and 18-pin FFC connector. Both of them can be used to interface the display with external microcontroller boards. You can use the cables found in the product box for this. The JST-GH cable has 2.54 mm header sockets (female) on the other end. This means you can plug the wires into any boards with similar 2.54 mm header pins. If you have header sockets on the board instead, you will have to use extra jumper cables. We also suggest to keep the wire lengths as short as possible. The JST-GH connector has a locking mechanism which prevents it from getting accidentally disconnected. Make sure you unlock it before removing it. The following image shows how we connected the display module with a FireBeetle-ESP32E board from DFRobot.

The FFC is the easiest to use since it is thin and flexible. A 15 cm cable is already provided in the package. You can use this cable to connect the display to development boards from Waveshare. We accidentally found out that the GDI (General Display Interface) of the FireBeetle-ESP32E is similar to the FFC on the Waveshare display module. When we checked the pinouts of both, they were identical as you can see from the table below. Bothe connectors have 18 pins and 0.5 mm pitch.

LCD Pin #LCD SignalFireBeetle Pin #FireBeetle Signal
1VCC183.3V
2LCD_BL17IO12/D13
3GND16GND
4LCD_SCLK15IO18/SCK
5LCD_MOSI14IO23/MOSI
6LCD_MISO13IO19/MISO
7LCD_DC12IO25/D2
8LCD_RST11IO26/D3
9LCD_CS10IO14/D6
10x9IO13/D7
11x8IO0/D5
12TP_RST7IO4/D12
13TP_SCL6IO22/SCL
14TP_SDA5IO21/SDA
15TP_INT4IO16/D11
16x3IO17/D10
17x2IO1
18x1IO2

This means you can directly connect the Waveshare display module with the FireBeetle-ESP32E board. There is one small problem though. The pin ordering of the GDI connector are in reverse order. That means we can not use the provided same-sided cable to connect the display. Therefore we purchased new 18-pin, 0.5 mm pitch, 100 mm long FCC cables with reverse-sided faces from Mangalam Electronics, Mumbai. As expected, the cable worked perfectly. The following images shows the orientation of the cable.

Waveshare 2.8 Inch Capacitive Touch LCD Module 18-Pin Reverse Face FFC Cable by CIRCUITSTATE Electronics
18-pin, 0.5 mm pitch, reverse faced FFC cable in 100 mm length.

Libraries

We will take a look at all of the libraries we are going to be using in our example interface codes. You have to make sure you install and configure all of these libraries before you can try out the example codes provided here.

TFT_eSPI

TFT_eSPI is a popular Arduino and PlatformIO IDE compatible LCD graphics library optimised for the RP2040, STM32, ESP8266 and ESP32 microcontrollers. The library is highly customizable and supports many different types LCD drivers and interfaces. The open-source library is written and published by Bodmer. Following is the list of interfaces supported.

Processor4 wire SPI8-bit parallel16-bit parallelDMA support
RP2040YesYesYesYes (all)
ESP32YesYesNoYes (SPI only)
ESP32 C3YesNoNoNo
ESP32 S2YesNoNoNo
ESP32 S3YesYesNoYes (SPI only)
ESP8266YesNoNoNo
STM32FxxxYesYesNoYes (SPI only)
OtherYesNoNoNo

Following is a list of supported LCD controllers.

  • GC9A01
  • ILI9163
  • ILI9225
  • ILI9341
  • ILI9342
  • ILI9481 (DMA not supported with SPI)
  • ILI9486 (DMA not supported with SPI)
  • ILI9488 (DMA not supported with SPI)
  • HX8357B (16-bit parallel tested with RP2040)
  • HX8357C (16-bit parallel tested with RP2040)
  • HX8357D
  • R61581
  • RM68120 (support files added but untested)
  • RM68140
  • S6D02A1
  • SSD1351
  • SSD1963 (this controller only has a parallel interface option)
  • ST7735
  • ST7789
  • ST7796

We will use the ESP32 processor target and the 4-wire SPI port for interfacing the display module with the FireBeetle-ESP32E board. But you are free to use any other supported microcontrollers and the respective wiring.

CSE_CST328

68747470733a2f2f736f6369616c6966792e6769742e63692f4349524355495453544154452f4353455f4353543332382f696d6167653f6465736372697074696f6e3d3126666f6e743d4b6f486f26666f726b733d31266973737565733d31266c6f676f3d68747470732533412532462532467777772e6369726375697473746174652e636f6d25324677702d636f6e74656e7425324675706c6f6164732532463230323425324630352532464349524355495453544154452d522d456d626c656d2d32303035323032342d322e737667266e616d653d31267061747465726e3d43697263756974253230426f6172642670756c6c733d31267374617267617a6572733d31267468656d653d4175746f

Since the TFT_eSPI library does not support all types of touch controllers, we had to develop a new library for the CST328 touch controller. The CSE_CST328 is a touch controller library from CIRCUITSTATE Electronics. Using this library, you can easily interface any touch panels with CST328 controller with any Arduino-compatible boards. You can use this library in conjunction with the TFT_eSPI library.

CSE_Touch

68747470733a2f2f736f6369616c6966792e6769742e63692f4349524355495453544154452f4353455f546f7563682f696d6167653f6465736372697074696f6e3d3126666f6e743d4b6f486f26666f726b733d31266973737565733d31266c6f676f3d68747470732533412532462532467777772e6369726375697473746174652e636f6d25324677702d636f6e74656e7425324675706c6f6164732532463230323425324630352532464349524355495453544154452d522d456d626c656d2d32303035323032342d322e737667266e616d653d31267061747465726e3d43697263756974253230426f6172642670756c6c733d31267374617267617a6572733d31267468656d653d4175746f

CSE_Touch is a unified touch support library from CIRCUITSTATE Electronics. This unifies multiple touch drivers into a single software interface that you can use in your applications. The advantage of this is that you can change the touch driver without changing your main application code, allowing you to use different types of touch panels. The CSE_CST328 library is already supported by CSE_Touch.

CSE_UI

68747470733a2f2f736f6369616c6966792e6769742e63692f4349524355495453544154452f4353455f55492f696d6167653f6465736372697074696f6e3d3126666f6e743d4b6f486f26666f726b733d31266973737565733d31266c6f676f3d68747470732533412532462532467777772e6369726375697473746174652e636f6d25324677702d636f6e74656e7425324675706c6f6164732532463230323425324630352532464349524355495453544154452d522d456d626c656d2d32303035323032342d322e737667266e616d653d31267061747465726e3d43697263756974253230426f6172642670756c6c733d31267374617267617a6572733d31267468656d653d4175746f

CSE_UI is yet another open-source Arduino library from CIRCUITSTATE Electronics. This is a Graphical User Interface (GUI) library built on top of the TFT_eSPI library. That means you can run the CSE_UI applications on any displays that are supported by TFT_eSPI library. The touch functions will be provided by the CSE_Touch library. Following are the main features of the CSE_UI library.

Arduino Code

To demonstrate the capabilities of the display module and our libraries, we have created a PlatformIO project for the Arduino platform. As said earlier, we are using the FireBeetle-ESP32E board from DFRobot. Since this is a PlatformIO project, you can easily change the target platform and the target microcontroller board. We have three examples present in this single project. We have uploaded the project to our GitHub.

To compile and upload an example, choose the PlatformIO environment configuration from the bottom bar of Visual Studio Code (VS Code). If you are new to VS Code and PlatformIO, we have great tutorials to help you get started.

Following is the main platformio.ini configuration file.

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

;------------------------------------------------------------------------------------;

; PlatformIO default environment
[platformio]
default_envs = LCD_Test

;------------------------------------------------------------------------------------;

; Common settings shared between environments

[env]
platform = espressif32
board = dfrobot_firebeetle2_esp32e
framework = arduino

; upload_protocol = esp-prog
debug_tool = esp-prog

lib_deps = bodmer/TFT_eSPI @ ^2.5.30

build_flags =
  -D USER_SETUP_LOADED=1
  -D ST7789_DRIVER=1
  -D TFT_INVERSION_ON=0
  -D TFT_RGB_ORDER=0
  -D TFT_WIDTH=240
  -D TFT_HEIGHT=320
  -D LOAD_GLCD=1
  -D LOAD_FONT2=1
  -D LOAD_FONT4=1
  -D LOAD_FONT6=1
  -D LOAD_FONT7=1
  -D LOAD_GFXFF=1
  -D SMOOTH_FONT=1
  -D SPI_FREQUENCY=80000000
  -D SPI_READ_FREQUENCY=2500000

  ; Pinouts for the FFC
  -D TFT_CS=14
  -D TFT_DC=25
  -D TFT_RST=26
  -D TFT_MOSI=23
  -D TFT_MISO=19
  -D TFT_SCLK=18
  -D TFT_BL=12
  ; -D TFT_TOUCH_SDA=21
  ; -D TFT_TOUCH_SCL=22
  ; -D TFT_TOUCH_INT=16

  ; ; Pinouts for the Molex connector
  ; -D TFT_CS=17
  ; -D TFT_DC=25
  ; -D TFT_RST=26
  ; -D TFT_MOSI=23
  ; -D TFT_MISO=19
  ; -D TFT_SCLK=18
  ; -D TFT_BL=2
  ; ; -D TFT_TOUCH_SDA=21
  ; ; -D TFT_TOUCH_SCL=22
  ; ; -D TFT_TOUCH_INT=25

;------------------------------------------------------------------------------------;

; For testing the LCD

[env:LCD_Test]
build_src_filter = +<LCD_Test/>

lib_deps =
  ${env.lib_deps}  ; Inherit common dependencies
  ; CSE_MillisTimer=symlink://D:\Code\Arduino\libraries\CSE_MillisTimer

;------------------------------------------------------------------------------------;

; For testing the touch panel

[env:Touch_Test]

build_src_filter = +<Touch_Test/>

lib_deps =
  ${env.lib_deps}  ; Inherit common dependencies
  CSE_CST328=symlink://D:/Code/Arduino/libraries/CSE_CST328
  CSE_Touch=symlink://D:/Code/Arduino/libraries/CSE_Touch

build_flags =
  ${env.build_flags}
  -D ENABLE_CST328=1

;------------------------------------------------------------------------------------;

; For testing UI using CSE_UI library

[env:UI_Test]

build_src_filter = +<UI_Test/>

lib_deps =
  ${env.lib_deps}  ; Inherit common dependencies
  CSE_UI=symlink://D:\Code\Arduino\libraries\CSE_UI
  CSE_Touch=symlink://D:/Code/Arduino/libraries/CSE_Touch
  CSE_CST328=symlink://D:/Code/Arduino/libraries/CSE_CST328
  CSE_MillisTimer=symlink://D:\Code\Arduino\libraries\CSE_MillisTimer

build_flags =
  ${env.build_flags}
  -D ENABLE_CST328=1

;------------------------------------------------------------------------------------;
platformio.ini

LCD Test

Waveshare 2.8 Inch Capacitive Touch LCD Module LCD Test Example by CIRCUITSTATE Electronics
LCD_Test example

This example tests the basic functionalities of the LCD module and prints information about on the serial monitor. You can find this example under the LCD_Test environment. Following is the main code.


//============================================================================================//
/*
  Filename: main.cpp [Waveshare-2.8-Inch-LCD -> LCD_Test]
  Description: Main source file for the LCD_Test example.
  Framework: Arduino, PlatformIO
  Author: Vishnu Mohanan (@vishnumaiea, @vizmohanan)
  Maintainer: CIRCUITSTATE Electronics (@circuitstate)
  Version: 0.1
  License: MIT
  Source: https://github.com/CIRCUITSTATE/CSE_CST328
  Last Modified: +05:30 17:48:12 PM 20-02-2025, Thursday
 */
//============================================================================================//

#include <SPI.h>
#include <TFT_eSPI.h> // TFT LCD library
#include <Wire.h>

//============================================================================================//
// Globals

TFT_eSPI LCD = TFT_eSPI(); // Create a new TFT driver instance

// The library defines the type "setup_t" as a struct.
// Calling LCD.getSetup(user) populates it with the settings.
setup_t user;

//============================================================================================//
// Forward Declarations

void setup (void);
void loop (void);
void printDriverInfo (void);
void printProcessorName (void);
int8_t getPinName (int8_t pin);
bool initWire (void);
bool initLCD (void);

//============================================================================================//
/**
 * @brief Setup runs once.
 * 
 */
void setup() {
  Serial.begin (115200);
  delay (1000);

  Serial.println();
  Serial.println (F("=== Waveshare 2.8 Inch LCD - LCD Test ==="));

  // Initialize everything.
  initWire();
  initLCD();

  Serial.println (F("setup [INFO]: System initialization complete."));
  Serial.println();

  delay (1000);
}

//============================================================================================//
/**
 * @brief Infinite loop.
 * 
 */
void loop() {
  LCD.fillScreen (TFT_BLACK);
  LCD.setTextSize (2);
  LCD.setTextColor (TFT_YELLOW);
  LCD.setCursor (20, 50);
  LCD.print ("Hello World");

  printDriverInfo();

  delay (1000);
}

//============================================================================================//
/**
 * @brief Initialize the I2C/Wire interface.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initWire (void) {
  Serial.print (F("initWire [INFO]: Initializing I2C.. "));
  Wire.begin();
  // Wire.setSCL (PIN_I2C_SCL);
  // Wire.setSDA (PIN_I2C_SDA);
  // Wire.setClock (100000); // 100 kHz
  // Wire.begin (I2C_PERIPHERAL_ADDR); // Initialize I2C as peripheral
  
  Serial.println (F("Done."));

  return true;
}

//============================================================================================//
/**
 * @brief Initializes the LCD.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initLCD (void) {
  Serial.print (F("initLCD [INFO]: Initializing LCD.. "));

  pinMode (TFT_BL, OUTPUT);
  digitalWrite (TFT_BL, LOW); // Turn off the backlight
  
  LCD.begin();
  LCD.setRotation (3);
  LCD.setFreeFont();
  LCD.fillScreen (TFT_BLACK); // Clear the screen
  delay (50);
  
  Serial.println (F("Done."));
  Serial.println (F("initLCD [INFO]: You should see \"Hello World\" printed in yellow color on the screen."));
  
  digitalWrite (TFT_BL, HIGH); // Turn on the backlight

  return true;
}

//============================================================================================//
/**
 * @brief Prints the TFT_eSPI driver information.
 * 
 */
void printDriverInfo (void) {
  LCD.getSetup (user); //

  Serial.print ("\n[code]\n");

  Serial.print ("TFT_eSPI ver = "); Serial.println(user.version);
  printProcessorName();
  #if defined (ESP32) || defined (ESP8266)
    if (user.esp < 0x32F000 || user.esp > 0x32FFFF) { Serial.print("Frequency    = "); Serial.print(ESP.getCpuFreqMHz());Serial.println("MHz"); }
  #endif
  #ifdef ESP8266
    Serial.print("Voltage      = "); Serial.print(ESP.getVcc() / 918.0); Serial.println("V"); // 918 empirically determined
  #endif
  Serial.print("Transactions = "); Serial.println((user.trans  ==  1) ? "Yes" : "No");
  Serial.print("Interface    = "); Serial.println((user.serial ==  1) ? "SPI" : "Parallel");
  #ifdef ESP8266
  if (user.serial ==  1){ Serial.print("SPI overlap  = "); Serial.println((user.overlap == 1) ? "Yes\n" : "No\n"); }
  #endif
  if (user.tft_driver != 0xE9D) // For ePaper displays the size is defined in the sketch
  {
    Serial.print("Display driver = "); Serial.println(user.tft_driver, HEX); // Hexadecimal code
    Serial.print("Display width  = "); Serial.println(user.tft_width);  // Rotation 0 width and height
    Serial.print("Display height = "); Serial.println(user.tft_height);
    Serial.println();
  }
  else if (user.tft_driver == 0xE9D) Serial.println("Display driver = ePaper\n");

  if (user.r0_x_offset  != 0)  { Serial.print("R0 x offset = "); Serial.println(user.r0_x_offset); } // Offsets, not all used yet
  if (user.r0_y_offset  != 0)  { Serial.print("R0 y offset = "); Serial.println(user.r0_y_offset); }
  if (user.r1_x_offset  != 0)  { Serial.print("R1 x offset = "); Serial.println(user.r1_x_offset); }
  if (user.r1_y_offset  != 0)  { Serial.print("R1 y offset = "); Serial.println(user.r1_y_offset); }
  if (user.r2_x_offset  != 0)  { Serial.print("R2 x offset = "); Serial.println(user.r2_x_offset); }
  if (user.r2_y_offset  != 0)  { Serial.print("R2 y offset = "); Serial.println(user.r2_y_offset); }
  if (user.r3_x_offset  != 0)  { Serial.print("R3 x offset = "); Serial.println(user.r3_x_offset); }
  if (user.r3_y_offset  != 0)  { Serial.print("R3 y offset = "); Serial.println(user.r3_y_offset); }

  if (user.pin_tft_mosi != -1) { Serial.print("MOSI    = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_mosi)); }
  if (user.pin_tft_miso != -1) { Serial.print("MISO    = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_miso)); }
  if (user.pin_tft_clk  != -1) { Serial.print("SCK     = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_clk)); }

  #ifdef ESP8266
  if (user.overlap == true)
  {
    Serial.println("Overlap selected, following pins MUST be used:");

                              Serial.println("MOSI     = SD1 (GPIO 8)");
                              Serial.println("MISO     = SD0 (GPIO 7)");
                              Serial.println("SCK      = CLK (GPIO 6)");
                              Serial.println("TFT_CS   = D3  (GPIO 0)\n");

    Serial.println("TFT_DC and TFT_RST pins can be user defined");
  }
  #endif
  String pinNameRef = "GPIO ";
  #ifdef ESP8266
    pinNameRef = "PIN_D";
  #endif

  if (user.esp == 0x32F) {
    Serial.println("\n>>>>> Note: STM32 pin references above D15 may not reflect board markings <<<<<");
    pinNameRef = "D";
  }
  if (user.pin_tft_cs != -1) { Serial.print("TFT_CS   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_cs)); }
  if (user.pin_tft_dc != -1) { Serial.print("TFT_DC   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_dc)); }
  if (user.pin_tft_rst!= -1) { Serial.print("TFT_RST  = " + pinNameRef); Serial.println(getPinName(user.pin_tft_rst)); }

  if (user.pin_tch_cs != -1) { Serial.print("TOUCH_CS = " + pinNameRef); Serial.println(getPinName(user.pin_tch_cs)); }

  if (user.pin_tft_wr != -1) { Serial.print("TFT_WR   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_wr)); }
  if (user.pin_tft_rd != -1) { Serial.print("TFT_RD   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_rd)); }

  if (user.pin_tft_d0 != -1) { Serial.print("\nTFT_D0   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d0)); }
  if (user.pin_tft_d1 != -1) { Serial.print("TFT_D1   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d1)); }
  if (user.pin_tft_d2 != -1) { Serial.print("TFT_D2   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d2)); }
  if (user.pin_tft_d3 != -1) { Serial.print("TFT_D3   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d3)); }
  if (user.pin_tft_d4 != -1) { Serial.print("TFT_D4   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d4)); }
  if (user.pin_tft_d5 != -1) { Serial.print("TFT_D5   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d5)); }
  if (user.pin_tft_d6 != -1) { Serial.print("TFT_D6   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d6)); }
  if (user.pin_tft_d7 != -1) { Serial.print("TFT_D7   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d7)); }

  #if defined (TFT_BL)
    Serial.print("\nTFT_BL           = " + pinNameRef); Serial.println(getPinName(user.pin_tft_led));
    #if defined (TFT_BACKLIGHT_ON)
      Serial.print("TFT_BACKLIGHT_ON = "); Serial.println(user.pin_tft_led_on == HIGH ? "HIGH" : "LOW");
    #endif
  #endif

  Serial.println();

  uint16_t fonts = LCD.fontsLoaded();
  if (fonts & (1 << 1))        Serial.print("Font GLCD   loaded\n");
  if (fonts & (1 << 2))        Serial.print("Font 2      loaded\n");
  if (fonts & (1 << 4))        Serial.print("Font 4      loaded\n");
  if (fonts & (1 << 6))        Serial.print("Font 6      loaded\n");
  if (fonts & (1 << 7))        Serial.print("Font 7      loaded\n");
  if (fonts & (1 << 9))        Serial.print("Font 8N     loaded\n");
  else
  if (fonts & (1 << 8))        Serial.print("Font 8      loaded\n");
  if (fonts & (1 << 15))       Serial.print("Smooth font enabled\n");
  Serial.print("\n");

  if (user.serial==1)        { Serial.print("Display SPI frequency = "); Serial.println(user.tft_spi_freq/10.0); }
  if (user.pin_tch_cs != -1) { Serial.print("Touch SPI frequency   = "); Serial.println(user.tch_spi_freq/10.0); }

  Serial.println("[/code]");

  delay(3000);
}

//============================================================================================//
/**
 * @brief Gets the name of the processor.
 * 
 */
void printProcessorName (void) {
  Serial.print("Processor    = ");
  if ( user.esp == 0x8266) Serial.println("ESP8266");
  if ( user.esp == 0x32)   Serial.println("ESP32");
  if ( user.esp == 0x32F)  Serial.println("STM32");
  if ( user.esp == 0x2040) Serial.println("RP2040");
  if ( user.esp == 0x0000) Serial.println("Generic");
}

//============================================================================================//
/**
 * @brief Gets the names of the pins used by the processor.
 * 
 * @param pin 
 * @return int8_t 
 */
int8_t getPinName (int8_t pin) {
  // For ESP32 and RP2040 pin labels on boards use the GPIO number
  if (user.esp == 0x32 || user.esp == 0x2040) return pin;

  if (user.esp == 0x8266) {
    // For ESP8266 the pin labels are not the same as the GPIO number
    // These are for the NodeMCU pin definitions:
    //        GPIO       Dxx
    if (pin == 16) return 0;
    if (pin ==  5) return 1;
    if (pin ==  4) return 2;
    if (pin ==  0) return 3;
    if (pin ==  2) return 4;
    if (pin == 14) return 5;
    if (pin == 12) return 6;
    if (pin == 13) return 7;
    if (pin == 15) return 8;
    if (pin ==  3) return 9;
    if (pin ==  1) return 10;
    if (pin ==  9) return 11;
    if (pin == 10) return 12;
  }

  if (user.esp == 0x32F) return pin;

  return -1; // Invalid pin
}

//============================================================================================//
main.cpp

After choosing the target, you can compile and upload the code to your microcontroller board. Following is the compilation and upload log.

 *  Executing task: C:\PIO\penv\Scripts\platformio.exe run --target upload --environment LCD_Test 


Processing LCD_Test (platform: espressif32; board: dfrobot_firebeetle2_esp32e; framework: arduino)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/dfrobot_firebeetle2_esp32e.html
PLATFORM: Espressif 32 (6.10.0) > DFRobot Firebeetle 2 ESP32-E
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (esp-prog) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES: 
 - framework-arduinoespressif32 @ 3.20017.241212+sha.dcc1105b 
 - tool-esptoolpy @ 1.40501.0 (4.5.1) 
 - tool-mkfatfs @ 2.0.1 
 - tool-mklittlefs @ 1.203.210628 (2.3) 
 - tool-mkspiffs @ 2.230.0 (2.30) 
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 34 compatible libraries
Scanning dependencies...
Dependency Graph
|-- TFT_eSPI @ 2.5.43
|-- SPI @ 2.0.0
|-- Wire @ 2.0.0
Building in release mode
Compiling .pio\build\LCD_Test\src\LCD_Test\main.cpp.o
Building .pio\build\LCD_Test\bootloader.bin
Generating partitions .pio\build\LCD_Test\partitions.bin
esptool.py v4.5.1
Creating esp32 image...
Merged 1 ELF section
Successfully created esp32 image.
Compiling .pio\build\LCD_Test\lib2bb\SPI\SPI.cpp.o
Compiling .pio\build\LCD_Test\liba1d\FS\FS.cpp.o
Compiling .pio\build\LCD_Test\liba1d\FS\vfs_api.cpp.o
Compiling .pio\build\LCD_Test\libb83\SPIFFS\SPIFFS.cpp.o
Compiling .pio\build\LCD_Test\lib194\LittleFS\LittleFS.cpp.o
In file included from src/LCD_Test/main.cpp:17:
.pio/libdeps/LCD_Test/TFT_eSPI/TFT_eSPI.h:973:8: warning: #warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available! [-Wcpp]
       #warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available!
        ^~~~~~~
Compiling .pio\build\LCD_Test\lib94e\TFT_eSPI\TFT_eSPI.cpp.o
Compiling .pio\build\LCD_Test\lib265\Wire\Wire.cpp.o
Archiving .pio\build\LCD_Test\lib2bb\libSPI.a
Indexing .pio\build\LCD_Test\lib2bb\libSPI.a
Compiling .pio\build\LCD_Test\FrameworkArduino\Esp.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\FirmwareMSC.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\FunctionalInterrupt.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\HWCDC.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\HardwareSerial.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\IPAddress.cpp.o
Archiving .pio\build\LCD_Test\liba1d\libFS.a
Compiling .pio\build\LCD_Test\FrameworkArduino\IPv6Address.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\MD5Builder.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\Print.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\Stream.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\StreamString.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\Tone.cpp.o
Indexing .pio\build\LCD_Test\liba1d\libFS.a
Compiling .pio\build\LCD_Test\FrameworkArduino\USB.cpp.o
Archiving .pio\build\LCD_Test\libb83\libSPIFFS.a
Compiling .pio\build\LCD_Test\FrameworkArduino\USBCDC.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\USBMSC.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\WMath.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\WString.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\base64.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\cbuf.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-adc.c.o
Indexing .pio\build\LCD_Test\libb83\libSPIFFS.a
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-bt.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-cpu.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-dac.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-gpio.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-i2c-slave.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-i2c.c.o
In file included from .pio/libdeps/LCD_Test/TFT_eSPI/TFT_eSPI.cpp:16:
.pio/libdeps/LCD_Test/TFT_eSPI/TFT_eSPI.h:973:8: warning: #warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available! [-Wcpp]
       #warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available!
        ^~~~~~~
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-ledc.c.o
Archiving .pio\build\LCD_Test\lib194\libLittleFS.a
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-matrix.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-misc.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-psram.c.o
Indexing .pio\build\LCD_Test\lib194\libLittleFS.a
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-rgb-led.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-rmt.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-sigmadelta.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-spi.c.o
Archiving .pio\build\LCD_Test\lib265\libWire.a
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-time.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-timer.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-tinyusb.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-touch.c.o
Indexing .pio\build\LCD_Test\lib265\libWire.a
Compiling .pio\build\LCD_Test\FrameworkArduino\esp32-hal-uart.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\firmware_msc_fat.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\libb64\cdecode.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\libb64\cencode.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\main.cpp.o
Compiling .pio\build\LCD_Test\FrameworkArduino\stdlib_noniso.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\wiring_pulse.c.o
Compiling .pio\build\LCD_Test\FrameworkArduino\wiring_shift.c.o
Archiving .pio\build\LCD_Test\libFrameworkArduino.a
Indexing .pio\build\LCD_Test\libFrameworkArduino.a
Archiving .pio\build\LCD_Test\lib94e\libTFT_eSPI.a
Indexing .pio\build\LCD_Test\lib94e\libTFT_eSPI.a
Linking .pio\build\LCD_Test\firmware.elf
Retrieving maximum program size .pio\build\LCD_Test\firmware.elf
Checking size .pio\build\LCD_Test\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]   6.8% (used 22284 bytes from 327680 bytes)
Flash: [===       ]  25.3% (used 332197 bytes from 1310720 bytes)
Building .pio\build\LCD_Test\firmware.bin
esptool.py v4.5.1
Creating esp32 image...
Merged 2 ELF sections
Successfully created esp32 image.
Configuring upload protocol...
AVAILABLE: cmsis-dap, esp-bridge, esp-prog, espota, esptool, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa
CURRENT: upload_protocol = esptool
Looking for upload port...
Auto-detected: COM8
Uploading .pio\build\LCD_Test\firmware.bin
esptool.py v4.5.1
Serial port COM8
Connecting.....
Chip is ESP32-D0WD-V3 (revision v3.0)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: e0:5a:1b:55:d1:bc
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Flash will be erased from 0x00001000 to 0x00005fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00010000 to 0x00061fff...
Compressed 17536 bytes to 12202...
Writing at 0x00001000... (100 %)
Wrote 17536 bytes (12202 compressed) at 0x00001000 in 0.6 seconds (effective 228.3 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 146...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.1 seconds (effective 355.0 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 47...
Writing at 0x0000e000... (100 %)
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 452.7 kbit/s)...
Hash of data verified.
Compressed 332560 bytes to 184137...
Writing at 0x00010000... (8 %)
Writing at 0x0001b092... (16 %)
Writing at 0x000294d1... (25 %)
Writing at 0x0002e83b... (33 %)
Writing at 0x000343b6... (41 %)
Writing at 0x00039930... (50 %)
Writing at 0x0003eddd... (58 %)
Writing at 0x000444da... (66 %)
Writing at 0x000499de... (75 %)
Writing at 0x00051bff... (83 %)
Writing at 0x0005a4d2... (91 %)
Writing at 0x0005fd46... (100 %)
Wrote 332560 bytes (184137 compressed) at 0x00010000 in 4.6 seconds (effective 572.8 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
======================================================================================== [SUCCESS] Took 14.30 seconds ========================================================================================

Environment    Status    Duration
-------------  --------  ------------
LCD_Test       SUCCESS   00:00:14.297
========================================================================================= 1 succeeded in 00:00:14.297 =========================================================================================
 *  Terminal will be reused by tasks, press any key to close it.
Terminal

If you open the serial monitor after uploading the code, you will see something like below printed to it. You will also see a “Hello World” message printed on the LCD in yellow color and black background. If you are not seeing it, then something is wrong. Check your wiring and pin configurations and try again.

ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13232
load:0x40080400,len:3028
entry 0x400805e4

=== Waveshare 2.8 Inch LCD - LCD Test ===
initWire [INFO]: Initializing I2C.. Done.
initLCD [INFO]: Initializing LCD.. Done.
initLCD [INFO]: You should see "Hello World" printed in yellow color on the screen.
setup [INFO]: System initialization complete.


[code]
TFT_eSPI ver = 2.5.43
Processor    = ESP32
Frequency    = 240MHz
Transactions = Yes
Interface    = SPI
Display driver = 7789
Display width  = 240
Display height = 320

MOSI    = GPIO 23
MISO    = GPIO 19
SCK     = GPIO 18
TFT_CS   = GPIO 14
TFT_DC   = GPIO 25
TFT_RST  = GPIO 26

TFT_BL           = GPIO 12

Font GLCD   loaded
Font 2      loaded
Font 4      loaded
Font 6      loaded
Font 7      loaded
Smooth font enabled

Display SPI frequency = 80.00
[/code]
Serial Monitor

Touch Test

Waveshare 2.8 Inch Capacitive Touch LCD Module Touch Test Example by CIRCUITSTATE Electronics
Touch_Test example showing crosshair where it is touched.

This example checks the touch functionality of the LCD module. It draws a yellow XY crosshair on the point you touch. A small red square is also drawn on the origin of the display. This will help you visually determine where the origin of the LCD is. If the LCD origin does not match the touch panel origin, you can try changing the rotations to one of the other three values. One of the rotations should work.

You can find this example under the Touch_Test environment. Following is the main code.


//============================================================================================//
/*
  Filename: main.cpp [Waveshare-2.8-Inch-LCD -> Touch_Test]
  Description: Main source file for the Touch_Test example.
  Framework: Arduino, PlatformIO
  Author: Vishnu Mohanan (@vishnumaiea, @vizmohanan)
  Maintainer: CIRCUITSTATE Electronics (@circuitstate)
  Version: 0.1
  License: MIT
  Source: https://github.com/CIRCUITSTATE/CSE_CST328
  Last Modified: +05:30 22:15:35 PM 10-03-2025, Monday
 */
//============================================================================================//

#include <SPI.h>
#include <TFT_eSPI.h> // TFT LCD library
#include <Wire.h>
#include <CSE_CST328.h>

//============================================================================================//
// Macros and constants.

#define  CST328_PIN_RST     4
#define  CST328_PIN_INT     16

#define  LCD_ROTATION       0   // The LCD panel rotation
#define  TS_ROTATION        0   // The touch panel rotation

//============================================================================================//
// Globals

TFT_eSPI LCD = TFT_eSPI(); // Create a new TFT driver instance

// Create a new instance of the CST328 class.
// Parameters: Width, Height, &Wire, Reset pin, Interrupt pin
CSE_CST328 tsPanel = CSE_CST328 (240, 320, &Wire, CST328_PIN_RST, CST328_PIN_INT);

bool intReceived = false; // Flag to indicate that an interrupt has been received
CSE_TouchPoint point;

//============================================================================================//
// Forward Declarations

void setup (void);
void loop (void);
void readTouch (void);
bool initWire (void);
bool initLCD (void);
bool initTouch (void);
void touchISR (void);
void drawCrosshair (void);

//============================================================================================//
/**
 * @brief Setup runs once.
 * 
 */
void setup() {
  Serial.begin (115200);
  delay (2000);

  Serial.println();
  Serial.println (F("=== Waveshare 2.8 Inch LCD - Touch Test ==="));

  // Initialize everything.
  initWire();
  initTouch();
  initLCD();

  Serial.println (F("setup [INFO]: System initialization complete."));
  Serial.println();

  delay (1000);
}

//============================================================================================//
/**
 * @brief Infinite loop.
 * 
 */
void loop() {
  if (intReceived) {
    tsPanel.fastReadData (0); // Read the touch point once.
    LCD.drawRect (0, 0, 10, 10, TFT_RED); // Draw a small rectangle to indicate the origin of the display and the touch panel.
    readTouch();  // Print touch point info.
    drawCrosshair();  // Draw a cross hair.
    intReceived = false;
    attachInterrupt (digitalPinToInterrupt (CST328_PIN_INT), touchISR, FALLING);
  }
}

//============================================================================================//
/**
 * @brief Initializes the touch panel.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initTouch (void) {
  Serial.print (F("initTouch [INFO]: Initializing touch panel.."));
  tsPanel.begin();
  tsPanel.setRotation (TS_ROTATION);

  // Attach the interrupt function.
  attachInterrupt (digitalPinToInterrupt (CST328_PIN_INT), touchISR, FALLING);
  
  Serial.println (F("Done."));

  return true;
}

//============================================================================================//
/**
 * @brief Initialize the I2C/Wire interface.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initWire (void) {
  Serial.print (F("initWire [INFO]: Initializing I2C.. "));
  Wire.begin();
  Serial.println (F("Done."));

  return true;
}

//============================================================================================//
/**
 * @brief Initializes the LCD.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initLCD (void) {
  Serial.print (F("initLCD [INFO]: Initializing LCD.. "));

  pinMode (TFT_BL, OUTPUT);
  digitalWrite (TFT_BL, LOW); // Turn off the backlight
  
  LCD.begin();
  LCD.setRotation (LCD_ROTATION);
  LCD.setFreeFont();
  LCD.fillScreen (TFT_BLACK); // Clear the screen
  delay (50);
  
  Serial.println (F("Done."));
  digitalWrite (TFT_BL, HIGH); // Turn on the backlight

  return true;
}

//============================================================================================//
/**
 * @brief Reads a single touch point from the panel and print their info to the serial monitor.
 * 
 */
void readTouch() {
  if (1) {
    uint8_t i = 0;
    Serial.print ("Touch ID: ");
    Serial.print (i);
    Serial.print (", X: ");
    Serial.print (tsPanel.getPoint (i).x);
    Serial.print (", Y: ");
    Serial.print (tsPanel.getPoint (i).y);
    Serial.print (", Z: ");
    Serial.print (tsPanel.getPoint (i).z);
    Serial.print (", State: ");
    Serial.print (tsPanel.getPoint (i).state);
    Serial.print (", W: ");
    Serial.print (tsPanel.getWidth());
    Serial.print (", H: ");
    Serial.println (tsPanel.getHeight());
  }
  else {
    Serial.println ("No touches detected");
  }
}

//============================================================================================//
/**
 * @brief The touch interrupt service routine.
 * 
 */
void touchISR() {
  // Detach the interrupt to prevent multiple interrupts
  detachInterrupt (digitalPinToInterrupt (CST328_PIN_INT));
  intReceived = true;
}

//============================================================================================//

void drawCrosshair() {
  static CSE_TouchPoint prevPoint; // Previous touch point (non-calibrated)

  // tsPanel.fastReadData (0);
  point = tsPanel.getPoint();

  // Return if the point is the same as the previous point, or invalid.
  if ((point.x == prevPoint.x) || (point.y == prevPoint.y) || (point.x == -1) || (point.y == -1)) {
    return;
  }

  // LCD.drawRect (prevPoint.x, prevPoint.y, 10, 10, TFT_BLACK);
  // LCD.drawRect (point.x, point.y, 10, 10, TFT_YELLOW);

  // Clear the previous cursor with background color
  LCD.drawLine (prevPoint.x, 0, prevPoint.x, tsPanel.height, TFT_BLACK);
  LCD.drawLine (0, prevPoint.y, tsPanel.width, prevPoint.y, TFT_BLACK);

  // Draw the new cursor with foreground color
  LCD.drawLine (point.x, 0, point.x, tsPanel.height, TFT_YELLOW);
  LCD.drawLine (0, point.y, tsPanel.width, point.y, TFT_YELLOW);

  // Save the points
  prevPoint = point;
}

//============================================================================================//
main.cpp

If you open the serial monitor after uploading the code, you will see messages printed like the following. Try touching the display and you will see new touch point information.

ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13232
load:0x40080400,len:3028
entry 0x400805e4

=== Waveshare 2.8 Inch LCD - Touch Test ===
initWire [INFO]: Initializing I2C.. Done.
initTouch [INFO]: Initializing touch panel..Done.
initLCD [INFO]: Initializing LCD.. Done.
setup [INFO]: System initialization complete.

Touch ID: 0, X: 115, Y: 151, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 115, Y: 151, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 115, Y: 151, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 115, Y: 151, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 115, Y: 151, Z: 0, State: 0, W: 240, H: 320
Touch ID: 0, X: 154, Y: 142, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 154, Y: 142, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 154, Y: 142, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 154, Y: 142, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 154, Y: 142, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 154, Y: 142, Z: 0, State: 0, W: 240, H: 320
Touch ID: 0, X: 97, Y: 216, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 97, Y: 216, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 97, Y: 216, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 97, Y: 216, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 97, Y: 216, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 97, Y: 216, Z: 0, State: 0, W: 240, H: 320
Touch ID: 0, X: 124, Y: 66, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 124, Y: 66, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 124, Y: 66, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 124, Y: 66, Z: 0, State: 1, W: 240, H: 320
Touch ID: 0, X: 124, Y: 66, Z: 0, State: 0, W: 240, H: 320
Serial Monitor

UI Test

This example demonstrates some of the capabilities of the CSE_UI library. You can find this example under the UI_Test environment. Following is the main code.


//============================================================================================//
/*
  Filename: main.cpp [Waveshare-2.8-Inch-LCD -> UI_Test]
  Description: Main source file for the UI_Test example.
  Framework: Arduino, PlatformIO
  Author: Vishnu Mohanan (@vishnumaiea, @vizmohanan)
  Maintainer: CIRCUITSTATE Electronics (@circuitstate)
  Version: 0.1
  License: MIT
  Source: https://github.com/CIRCUITSTATE/CSE_CST328
  Last Modified: +05:30 15:50:11 PM 01-03-2025, Saturday
 */
//============================================================================================//

#include <SPI.h>
#include <TFT_eSPI.h> // TFT LCD library
#include <Wire.h>
#include <CSE_CST328.h>
#include <CSE_UI.h>

// #include "NotoSans-Bold-15.h"
// #include "FiraSans-Regular-15.h"
#include "FiraSans-Medium-15.h"
// #include "Roboto-Regular-15.h"
#include "NotoSans-Bold-28.h"
#include "NotoSans-Bold-36.h"
// #include "forkawesome-36.h"
// #include "forkawesome-32.h"
#include "forkawesome-30.h"

// Include the header files that contain the icons
#include "Alert.h"
#include "Close.h"
#include "Info.h"

//============================================================================================//
// Macros and constants.

#define  CST328_PIN_RST     4
#define  CST328_PIN_INT     16

#define  LCD_ROTATION       3   // The LCD panel rotation
#define  TS_ROTATION        3   // The touch panel rotation

// Fonts
// #define   AA_FONT_SMALL               NotoSansBold15
// #define   AA_FONT_SMALL               FiraSansRegular15
#define   AA_FONT_SMALL               FiraSansMedium15
// #define   AA_FONT_SMALL               RobotoRegular15
#define   AA_FONT_LARGE               NotoSansBold36

//============================================================================================//
// Globals

TFT_eSPI LCD = TFT_eSPI(); // Create a new TFT driver instance

// Create a new instance of the CST328 class.
// Parameters: Width, Height, &Wire, Reset pin, Interrupt pin
CSE_Touch* tsPanel = CSE_Touch_Driver:: createDriver (CSE_TOUCH_CST328, 240, 320, &Wire, TFT_RST);

// UI objects.
CSE_UI myui (&LCD, tsPanel);

// Pages
pageClass home_Page (&myui, 0);

// Buttons
buttonClass home_mainTitle (&myui);
buttonClass home_timeDateString (&myui);
buttonClass home_Button_A (&myui);
buttonClass home_Button_B (&myui);
buttonClass home_Button_C (&myui);
buttonClass home_Button_D (&myui);
buttonClass home_oxygenLabel (&myui);
buttonClass home_oxygenValue (&myui);
buttonClass home_feLabel (&myui);
buttonClass home_feValue (&myui);
buttonClass home_fgtLabel (&myui);
buttonClass home_fgtValue (&myui);
buttonClass home_atLabel (&myui);
buttonClass home_atValue (&myui);

bool intReceived = false; // Flag to indicate that an interrupt has been received

//============================================================================================//
// Forward Declarations

void setup (void);
void loop (void);
void readTouch (void);
bool initWire (void);
bool initLCD (void);
bool initTouch (void);

void home_Page_Init (void);
void home_Page_Draw (void);

//============================================================================================//
/**
 * @brief Setup runs once.
 * 
 */
void setup() {
  Serial.begin (115200);
  delay (2000);

  Serial.println();
  Serial.println (F("=== Waveshare 2.8 Inch LCD - Touch Test ==="));

  // Initialize everything.
  initWire();
  initTouch();
  initLCD();

  Serial.println (F("setup [INFO]: System initialization complete."));
  Serial.println();

  delay (1000);
}

//============================================================================================//
/**
 * @brief Infinite loop.
 * 
 */
void loop() {
  // readTouch();
  home_Page.draw();

  delay (50);
}

//============================================================================================//
/**
 * @brief Initializes the touch panel.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initTouch (void) {
  Serial.print (F("initTouch [INFO]: Initializing touch panel.."));
  tsPanel->begin();
  tsPanel->setRotation (TS_ROTATION);

  // Attach the interrupt function.
  // attachInterrupt (digitalPinToInterrupt (CST328_PIN_INT), touchISR, FALLING);
  
  Serial.println (F("Done."));

  return true;
}

//============================================================================================//
/**
 * @brief Initialize the I2C/Wire interface.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initWire (void) {
  Serial.print (F("initWire [INFO]: Initializing I2C.. "));
  Wire.begin();
  Serial.println (F("Done."));

  return true;
}

//============================================================================================//
/**
 * @brief Initializes the LCD.
 * 
 * @return true Initialization successful.
 * @return false Initialization failed.
 */
bool initLCD (void) {
  Serial.print (F("initLCD [INFO]: Initializing LCD.. "));

  pinMode (TFT_BL, OUTPUT);
  digitalWrite (TFT_BL, LOW); // Turn off the backlight
  
  LCD.begin();
  LCD.setRotation (LCD_ROTATION);
  LCD.setFreeFont();
  LCD.fillScreen (TFT_BLACK); // Clear the screen
  delay (50);

  home_Page.setInitFunction (home_Page_Init);
  home_Page.setDrawFunction (home_Page_Draw);
  home_Page.init();
  home_Page.setActive();
  
  Serial.println (F("Done."));
  digitalWrite (TFT_BL, HIGH); // Turn on the backlight

  return true;
}

//============================================================================================//

void home_Page_Init() {
  home_mainTitle.initialize (10, 10, 235, 25, TFT_BLUE, TFT_WHITE, "AIR QUALITY MONITORING");
  home_mainTitle.radius = 0;
  home_mainTitle.labelOffsetY = 2;

  home_timeDateString.initialize (10, 205, 235, 25, TFT_DARKCYAN, TFT_WHITE, "08:24 PM, Wed, 24-06-2023");
  home_timeDateString.radius = 0;
  home_timeDateString.labelOffsetY = 2;

  home_oxygenLabel.initialize (10, 95, 110, 20, 0x9edf, TFT_BLACK, "PM 1.0 ug/m3");
  home_oxygenLabel.radius = 0;
  home_oxygenLabel.labelOffsetY = 2;

  home_oxygenValue.initialize (10, 45, 110, 50, TFT_BLUE, TFT_WHITE, "110");
  home_oxygenValue.radius = 0;
  home_oxygenValue.labelOffsetY = 2;

  home_feLabel.initialize (135, 95, 110, 20, 0xfea8, TFT_BLACK, "PM 2.5 ug/m3");
  home_feLabel.radius = 0;
  home_feLabel.labelOffsetY = 2;

  home_feValue.initialize (135, 45, 110, 50, TFT_BLUE, TFT_WHITE, "230");
  home_feValue.radius = 0;
  home_feValue.labelOffsetY = 2;

  home_fgtLabel.initialize (10, 175, 110, 20, 0xe61e, TFT_BLACK, "PM 10 ug/m3");
  home_fgtLabel.radius = 0;
  home_fgtLabel.labelOffsetY = 2;
  
  home_fgtValue.initialize (10, 125, 110, 50, TFT_BLUE, TFT_WHITE, "190");
  home_fgtValue.radius = 0;
  home_fgtValue.labelOffsetY = 2;

  home_atLabel.initialize (135, 175, 110, 20, TFT_GREEN, TFT_BLACK, "TEMP °C");
  home_atLabel.radius = 0;
  home_atLabel.labelOffsetY = 2;

  home_atValue.initialize (135, 125, 110, 50, TFT_BLUE, TFT_WHITE, "32.68");
  home_atValue.radius = 0;
  home_atValue.labelOffsetY = 2;

  home_Button_A.initialize (267, 10, 44, 40, TFT_BLUE, TFT_WHITE, "\uF05A");
  home_Button_B.initialize (267, 70, 44, 40, TFT_BLUE, TFT_WHITE, "\uF046");
  home_Button_C.initialize (267, 130, 44, 40, TFT_BLUE, TFT_WHITE, "\uF071");
  home_Button_D.initialize (267, 190, 44, 40, TFT_BLUE, TFT_WHITE, "\uF013");
}

//============================================================================================//

void home_Page_Draw() { 
  if (home_Page.isActive()) {
    if (myui.prevPage != home_Page.pageNum) {
      home_Page.uiParent->lcdParent->fillScreen (TFT_WHITE);
      myui.currentPage = home_Page.pageNum;
      myui.prevPage = home_Page.pageNum;
    }

    LCD.loadFont (AA_FONT_SMALL);
    home_timeDateString.draw();
    home_mainTitle.draw();
    home_oxygenLabel.draw();
    home_feLabel.draw();
    home_atLabel.draw();
    home_fgtLabel.draw();

    LCD.loadFont (NotoSansBold28);
    home_fgtValue.draw();

    LCD.loadFont (AA_FONT_LARGE);
    home_oxygenValue.draw();
    home_feValue.draw();
    home_atValue.draw();

    LCD.loadFont (forkawesome30);
    home_Button_A.draw();
    home_Button_B.draw();
    home_Button_C.draw();
    home_Button_D.draw();

    // bool buttonA = home_Button_A.isPressed();
    // bool buttonB = home_Button_B.isPressed();
    // bool buttonC = home_Button_C.isPressed();
    // bool buttonD = home_Button_D.isPressed();

    // if (buttonA || buttonB || buttonC || buttonD) {
    //   home_Page.setInactive();

    //   home_timeDateString.reset();

    //   home_mainTitle.reset();

    //   home_oxygenLabel.reset();
    //   home_feLabel.reset();
    //   home_atLabel.reset();
    //   home_fgtLabel.reset();
    //   home_fgtValue.reset();
    //   home_oxygenValue.reset();
    //   home_feValue.reset();
    //   home_atValue.reset();
      
    //   home_Button_A.reset();
    //   home_Button_B.reset();
    //   home_Button_C.reset();
    //   home_Button_D.reset();

    //   if (buttonA) {
    //     // dataAndInfo_Page.setActive();
    //   }

    //   else if (buttonB) {
    //     // tasks_Page.setActive();
    //   }

    //   else if (buttonC) {
    //     // errorsAndAlarms_Page.setActive();
    //   }

    //   else if (buttonD) {
    //     // settings_Page.setActive();
    //   }
    // }
  }
}

//============================================================================================//
/**
 * @brief Reads a single touch point from the panel and print their info to the serial monitor.
 * 
 */
void readTouch() {
  if (tsPanel->isTouched (0)) {
    uint8_t i = 0;
    Serial.print ("Touch ID: ");
    Serial.print (i);
    Serial.print (", X: ");
    Serial.print (tsPanel->getPoint (i).x);
    Serial.print (", Y: ");
    Serial.print (tsPanel->getPoint (i).y);
    Serial.print (", Z: ");
    Serial.print (tsPanel->getPoint (i).z);
    Serial.print (", State: ");
    Serial.print (tsPanel->getPoint (i).state);
    Serial.print (", W: ");
    Serial.print (tsPanel->getWidth());
    Serial.print (", H: ");
    Serial.println (tsPanel->getHeight());
  }
  // else {
  //   Serial.println ("No touches detected");
  // }
}

//============================================================================================//
main.cpp

After uploading the code, you will a UI mock up that will show multiple text boxes, labels and buttons. You can touch the buttons and they will change colors when you do. You can also attach different functionalities to the touches.

  1. Waveshare 2.8 Inch Capacitive Touch LCD – Product Page
  2. Waveshare 2.8 Inch Capacitive Touch LCD – Wiki
  3. Buy Waveshare 2.8 Inch Capacitive Touch LCD – Robu
  4. Buy Waveshare 2.8 Inch Capacitive Touch LCD – Hubtronics
  5. Mangalam Electronics – Mumbai – Buy FFC/FPC
  6. TFT_eSPI Arduino Library – GitHub
  7. CSE_CST328 Arduino Library – GitHub
  8. CSE_Touch Arduino Library – GitHub
  9. CSE_UI Arduino Library – GitHub
  10. Waveshare-2.8-Inch-LCD – PlatformIO Project Example – GitHub
  11. Buy FireBeetle-ESP32E – Robu
Share to your friends
Vishnu Mohanan

Vishnu Mohanan

Founder and CEO at CIRCUITSTATE Electronics

Articles: 98

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.