Documentation

ESP32 LVGL 2.8" 240x320 Smart Display with Resistive Touch & WiFi/Bluetooth Dev Board | ShillehTek Product Manual
Documentation / ESP32 LVGL 2.8" 240x320 Smart Display with Resistive Touch & WiFi/Bluetooth Dev Board | ShillehTek Product Manual

ESP32 LVGL 2.8" 240x320 Smart Display with Resistive Touch & WiFi/Bluetooth Dev Board | ShillehTek Product Manual

manualshillehtek

Overview

The ESP32 LVGL 2.8" Smart Display — better known in the maker community as the Sunton ESP32-2432S028R or simply the "Cheap Yellow Display" (CYD) — is one of the best value all-in-one ESP32 dev boards you can buy. You get an ESP-WROOM-32 module, a 2.8" 240x320 ILI9341 TFT, a resistive touch panel driven by an XPT2046, a microSD slot, a small speaker output, and a USB programming port, all on a single board for about the price of a bare ESP32 plus a discrete display.

What makes it especially attractive is software support. The board is fully compatible with the LVGL graphics library, TFT_eSPI, and LovyanGFX on the Arduino IDE side, plus various MicroPython display drivers. If you want a Wi-Fi connected smart-home control panel, a portable instrument display, or a small dashboard for sensor data, this board cuts your bring-up time dramatically.

The trade-off is that you have to use the specific GPIOs the board manufacturer chose — the display, touch, and SD share the same SPI bus but have separate chip-select lines, and the backlight is on GPIO21. Once you have the right pin map plugged into TFT_eSPI's User_Setup.h (or your LVGL config), everything works flawlessly.

At a Glance

MCU
ESP-WROOM-32 (dual-core)
Display
2.8" 240x320 ILI9341
Touch
Resistive, XPT2046
Wireless
Wi-Fi + Bluetooth
Storage
microSD slot
Programming
Micro-USB (CH340)

Specifications

Parameter Value
MCU ESP32-WROOM-32, 240 MHz dual-core Xtensa
Flash 4 MB
SRAM 520 KB
Display Size 2.8 inch diagonal
Resolution 240 x 320 pixels (portrait)
Display Driver ILI9341 over SPI
Touch Controller XPT2046 resistive, SPI
Connectivity Wi-Fi 802.11 b/g/n, Bluetooth 4.2 + BLE
USB / Power Micro-USB (CH340 USB-UART) and 4P 1.25mm JST 5V input
Audio Mono speaker output via onboard amp
Expansion Extended IO header + 4-pin I2C temperature/humidity port
Backlight Control GPIO21 (active HIGH)

Pinout Diagram

ESP32 LVGL 2.8 inch 240x320 smart display board pinout showing ESP-WROOM-32, Micro-USB, RESET, BOOT, Speaker, Extended IO, Temperature/Humidity interface, TF card slot, and 4P 1.25mm power base.

Wiring Guide

Display Driver (ILI9341, hardwired on the board):

  • CS → GPIO15
  • DC / RS → GPIO2
  • RST → GPIO12
  • SCK → GPIO14
  • MOSI → GPIO13
  • MISO → GPIO12 (shared, rarely used for display)
  • Backlight → GPIO21 (set HIGH or PWM for brightness)

The display SPI bus (HSPI) is shared with the touch controller and the SD slot. Each peripheral has its own chip-select, so as long as you only assert one CS at a time, they coexist happily.

Heads up: if your screen stays dark even though your sketch is running, you almost certainly forgot to set GPIO21 HIGH for the backlight. This is the #1 gotcha on this board.

Touch Controller (XPT2046 resistive, hardwired):

  • T_CS → GPIO33
  • T_IRQ → GPIO36 (input only, optional)
  • T_CLK → GPIO25
  • T_DIN (MOSI) → GPIO32
  • T_DO (MISO) → GPIO39 (input only)

Touch uses a separate set of SPI pins from the display on most CYD variants — do not assume they share the bus until you cross-reference the silkscreen. The XPT2046 is slow (typical < 2 MHz SPI), so keep that bus speed low or you will get noisy readings.

Resistive touch always needs calibration. The first time you flash an LVGL example, you tap three corners and the offsets are stored to flash. Plan for that.

microSD Slot (SPI mode, shares display bus):

  • SD_CS → GPIO5
  • SD_SCK → GPIO18
  • SD_MOSI → GPIO23
  • SD_MISO → GPIO19

