Skip to content
// Article · Field Report

How to use a 1.77 inch TFT with ESP32?

Published admin · Researcher

How to Use a 1.77 Inch TFT with ESP32

To get a 1.77 inch TFT display working with an ESP32, you wire it up via SPI, load the right library, and push pixel data. The specific panel I am referencing here is a 128x160 resolution RGB TFT that uses the ST7735S driver, which is common for these small screens. The ESP32’s 3.3V logic levels match the display’s voltage, but you still need a level shifter if your display expects 5V for backlight—check the datasheet. For this display, the SPI interface uses four main lines: MOSI (Master Out Slave In), MISO (Master In Slave Out, often unused for TFTs), SCK (Serial Clock), and a Chip Select (CS) pin. You also need a Data/Command (DC) pin, a Reset (RST) pin, and a backlight control pin (LED). Most modules expose these as a 7-pin or 8-pin header. The pinout for the 1.77 inch spi mcu rgb tft display is standard: VCC (3.3V), GND, CS, RESET, DC, MOSI, SCK, and LED. Some variants combine MOSI and MISO into a single data line, but for this module, you treat it as a standard SPI device. The ESP32’s hardware SPI pins are flexible—you can remap them using the VSPI or HSPI peripherals. A common setup uses VSPI: MOSI on GPIO 23, MISO on GPIO 19 (optional), SCK on GPIO 18, CS on GPIO 5, DC on GPIO 17, RST on GPIO 16, and backlight on GPIO 4. This gives you a clean configuration that avoids conflicts with the ESP32’s default serial and flash pins. The backlight pin is PWM-capable, so you can dim the display by writing an analog value (0-255) using the ledc library. The display’s refresh rate is typically 60 Hz, but the SPI clock speed determines how fast you can push data. The ST7735S supports up to 15 MHz SPI clock, but the ESP32 can handle up to 40 MHz on its SPI bus. In practice, running at 10 MHz to 20 MHz is stable with short wires—anything above 20 MHz may introduce signal noise if your breadboard wiring is sloppy. The display’s pixel format is 16-bit RGB565, meaning each pixel uses two bytes: 5 bits for red, 6 bits for green, and 5 bits for blue. This gives you 65,536 colors total. The frame buffer for a 128x160 image is 128 * 160 * 2 = 40,960 bytes (about 40 KB). The ESP32 has 520 KB of SRAM, so you can easily allocate a full frame buffer, but for animations or video, you might want to use a partial buffer to save memory. The TFT’s controller also supports window addressing—you can update only a rectangular region instead of the whole screen, which cuts down SPI traffic. For example, if you only change a 50x50 pixel area, you send 5,000 bytes instead of 40,960 bytes. This is critical for responsive UI elements like buttons or text updates.

Now, the software side: you need a driver library that handles the ST7735S initialization sequence. The most popular choice is the Adafruit ST7735 library, which is built on top of the Adafruit GFX library. But the Adafruit library expects a specific pin mapping and uses software SPI by default, which is slower. For the ESP32, you want to use hardware SPI for speed. You can modify the library’s constructor to pass the ESP32’s SPI object. Alternatively, use the TFT_eSPI library by Bodmer, which is optimized for ESP32 and supports hardware SPI out of the box. TFT_eSPI has a configuration file called User_Setup.h where you define your pin assignments and display driver. For this 1.77 inch panel, you set the driver to ST7735, the display size to 128x160, and the color order to RGB (not BGR, which some ST7735 variants use). The library also lets you set the SPI frequency, which I recommend at 20 MHz for stability. If you use TFT_eSPI, you don’t need to manually send initialization commands—it has a built-in init sequence for the ST7735S. But note that some cheap 1.77 inch modules have a different initial register set (like the “red tab” or “green tab” versions), so you might need to tweak the initR function. The typical init sequence includes commands to set the display sleep mode off, set the pixel format to 16-bit, set the memory data access control (to match the orientation), and set the column and page addresses. If the colors look inverted or the display shows a green tint, you likely have the wrong color order or the MADCTL register set incorrectly. The MADCTL register (0x36) controls the RGB/BGR order, the page/column order, and the row/column exchange. For a portrait orientation (128x160), you set MADCTL to 0x00 for normal orientation or 0xC0 for landscape. Experiment with values like 0x00, 0x60, 0xC0, and 0xA0 to get the correct orientation. The TFT_eSPI library also supports a setRotation function that abstracts this—you call tft.setRotation(1) for landscape, and it sets the MADCTL register accordingly. But if you use a custom library, you must write the MADCTL value directly via SPI.

