ESP32 Touchscreen On/Off Button – Cheap Yellow Display (ESP32-2432S028R)

In this project, you’ll learn how to create a simple graphical user interface (GUI) with the ESP32 Cheap Yellow Display (ESP32-2432S028R). The TFT display will have an ON and OFF button that you can use to control an output.

ESP32 Touchscreen On and Off Buttons Cheap Yellow Display ESP32-2432S028R

If you have a standalone TFT Touchscreen Display 2.8 inch with ILI9341 driver, you can follow this guide.

Introducing the ESP32 Cheap Yellow Display – CYD (ESP32-2432S028R)

The ESP32-2432S028R development board has become known in the maker community as the “Cheap Yellow Display” or CYD for short. This development board, whose main chip is an ESP32-WROOM-32 module, comes with a 2.8-inch TFT touchscreen LCD, a microSD card interface, an RGB LED, and all the required circuitry to program and apply power to the board.

ESP32 Cheap Yellow Display CYD Board ESP32-2432S028R front

This is a very versatile board to build GUIs for your IoT projects, and is much more convenient and practical than using a separate ESP32 board with a TFT screen.

Where to buy?

You can click on the link below to check where to buy the ESP32 Cheap Yellow display and its price in different stores.

Prerequisites

The ESP32 communicates with the TFT Display and Touchscreen using SPI communication protocol. We’ll be using the TFT_eSPI and XPT2046_Touchscreen libraries. To properly use the TFT_eSPI library, you need a configuration file called User_Setup.h with the right definitions.

Code – Touchscreen with On/Off Button

With the following code, the TFT will display an ON/OFF button to control an output. When you press the touchscreen with your finger or pen, it should turn on/off the green RGB LED on the back of the board.

Copy the following code to the Arduino IDE and upload it to your board.

/*  Rui Santos & Sara Santos - Random Nerd Tutorials
    THIS EXAMPLE WAS TESTED WITH THE FOLLOWING HARDWARE:
      ESP32-2432S028R 2.8 inch 240×320 also known as the Cheap Yellow Display (CYD): https://makeradvisor.com/tools/cyd-cheap-yellow-display-esp32-2432s028r/
      SET UP INSTRUCTIONS: https://RandomNerdTutorials.com/cyd/
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
    Complete project details: https://RandomNerdTutorials.com/touchscreen-on-off-button-cheap-yellow-display-esp32-2432s028r/
*/

#include <SPI.h>

/*  Install the "TFT_eSPI" library by Bodmer to interface with the TFT Display - https://github.com/Bodmer/TFT_eSPI
    *** IMPORTANT: User_Config.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***
    *** YOU MUST USE THE User_Config.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***
    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https://RandomNerdTutorials.com/cyd/ */
#include <TFT_eSPI.h>

// Install the "XPT2046_Touchscreen" library by Paul Stoffregen to use the Touchscreen - https://github.com/PaulStoffregen/XPT2046_Touchscreen
// Note: this library doesn't require further configuration
#include <XPT2046_Touchscreen.h>

TFT_eSPI tft = TFT_eSPI();

// Touchscreen pins
#define XPT2046_IRQ 36   // T_IRQ
#define XPT2046_MOSI 32  // T_DIN
#define XPT2046_MISO 39  // T_OUT
#define XPT2046_CLK 25   // T_CLK
#define XPT2046_CS 33    // T_CS

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define FONT_SIZE 3

// Button position and size
#define FRAME_X 60
#define FRAME_Y 60
#define FRAME_W 200
#define FRAME_H 120

// Red zone size
#define REDBUTTON_X FRAME_X
#define REDBUTTON_Y FRAME_Y
#define REDBUTTON_W (FRAME_W / 2)
#define REDBUTTON_H FRAME_H

// Green zone size
#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
#define GREENBUTTON_Y FRAME_Y
#define GREENBUTTON_W (FRAME_W / 2)
#define GREENBUTTON_H FRAME_H

// RGB LED Pins
#define CYD_LED_BLUE 17
#define CYD_LED_RED 4
#define CYD_LED_GREEN 16

