An undocumented display on ESP32: identifying it and getting it running

guide · smart home

A display with no specification or datasheet can be brought to life, but most of the time goes not into wiring, but into finding out what it actually is. This one was eventually identified as the E32R35T (ESP32-WROOM-32E, 3.5″ 320×480) with an ST7796S controller and an XPT2046 touch layer.

01

1. Find out what it is

  • Markings on the board and connector: the module designation (here E32R35T), pin names (SCK, SDI/MOSI, CS, DC/RS, RST, BL/LED) and the T_* touch pins.
  • Size and resolution: 3.5″ displays are usually 320 × 480 pixels.
  • Controller: the most common controllers on 3.5″ SPI displays are ILI9488 and ST7796; here ST7796S. If the marking is missing, try the drivers one by one – a wrong controller shows up as wrong colours or a shifted image.
  • Touch: a resistive layer has its own XPT2046 controller on the same SPI bus with its own CS.
02

2. E32R35T pins

signal GPIO
SCLK (shared) GPIO14
MOSI (shared) GPIO13
MISO (shared) GPIO12
LCD CS GPIO15
LCD DC GPIO2
LCD BL GPIO27
LCD RESET on EN (shared with ESP32)
Touch CS GPIO33
Touch IRQ GPIO36
03

3. Reset belongs on EN (a common trap)

The display's RESET is tied to the ESP32's EN, so it must not be driven in software. The original attempt set TFT_RST = -1 yet still called pinMode(TFT_RST, ...) – working with a non-existent GPIO crashed and reset the program right after the “Resetting display…” message. The display stayed white: the backlight worked, but the controller was not initialised. The fix: do not handle reset in software and do not call pinMode on TFT_RST.

04

4. The ST7796S init sequence

The generic ST7796 initialisation from TFT_eSPI may not be enough; more reliable is the manufacturer's sequence for the E32R35T: registers 0x11 (sleep out), 0x36 (orientation/MADCTL), 0x3A (colour format), then 0xF0, 0xB4, 0xB7, 0xC0…, gamma 0xE0/0xE1 and finally 0x29 (display on).

Porting the original API to Arduino: LCD_WR_REG(x) → tft.writecommand(x), LCD_WR_DATA(x) → tft.writedata(x), delay_ms() → delay(). LCD_direction() can be replaced by writing register 0x36 or by the library's rotation.

05

5. TFT_eSPI User_Setup

#define ST7796_DRIVER
#define TFT_WIDTH  320
#define TFT_HEIGHT 480

#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS   15
#define TFT_DC    2
#define TFT_RST  -1   // reset is on EN; do NOT touch it via pinMode
#define TFT_BL   27
#define TOUCH_CS 33

To verify the wiring and controller: tft.fillScreen() in red, green, blue. If the colours fill the whole display, the pins and controller are right.

06

6. XPT2046 touch

On the same SPI bus, with its own CS on GPIO33 and an IRQ on GPIO36. It is initialised on the same bus, only with a different CS.

07

7. Downloading an image and JPEG (an open problem)

Once the LCD was running, the next step was downloading an image over Wi-Fi and decoding JPEG. The HTTP download worked (200, ~2.7 kB, a valid FF D8 start), but the TJpg_Decoder could not determine the dimensions (0×0). That is a separate problem of the JPEG parser/decoder, not of the ST7796S initialisation.

Wi-Fi credentials belong in configuration outside the repository, not in the source.

08

Things to watch out for

  • RESET is on EN – do not touch it in software.
  • DC is fixed on GPIO2, a strapping pin: logic 1 at start-up prevents entering the firmware download mode. If flashing fails, make sure nothing holds GPIO2 at logic 1 at start-up.
  • A full 320 × 480 redraw over SPI takes time; redraw only the changed area.
  • Images take up memory – scale them to the display before they are built into the firmware.

← back to explore