Let’s talk about power consumption because it matters for battery-powered projects. The 1.77 inch TFT with backlight on draws about 40 mA at 3.3V. The ESP32 itself draws around 80 mA during active SPI communication, plus Wi-Fi if you use it. Total current can hit 120-150 mA. If you power it from a 3.7V LiPo battery, you need a 3.3V regulator (like the AMS1117) that can handle 200 mA. The display’s backlight LED typically has a forward voltage of 3.0V to 3.2V and draws 20 mA to 30 mA. You can connect it directly to a GPIO pin through a 100-ohm resistor, but the ESP32 GPIO can only source 12 mA per pin, so you need a transistor (like a 2N2222) or a dedicated backlight driver for full brightness. A simpler approach is to connect the backlight to the 3.3V rail through a 100-ohm resistor, which gives you constant brightness but no PWM control. If you want dimming, use a MOSFET like the 2N7000 with the gate connected to a PWM-capable GPIO. The display’s sleep mode (via command 0x10) reduces current to under 1 mA, which is useful for deep sleep applications. You can wake it by sending the sleep-out command (0x11) and waiting 120 ms for the internal oscillator to stabilize. The ESP32’s deep sleep current is about 5 µA, so the combined system can last weeks on a 2000 mAh battery if you use sleep modes wisely.

Now, let’s get into actual code structure. You need to initialize the SPI bus and the display. In Arduino IDE, with TFT_eSPI installed, the setup looks like this: include , create an instance TFT_eSPI tft = TFT_eSPI(), then in setup(), call tft.init() and tft.setRotation(1). The init() function sends the initialization sequence, sets the display to 128x160, and clears the screen to black. Then you can draw shapes: tft.fillScreen(TFT_BLACK), tft.fillRect(10, 10, 50, 50, TFT_RED), tft.drawPixel(100, 100, TFT_GREEN). For text, use tft.setCursor(0, 0), tft.setTextColor(TFT_WHITE), tft.println("Hello"). The default font is a 5x7 pixel bitmap, which is small but readable. You can load custom fonts using the tft.loadFont() function with a font file stored in SPIFFS or SD card. The library supports TrueType font conversion via a Python tool, but for most applications, the built-in fonts are enough. For images, you can decode a JPEG or BMP file from SPIFFS and draw it pixel by pixel. The TFT_eSPI library has a drawJpgFile() function that handles the decoding, but it requires the JPEG to be stored as a byte array or in SPIFFS. For a 128x160 JPEG, the file size is typically 5-10 KB, and decoding takes about 100-200 ms at 20 MHz SPI speed. If you need smooth animations, use a frame buffer in PSRAM (if your ESP32 has it, like the ESP32-WROVER modules). The ESP32-S3 and ESP32-C3 also have PSRAM options. With PSRAM, you can allocate a 40 KB frame buffer and use double buffering: draw to the back buffer, then copy it to the display via SPI. This avoids tearing artifacts. The copy operation at 20 MHz takes about 20 ms for a full frame (40,960 bytes / 2.5 MB/s = 16.4 ms, plus overhead). That gives you a theoretical 50 FPS, but the display’s internal refresh rate limits you to 60 Hz. In practice, you get smooth scrolling at 30 FPS.

Let’s address common issues. If the display shows nothing, check the reset pin timing. The ST7735S requires a low pulse on RST for at least 10 µs after power-up. The TFT_eSPI library handles this, but if you wire RST to a GPIO, make sure it’s not floating. Some modules have a built-in RC reset circuit, but it’s safer to use a dedicated GPIO. If the screen shows random pixels or garbled data, you likely have a wiring issue—short wires, loose connections, or wrong pin mapping. Use an oscilloscope or logic analyzer to verify the SPI signals: CS should go low before data, SCK should have clean edges, and MOSI should change on the rising edge of SCK. The display expects SPI mode 0 (CPOL=0, CPHA=0), meaning the clock idles low and data is sampled on the rising edge. The ESP32’s hardware SPI defaults to mode 0, so no changes needed. If the colors are wrong (e.g., red appears blue), you have the RGB/BGR order inverted. In TFT_eSPI, you can set #define TFT_RGB_ORDER TFT_BGR in User_Setup.h. Alternatively, in the MADCTL register, bit 3 controls the RGB/BGR order: 0 for RGB, 1 for BGR. So if the colors are swapped, set MADCTL to 0x08 (BGR) or 0x00 (RGB). Another issue is the display’s initialization sequence. Some cheap modules use a different driver variant like the ST7735S with a “red tab” that requires a different init command set. The TFT_eSPI library has a ST7735_redtab option in the setup file. If you use the Adafruit library, you can call tft.initR(INITR_BLACKTAB) for the common variant, or INITR_REDTAB for the red tab version. If you don’t know which tab you have, try both. The physical tab is a small colored plastic piece on the flex cable—red, green, or black—but not all modules have it. For this specific 1.77 inch module from the link, the manufacturer specifies it uses the standard ST7735S with RGB order, so you don’t need to guess.