// Touchscreen coordinates: (x, y) and pressure (z)
int x, y, z;

// Stores current button state
bool buttonState = false;

// Print Touchscreen info about X, Y and Pressure (Z) on the Serial Monitor
void printTouchToSerial(int touchX, int touchY, int touchZ) {
  Serial.print("X = ");
  Serial.print(touchX);
  Serial.print(" | Y = ");
  Serial.print(touchY);
  Serial.print(" | Pressure = ");
  Serial.print(touchZ);
  Serial.println();
}

// Draw button frame
void drawFrame() {
  tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, TFT_BLACK);
}

// Draw a red button
void drawRedButton() {
  tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_RED);
  tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_WHITE);
  drawFrame();
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(FONT_SIZE);
  tft.setTextDatum(MC_DATUM);
  tft.drawString("ON", GREENBUTTON_X + (GREENBUTTON_W / 2), GREENBUTTON_Y + (GREENBUTTON_H / 2));
  buttonState = false;
}

// Draw a green button
void drawGreenButton() {
  tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_GREEN);
  tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_WHITE);
  drawFrame();
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(FONT_SIZE);
  tft.setTextDatum(MC_DATUM);
  tft.drawString("OFF", REDBUTTON_X + (REDBUTTON_W / 2) + 1, REDBUTTON_Y + (REDBUTTON_H / 2));
  buttonState = true;
}

void setup() {
  Serial.begin(115200);

  // Start the SPI for the touchscreen and init the touchscreen
  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  touchscreen.begin(touchscreenSPI);
  // Set the Touchscreen rotation in landscape mode
  // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 3: touchscreen.setRotation(3);
  touchscreen.setRotation(1);

  // Start the tft display
  tft.init();
  // Set the TFT display rotation in landscape mode
  tft.setRotation(1);

  // Clear the screen before writing to it
  tft.fillScreen(TFT_BLACK);

  // Draw button 
  drawGreenButton();

  pinMode(CYD_LED_GREEN, OUTPUT);
  digitalWrite(CYD_LED_GREEN, LOW);
}

void loop() {
  // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z) info on the TFT display and Serial Monitor
  if (touchscreen.tirqTouched() && touchscreen.touched()) {
    // Get Touchscreen points
    TS_Point p = touchscreen.getPoint();
    // Calibrate Touchscreen points with map function to the correct width and height
    x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
    y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
    z = p.z;

    printTouchToSerial(x, y, z);

    if (buttonState) {
      Serial.println("ON");
      if ((x > REDBUTTON_X) && (x < (REDBUTTON_X + REDBUTTON_W))) {
        if ((y > (REDBUTTON_Y)) && (y <= (REDBUTTON_Y + REDBUTTON_H))) {
          Serial.println("Red button pressed");
          drawRedButton();
          digitalWrite(CYD_LED_GREEN, HIGH);
        }
      }
    }
    else {
      Serial.println("OFF");
      if ((x > (GREENBUTTON_X)) && (x < (GREENBUTTON_X + GREENBUTTON_W))) {
        if ((y > (GREENBUTTON_Y)) && (y <= (GREENBUTTON_Y + GREENBUTTON_H))) {
          Serial.println("Green button pressed");
          drawGreenButton();
          digitalWrite(CYD_LED_GREEN, LOW);
        }
      }
    }
  }
}

View raw code

How the Code Works

Let’s take a quick look at the parts of the code that are relevant to this example.

Libraries

Include the SPI, TFT_eSPI and XPT2046_Touchscreen libraries.

#include <SPI.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>

Initialize TFT

Create a TFT_eSPI instance:

TFT_eSPI tft = TFT_eSPI();

Initialize Touchscreen

The following lines set the touchscreen pinout:

#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33

Create a touchscreenSPI and touchscreen instances:

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);

Other Variables

Set the screen width, screen height, and font size:

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define FONT_SIZE 3

These next variables have self-explanatory names, they are used to define the frame/button sizes and coordinates:

// Button position and size
#define FRAME_X 60
#define FRAME_Y 60
#define FRAME_W 200
#define FRAME_H 120