The SD slot uses VSPI by default while the display uses HSPI, so they are physically separate buses on this board. Format your card as FAT32 with a 32KB allocation unit and the standard Arduino SD.h library works out of the box. Cards larger than 32GB usually still work if formatted as FAT32 manually (Windows won't do it for you above 32GB — use guiformat or mkfs.fat).

Programming & Power options:

  • Micro-USB — CH340 USB-to-UART, used for flashing and serial monitor. Provides 5V power.
  • 4P 1.25mm JST connector (power base) — 5V external supply. Useful for enclosure builds where USB isn't exposed.
  • RESET button — pulls EN low to reboot the ESP32.
  • BOOT button — hold while pressing RESET to force download mode. Most upload tools handle this automatically via DTR/RTS, but it's there as a backup.
  • Extended IO header — breaks out spare GPIOs (typically GPIO22, GPIO27, GPIO35) and 3.3V/GND for adding sensors or buttons.
  • 4-pin I2C temperature/humidity port — 3.3V, GND, SDA, SCL on a 4-pin connector for plugging in something like an SHT3x or AHT10 without soldering.

Driver tip: on Windows install the CH340 driver from WCH; on macOS recent versions install it automatically; on Linux it shows up as /dev/ttyUSB0 with no extra setup.

Code Examples

Arduino IDE — TFT_eSPI setup for the CYD (User_Setup.h snippet):

In User_Setup.h inside the TFT_eSPI library folder, comment out the default setup and use this:

#define ILI9341_DRIVER
#define TFT_WIDTH  240
#define TFT_HEIGHT 320

#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS   15
#define TFT_DC    2
#define TFT_RST  -1   // tied to ESP32 reset
#define TFT_BL   21
#define TFT_BACKLIGHT_ON HIGH

#define TOUCH_CS 33

#define SPI_FREQUENCY       55000000
#define SPI_READ_FREQUENCY  20000000
#define SPI_TOUCH_FREQUENCY  2500000

#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_GFXFF
#define SMOOTH_FONT

Then a minimal sketch to verify the panel is alive:

#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();

void setup() {
  pinMode(21, OUTPUT);
  digitalWrite(21, HIGH);   // backlight ON
  tft.init();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(2);
  tft.setCursor(20, 100);
  tft.println("CYD is alive!");
}

void loop() {}

ESP32 + LVGL — Hello World with a button:

#include <lvgl.h>
#include <TFT_eSPI.h>

static const uint16_t SCREEN_W = 320;
static const uint16_t SCREEN_H = 240;

TFT_eSPI tft = TFT_eSPI();
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[SCREEN_W * 10];

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);
}

void setup() {
  pinMode(21, OUTPUT); digitalWrite(21, HIGH);
  tft.begin();
  tft.setRotation(1);

  lv_init();
  lv_disp_draw_buf_init(&draw_buf, buf, NULL, SCREEN_W * 10);

  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = SCREEN_W;
  disp_drv.ver_res = SCREEN_H;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.draw_buf = &draw_buf;
  lv_disp_drv_register(&disp_drv);

  lv_obj_t *btn = lv_btn_create(lv_scr_act());
  lv_obj_center(btn);
  lv_obj_t *label = lv_label_create(btn);
  lv_label_set_text(label, "Hello CYD");
}

void loop() {
  lv_timer_handler();
  delay(5);
}

MicroPython — quick check using ili9341 driver:

from machine import Pin, SPI
import ili9341

# Backlight ON
Pin(21, Pin.OUT).value(1)

spi = SPI(1, baudrate=40000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
display = ili9341.Display(spi, dc=Pin(2), cs=Pin(15), rst=Pin(12))

display.clear(ili9341.color565(0, 0, 0))
display.draw_text8x8(60, 100, "Hello CYD", ili9341.color565(255, 220, 0))

Frequently Asked Questions

My screen is completely black even though the code runs — what's wrong?
99% of the time you forgot the backlight. GPIO21 must be set HIGH (or driven by PWM) to turn the LED backlight on. Add pinMode(21, OUTPUT); digitalWrite(21, HIGH); at the top of setup() and you should see the display light up immediately.
Which board do I select in the Arduino IDE?
Use "ESP32 Dev Module" with default settings: Flash Size 4MB, Partition Scheme "Default 4MB with spiffs", Upload Speed 921600, CPU 240MHz. The CH340 will appear as a serial port once you plug in the USB cable.
Is the touch panel capacitive or resistive?
The standard 2432S028R is resistive with an XPT2046 controller. There is a separate "C" variant with a capacitive panel that uses a GT911 controller — check the part number on the back to confirm which one you have. The wiring/code differs between the two.
Can I use this board with ESPHome or Home Assistant?
Yes — ESPHome has built-in support for the ILI9341 display and XPT2046 touch components, and there are community YAML configs specifically for the CYD that you can paste in with minimal changes. It's a popular choice for wall-mounted Home Assistant control panels.
How do I use the microSD slot without breaking the display?
On most CYD boards the SD slot uses VSPI (GPIO18, 19, 23, 5) while the display uses HSPI (GPIO14, 13, 12, 15), so they are physically separate buses. Just initialize both libraries with their own pin sets — SD.begin(5) for the card and TFT_eSPI's defaults for the display — and they will coexist without conflict.
Why does my touch input feel inverted or offset?
Resistive touch needs calibration on first boot because the panel's raw ADC range doesn't match the display orientation. Run LVGL's touch calibration example, tap the three corner targets, and save the resulting offsets to NVS or SPIFFS. Many starter sketches include this step automatically.
Can I run the display in landscape (320x240) instead of portrait?
Yes — the ILI9341 supports four rotations. In TFT_eSPI call tft.setRotation(1) or tft.setRotation(3) for landscape, and in LVGL set disp_drv.hor_res = 320; disp_drv.ver_res = 240; to match. Remember to also rotate touch coordinates accordingly — the helper functions in most touch drivers take a rotation argument.
Can I power it from the JST connector instead of USB?
Yes. The 4P 1.25mm JST on the back accepts regulated 5V (not raw battery voltage). Wire 5V and GND to the correct pins — check the silkscreen carefully because pinout varies between board revisions. For battery use, add a TP4056 charger and boost converter rather than feeding LiPo voltage directly.