For performance optimization, you can use DMA (Direct Memory Access) on the ESP32’s SPI bus. The ESP32’s SPI controller supports DMA transfers, which offloads the CPU from moving data. In TFT_eSPI, you enable DMA by setting #define USE_DMA in User_Setup.h. This requires the SPI pins to be on the same DMA channel. With DMA, you can push a full frame buffer in about 10 ms at 20 MHz, freeing the CPU for other tasks like sensor reading or Wi-Fi. The downside is that DMA uses the SPI bus exclusively during the transfer, so you can’t interleave other SPI devices. If you have an SD card on the same bus, you need to handle CS switching carefully. Another optimization is to use the ESP32’s dual-core architecture. You can run the display update on Core 1 and your main loop on Core 0. In Arduino, you use the xTaskCreatePinnedToCore function. For example, create a task that continuously reads from a frame buffer and calls tft.pushImage() every 33 ms (30 FPS). This gives you smooth video playback from a camera module like the OV2640. The OV2640 outputs JPEG data at 640x480, which you downscale to 128x160 and decode to RGB565. The decoding takes about 50 ms per frame on the ESP32, so you get around 15-20 FPS. If you use the ESP32-S3 with its hardware JPEG decoder, you can hit 30 FPS. The display’s SPI speed becomes the bottleneck here—20 MHz gives you about 2.5 MB/s, which is enough for 30 FPS (40,960 bytes * 30 = 1.23 MB/s). So the display is not the limiting factor; the JPEG decoding is.

Let’s talk about the physical interface. The 1.77 inch TFT module has a 0.1-inch pitch pin header, which is breadboard-friendly. But for a permanent project, you should solder wires or use a PCB. The ESP32 development boards (like the ESP32-DevKitC) have pin headers that align with the display’s header if you use a custom PCB. The display’s thickness is about 2.5 mm, and the PCB is 1.6 mm, so it’s slim enough for a handheld device. The viewing angle is rated at 12 o’clock, meaning it’s best viewed from the top. The contrast ratio is typically 300:1, and the brightness is 200-300 cd/m² with the backlight at full power. In direct sunlight, it’s barely readable, so you need a backlight boost or a transflective panel for outdoor use. The response time is about 10 ms, which is fine for static images but may show ghosting for fast animations. The operating temperature range is -20°C to 70°C, so it works in most indoor and outdoor environments. The display’s connector is a 0.5mm pitch FPC (flexible printed circuit) on the module side, but the breakout board converts it to a 2.54mm pin header. If you buy the bare panel without the breakout board, you need to solder wires to the FPC, which is tricky. The module from the link includes the breakout board, so you get the pin header directly.

Now, a real-world example: building a weather station with the 1.77 inch TFT and an ESP32. You connect a DHT22 sensor for temperature and humidity, and a BMP280 for barometric pressure. The ESP32 reads the sensors every 10 seconds, updates the display with the values, and sends data to a cloud server via Wi-Fi. The display shows a simple UI: temperature in large font at the top, humidity in the middle, and pressure at the bottom. You use the tft.drawNumber() function to print the values. For the background, you draw a gradient from blue to white to simulate the sky. The whole thing runs on a 3.7V 18650 battery with a TP4056 charger. The ESP32 goes into deep sleep between updates, waking up every 10 seconds via the timer. During sleep, the display is turned off by setting the backlight GPIO low and sending the sleep command. The total current during sleep is 5 µA (ESP32) + 1 µA (display) + 2 µA (sensors) = 8 µA. With a 2000 mAh battery, you get 2000 mAh / 0.008 mA = 250,000 hours, but that’s theoretical because the battery self-discharges. In practice, you get months of operation. The display update takes 100 ms, drawing 120 mA, so the average current is (100 ms * 120 mA + 9900 ms * 0.008 mA) / 10000 ms = 1.2 mA + 0.0079 mA = 1.21 mA. That gives you about 1650 hours of run time, or 68 days. If you update every 60 seconds, you get over a year. This is a practical example of using the display in a low-power scenario.

For troubleshooting, I recommend using the SPIFFS file system to store test images and fonts. You can upload a 128x160 BMP file via the Arduino IDE’s SPIFFS upload tool, then load it with tft.drawBmpFile(). If the image looks correct, your SPI wiring and initialization are fine. If not, check the color order and orientation. Another test is to draw a checkerboard pattern: for (int y=0; y<160; y+=16) for (int x=0; x<128; x+=16

admin
Vulnerability Researcher · ZeroDayCN

Investigates zero-day activity in the Chinese threat landscape and coordinates responsible disclosure across vendors.

// Get the Brief

The Threat Brief, in your inbox.

Triage-ready advisories land an average of 19 hours before NVD publication. Trusted by 2,100+ SOC teams.