// Red zone size
#define REDBUTTON_X FRAME_X
#define REDBUTTON_Y FRAME_Y
#define REDBUTTON_W (FRAME_W / 2)
#define REDBUTTON_H FRAME_H

// Green zone size
#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
#define GREENBUTTON_Y FRAME_Y
#define GREENBUTTON_W (FRAME_W / 2)
#define GREENBUTTON_H FRAME_H

Assign the RGB LED GPIOs (this is the LED at the back of the board). In this example, we’ll only be controlling the green part of the LED (i.e. GPIO 16), but you can control other GPIOs to generate different colors.

#define CYD_LED_BLUE 17
#define CYD_LED_RED 4
#define CYD_LED_GREEN 16

Variables to store the touch coordinates: (x, y) and pressure (z).

int x, y, z;

The switchState stores the current button state:

bool buttonState = false;

setup()

Start a serial communication with the Serial Monitor at a baud rate of 115200:

Serial.begin(115200);

Start the SPI for the touchscreen and initialize the touchscreen.

touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreenSPI);
touchscreen.setRotation(1);

Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 3: touchscreen.setRotation(3);

Start the TFT display and set the TFT display rotation in landscape mode.

tft.init();
tft.setRotation(1);

Set the TFT screen background to black.

tft.fillScreen(TFT_BLACK);

At the start, the LED is set to off, so draw the green button that will turn on the LED when pressed.

drawGreenButton();
pinMode(CYD_LED_GREEN, OUTPUT);
digitalWrite(CYD_LED_GREEN, LOW);

loop()

In the loop(), it constantly checks if the touchscreen was touched.

if (touchscreen.tirqTouched() && touchscreen.touched()) {

When it detects that the touchscreen was touched, it will get the (x,y) coordinates and the pressure (z) from the point.

TS_Point p = touchscreen.getPoint();
// Calibrate Touchscreen points with map function to the correct width and height
x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
z = p.z;

Call the printTouchToSerial function to print the touchscreen info in the Serial Monitor for debugging purposes.

printTouchToSerial(x, y, z);

Based on the x and y coordinates detected by the touchscreen press, it will check if you pressed within the limits of the ON or OFF button coordinates. Then, it will turn the LED on or off accordingly and draw the right button to display by calling the drawRedButton() or drawGreenButton() functions.

if (buttonState) {
  Serial.println("ON");
  if ((x > REDBUTTON_X) && (x < (REDBUTTON_X + REDBUTTON_W))) {
    if ((y > (REDBUTTON_Y)) && (y <= (REDBUTTON_Y + REDBUTTON_H))) {
      Serial.println("Red button pressed");
      drawRedButton();
      digitalWrite(CYD_LED_GREEN, HIGH);
    }
  }
}
else {
  Serial.println("OFF");
  if ((x > (GREENBUTTON_X)) && (x < (GREENBUTTON_X + GREENBUTTON_W))) {
    if ((y > (GREENBUTTON_Y)) && (y <= (GREENBUTTON_Y + GREENBUTTON_H))) {
      Serial.println("Green button pressed");
      drawGreenButton();
      digitalWrite(CYD_LED_GREEN, LOW);
    }
  }
}

printTouchToSerial()

The printTouchToSerial() function prints touchscreen info about X, Y, and Pressure (Z) on the Serial Monitor.

void printTouchToSerial(int touchX, int touchY, int touchZ) {
  Serial.print("X = ");
  Serial.print(touchX);
  Serial.print(" | Y = ");
  Serial.print(touchY);
  Serial.print(" | Pressure = ");
  Serial.print(touchZ);
  Serial.println();
}

Other Functions: drawFrame(), drawRedButton(), drawGreenButton()

The drawFrame() function draws a frame around the ON or OFF buttons.

// Draw button frame
void drawFrame() {
  tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, TFT_BLACK);
}

The drawRedButton() and drawGreenButton() functions create the ON and OFF buttons on the TFT display. It will draw and fill a rectangle based on the x and y coordinates defined at the start of the sketch, as well as writing the ON and OFF text labels.

// Draw a red button
void drawRedButton() {
  tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_RED);
  tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_WHITE);
  drawFrame();
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(FONT_SIZE);
  tft.setTextDatum(MC_DATUM);
  tft.drawString("ON", GREENBUTTON_X + (GREENBUTTON_W / 2), GREENBUTTON_Y + (GREENBUTTON_H / 2));
  buttonState = false;
}

// Draw a green button
void drawGreenButton() {
  tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_GREEN);
  tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_WHITE);
  drawFrame();
  tft.setTextColor(TFT_BLACK);
  tft.setTextSize(FONT_SIZE);
  tft.setTextDatum(MC_DATUM);
  tft.drawString("OFF", REDBUTTON_X + (REDBUTTON_W / 2) + 1, REDBUTTON_Y + (REDBUTTON_H / 2));
  buttonState = true;
}

Demonstration

Upload the code to your board. Go to Tools > Board and select ESP32 > ESP32 Dev Module. Then, select the right COM port in Tools > Port. Finally, click the upload button.

Arduino IDE 2 Upload Button

After uploading the code to your board, the TFT display should have an ON button that you can press with your finger.

Touchscreen On Off Button Cheap Yellow Display ESP32-2432S028R Arduino IDE

Pressing the ON button turns on the green RGB LED at the back of the board (it turns on GPIO 16).

ESP32 Cheap Yellow Display RGB green LED ON ESP32-2432S028R

If the LED is on, the display should have the OFF button. You can click on it to turn the LED off.

Touchscreen Press On Off Button Cheap Yellow Display ESP32-2432S028R

The coordinates, pressure, and LED state are also printed on the Arduino IDE serial monitor for debugging purposes:

ESP32 TFT Touchscreen On Off Button Cheap Yellow Display Arduino IDE Serial Monitor

Wrapping Up

In this tutorial, you learned how to build a basic GUI for the ESP32 Cheap Yellow Display board with an On/Off button.

If it’s your first time using this board, we also recommend reading our: Getting Started with ESP32 Cheap Yellow Display Board – CYD (ESP32-2432S028R)

To learn more about the ESP32, make sure to take a look at our resources:



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

20 thoughts on “ESP32 Touchscreen On/Off Button – Cheap Yellow Display (ESP32-2432S028R)”

  1. I greatly appreciate your tutorials. This worked just as you explained. I did wonder if you plan to have a tutorial about using the SD card with the CYD explaining how to display jpeg images stored there.

    Reply
  2. Excellent. Thank you. Worked straight away. I love this little board.

    One thing I had to do was replace the User_Setup.h file in the TFT_eSPI library folder with the file you sent in the previous tutorial ‘Getting Started with ESP32 Cheap Yellow Display Board – CYD (ESP32-2432S028R)’. Although I had done that previously, it gets overwritten if the library is updated. Something to watch out for.

    Reply
  3. Dear Ruis, another great sketch. you teach so well Many thank.
    would you be able to explain how I can use the TFT_espi user setups and then I can add your User_Setup to users set up and can swap the user setup for other boards and setups within the library.
    I am not sure what you have to put in the code to get it to use one of the other user set ups which are numbered.
    Many thanks
    Mark

    Reply
  4. If the sketch is expected to run as described in this document, then a few changes are needed in the “Setup” function. Change drawGreenButton to drawRedButton and “LOW” to “HIGH”.

    // Draw button
    //drawGreenButton();
    drawRedButton();

    pinMode(CYD_LED_GREEN, OUTPUT);
    //digitalWrite(CYD_LED_GREEN, LOW);
    digitalWrite(CYD_LED_GREEN, HIGH);

    Reply
      • Mark,
        It’s no major point but I can explain what I meant when I said, “If the sketch is expected to run as described”.
        In this URL “https://randomnerdtutorials.com/touchscreen-on-off-button-cheap-yellow-display-esp32-2432s028r/”, if you go to the part titled “Demonstration”, you’ll see the text “After uploading the code to your board, the TFT display should have an ON button that you can press with your finger.”. It doesn’t initially come up with the “ON” button (RED)
        but instead, it comes up with the “OFF” button (GREEN).
        That is all I was saying — it’s no big deal, I was just trying to be more precise.
        (It’s a flaw I’ve had to live with for some time now.)
        Otherwise, it does work as described! The initial conditions are not necessarily of concern.
        Ray
        BTW; I’ve been looking for the docs for the various TFT methods used in the program like “tft.drawString”. Do you know where they might be?

        Reply
  5. Thank you for the tutorial, do you plan a tutorial on performing i/o operations with the build-in sd card reader for this board?

    Reply
  6. Hi Guys,
    I am trying to port this to Platform IO.
    Could you please advise the correct Platformio.ini lib deps code so I get the correct libraries.
    Cheers
    Mark

    Reply
  7. Rui,
    Just so I’m sure I’m not way off track — did you see my comment about having the sketch work as described in the tutorial?
    Do you agree that in the Setup function, the draw button and the digital write calls should be changed to the opposite ones in order for the initial program run to present a RED button as is said in the DEMONSTARTION part of the tutorial?

    Reply
  8. Hi,
    can someone explain the
    Calibrate Touchscreen map function.
    x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
    y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
    from where are the parameters 200,3700 / 240,3800
    on my CYD the touch is not working exactly, even the buttons are small.
    regards Friedrich

    Reply
    • I’m assuming you are really asking where the numbers for x (200,3700) and y (240,3800) came from. i.e. how were they determined?
      Furthermore, I assume you are not actually asking how the “map” function works!
      If this is correct, then here is what I have learned:
      My explanation is for ‘x’, ‘y’ is similiar.
      To begin, we should realize that when the screen is touched, a analog value is returned to the program. The value return has a range of approx. 120 to 3900. I don’t know why the value is in that range, it seems similiar to me as when you attach a potentiometer to a pin and read its value. I believe the value is based on the number of bits resolution employed by the digital to analog conversion process on the board.
      So, assuming the range which I proposed, all values in that range must be translated to the range of values for the x coordinate number of pixels, which is 320.
      In other words, inputs values of 120 to 3900 must be translated to values of 0 or 1 to 320.
      If you try to determine the input values from the analog input, you may get something different, but it will be close to the values given in the tutorial (200,3700).
      I got slightly different values when I ran a loop and printed out the values of x as I moved the stylus from the extreme left of the display to the extreme right. The value given (200,3700) are very good estimates. Another thing to consider is this: even if you knew the EXACT left hand value when you touch the extreme left hand part of the display with the stylus, you couldn’t really use that value because how could you ensure that a person using the display could actually touch that exact spot? What we do is determine the approx. values when someone can actually touch with the stylus and we use those values.
      I tried my best to touch the EXTREME left of the screen with the stylus and then move all the way over to the EXTREME right and I could not get any values better than what is given in the tutorial.
      So, we receive the analog values of 120 to 3700 for ‘x’ when we touch the screen and those values must be translated to the pixel coordinates of the display which are 0 or 1 to 320 for the ‘x’ coordinate.
      Another way to look at it is this: some group of the analog input numbers must represent the x coordinate of 1 and the next group of analog numbers represent the x coordinate of 2, and so forth. I don’t know what the breakout is but if it is a linear relationship, then perhaps we can say that since we are mapping from a large set of numbers to a smaller set, then our target set is 320 values but our source set is 3700 values. So 3700 by 320 and you get approx. 11, so perhaps every 11 analog values represent one x coordinate.
      So, if you touch anywhere on the screen that causes an analog value of 120 to 131 (120+11), then you may interpret that as an x coordinate of 1.
      I hope I didn’t get to windy on this reply — I just felt the explanation should be reasonably complete. I hope I haven’t made any really bad errors in my explanation. Please let me know if I have.
      Ray Leiter

      Reply
  9. Hi Ray,
    thanks for the detailed explanation. The last time I used TFT_eSPi by Bodmer with the “included” touch routines – with calibration function -> and therefore I had not to think about the details. The map-routine I know – it was just as you mentioned from where are the numbers in the map function. Thank you

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.