Skip to content
Issue No. 87 — March 2026
Subscribe →
The Side Project Dispatch — profiles, interviews & mixtape notes for indie hip-hop. Independent. Listener-supported. Atlanta, GA.
Issue No. 87 — March 2026Hip Hop Side Project · Editorial

How to display images on a 1.77 inch SPI TFT screen?

How to display images on a 1.77 inch SPI TFT screen

To display images on a 1.77 inch SPI TFT screen, you need to send pixel data over the SPI bus to the driver IC, typically the ST7735S or similar, which controls the 128x160 resolution panel. The process involves initializing the display with a specific sequence of commands, setting a window for the image area, and then streaming RGB565 color data byte-by-byte. For example, a full 128x160 image requires 128 * 160 * 2 = 40,960 bytes of data, since each pixel uses 16 bits (5 bits red, 6 bits green, 5 bits blue). You can use a microcontroller like an ESP32 or STM32, with SPI clock speeds up to 20 MHz, to achieve refresh rates of around 30-60 frames per second for static images. This specific 1.77 inch spi mcu rgb tft display uses the ST7735S driver, which operates at 3.3V logic and requires a backlight PWM pin for brightness control. The key is to pre-convert your image to a raw RGB565 array using tools like ImageMagick or a Python script, then flash it to the microcontroller’s flash memory or stream it from an SD card. For real-world use, you’ll need to handle the command set for the ST7735S, which includes 0x11 (Sleep Out), 0x29 (Display On), and 0x2A (Column Address Set) with 4-byte parameters for x-start, x-end, y-start, and y-end. Without proper initialization, the display will show garbage or remain blank. The SPI bus uses four wires: MOSI, MISO, SCLK, and CS, plus a separate DC pin to distinguish between commands and data. MISO is often unused for write-only displays, but it’s there for reading register values during debugging. The backlight pin typically draws 20-30 mA at 3.3V, and the total power consumption for the display is around 40-50 mA when fully lit, making it suitable for battery-powered projects if you use PWM dimming.

The physical layer matters: the 1.77 inch panel has a 128x160 pixel matrix with a 0.85 mm pixel pitch, giving a 1.77-inch diagonal (about 28 mm x 35 mm active area). The SPI interface can run at 3.3V or 5V tolerant on some boards, but the ST7735S is strictly 3.3V. If you’re using a 5V Arduino Uno, you need level shifters on MOSI, SCLK, CS, and DC lines, or risk damaging the driver. The typical initialization sequence includes 14 commands, starting with a software reset (0x01, wait 150 ms), then 0x11 (Sleep Out, wait 120 ms), 0xB1 (Frame Rate Control) with 3 bytes, 0xB4 (Display Inversion Control), 0xC0 (Power Control 1) with 2 bytes, 0xC1 (Power Control 2), 0xC5 (VCOM Control 1) with 4 bytes, 0x36 (Memory Access Control) to set the orientation, 0x3A (Interface Pixel Format) set to 0x05 for 16-bit color, 0x21 (Display Inversion On), 0x2A and 0x2B for column and page address, 0x2C for memory write, and finally 0x29 (Display On). Each command must be sent with the DC pin low for commands and high for data. The SPI transaction for a single pixel involves sending 2 bytes, MSB first, with the pixel format being 5-6-5: bits 15-11 are red, 10-5 are green, and 4-0 are blue. For example, pure red is 0xF800 (1111100000000000), pure green is 0x07E0, and pure blue is 0x001F. To display a full image, you set the column address from 0 to 127 and page address from 0 to 159, then send all 40,960 bytes in a single burst. Many libraries, like Adafruit_ST7735 or TFT_eSPI, handle this automatically, but they hide the low-level details. If you’re writing your own driver, you must account for the fact that the ST7735S has a 132x162 pixel GRAM, but only 128x160 are visible. The extra pixels are in the border, and you can set the column offset using the 0x2A command with a 2-byte start and end. Some panels have a 2-pixel offset, so you’ll need to start at column 2, not 0. This is a common gotcha that causes the image to appear shifted. You can verify the offset by sending a single pixel at (0,0) and checking if it appears at the edge.

For image storage, you have two main approaches: pre-converted arrays in code or files on an SD card. A 128x160 RGB565 image takes 40,960 bytes, which is too large for most Arduino Uno’s 2 KB SRAM, so you need to store it in program memory (PROGMEM) on an AVR or in flash on an ESP32. For example, an ESP32 with 4 MB flash can hold dozens of images. The conversion process: use ImageMagick with `convert input.png -resize 128x160! -depth 16 -colorspace sRGB output.rgb` to get raw 16-bit data. Then write a Python script to read the file and output a C array: `with open('output.rgb', 'rb') as f: data = f.read(); print('const uint16_t image[] PROGMEM = {' + ','.join(f'0x{data[i]:02X}{data[i+1]:02X}' for i in range(0, len(data), 2)) + '};')`. This gives you a uint16_t array. For SD cards, use a FAT32 filesystem and read the raw file in 512-byte sectors. The SPI speed for the SD card is separate from the display SPI, so you can use two SPI buses if your MCU supports it. On an ESP32, you can use VSPI for the display and HSPI for the SD card, each running at 10-20 MHz. The display’s SPI clock speed is limited by the ST7735S’s maximum of 15 MHz (some datasheets say 20 MHz, but 15 MHz is safer). At 15 MHz, sending 40,960 bytes takes about 2.7 ms, plus command overhead, so a full frame update takes about 5 ms. This allows for 200 FPS theoretically, but the microcontroller’s loop time and image processing will limit it to 30-60 FPS in practice. For animations, you can use double buffering in the MCU’s RAM if you have enough, like an ESP32’s 520 KB SRAM can hold 12 full frames. But for a simple static image, you just send the data once and the display holds it because the ST7735S has a built-in GRAM that retains the pixel data without refresh.

The wiring is straightforward: connect the display’s VCC to 3.3V, GND to ground, SCL to SPI clock, SDA to MOSI, CS to a GPIO pin, DC to another GPIO, RST to a GPIO, and BL to a PWM-capable pin. Leave MISO unconnected if you’re not reading registers. The reset pin is active low, so you need to pull it high after a low pulse of at least 10 µs. The CS pin must be pulled low for the entire SPI transaction. The DC pin determines whether the next byte is a command (low) or data (high). For the ST7735S, commands are 8 bits, and data can be 8-bit or 16-bit depending on the register. For example, the column address command (0x2A) takes 4 bytes of data: x-start high, x-start low, x-end high, x-end low. The pixel format command (0x3A) takes 1 byte: 0x05 for 16-bit color. The memory access control (0x36) takes 1 byte: 0x00 for normal orientation, 0xC0 for rotated 180 degrees, 0x60 for landscape, etc. You can also use the MADCTL register to flip the display without changing your image data. This is useful if you mount the display in different orientations. The backlight pin can be controlled with a 1 kHz PWM signal at 50% duty to reduce power consumption. At full brightness, the backlight draws about 20 mA, and the logic draws 10 mA, so total 30 mA at 3.3V is 0.1 watts. For battery life, you can turn off the backlight with a 0% duty cycle and still have the image visible in ambient light, though it’s dim. The display’s reflectivity is low, so it’s not readable in direct sunlight without backlight.

Common pitfalls include incorrect SPI mode: the ST7735S uses mode 0 (CPOL=0, CPHA=0) or mode 3 (CPOL=1, CPHA=1), but mode 0 is standard. The data is latched on the rising edge of SCLK. If you use mode 2, the display will show random pixels. Another issue is the reset sequence: you must hold the reset pin low for at least 10 µs, then high, then wait 150 ms for the internal oscillator to stabilize. Some cheap modules have a capacitor on the reset line that causes a slow rise, so you might need a longer delay. The initialization sequence must be sent exactly as the datasheet specifies, or the display may not turn on. For example, skipping the Sleep Out command (0x11) will leave the display in sleep mode, drawing less than 1 mA but showing nothing. The Display On command (0x29) must come after all other settings. You can verify the display is working by sending a single pixel at (0,0) with red color, then checking if it appears. If it doesn’t, check your wiring, SPI speed, and command sequence. A logic analyzer is helpful to see if the SPI data is correct. The MOSI line should show the 8-bit command bytes followed by 16-bit data bytes. The DC line should toggle between low and high. The CS line should stay low for the entire transaction. If you see glitches, you might have noise on the power lines, so add a 10 µF capacitor between VCC and GND near the display. The display’s VCC pin can handle up to 3.6V, but 3.3V is preferred. Running at 5V will damage the driver permanently.

For more advanced use, you can display images from a camera module like an OV2640 on an ESP32-CAM. The camera outputs JPEG, which you can decode to RGB565 and send to the display. The ESP32’s dual-core processor allows one core to handle the camera and the other to handle the display, achieving 15-20 FPS for live video. The JPEG decoding takes about 30 ms per frame, and the SPI transfer takes 5 ms, so total 35 ms per frame, giving 28 FPS. You can optimize by reducing the resolution to 160x120 and scaling to 128x160, but that adds complexity. The display’s 128x160 resolution is low for detailed images, but it’s fine for text, icons, or simple graphics. For text, you can use a 5x7 font and display 21 characters per line and 22 lines, using about 1,200 bytes for the font bitmap. The ST7735S has a built-in character generator? No, it doesn’t, so you need to render text in software. You can store the font in flash and use a lookup table to map ASCII to pixel data. Each character is 5x7 pixels, so 5 bytes per character. For a 128x160 display, you can fit 25 columns and 22 rows of 5x7 text, but with 1-pixel spacing, it’s 21x22. This is useful for data logging or user interfaces. The display’s response time is about 10 ms, so there’s no ghosting for static images. For animations, you can use the display’s partial update feature: set a smaller window with the 0x2A and 0x2B commands, then send only the changed pixels. This reduces the data transfer to a few hundred bytes per frame, allowing for 100+ FPS for small sprites. The ST7735S supports hardware scrolling by setting the scroll start address in the 0x33 command, which shifts the display content without rewriting the GRAM. This is useful for scrolling text or menus. The scroll area can be set to a portion of the screen, leaving a static header or footer. The command takes 2 bytes for the scroll start address, and you can change it dynamically. The GRAM is not double-buffered, so you’ll see tearing if you update the image while the display is being scanned. To avoid tearing, you can wait for the vertical sync by reading the display’s status register via the MISO pin, but most applications don’t need it. The display’s refresh rate is fixed at 60 Hz, so you have 16.6 ms to update the GRAM before the next scan. If you update faster than that, you’ll see partial updates. For most static images, this isn’t an issue.

In terms of reliability, the ST7735S has a typical operating temperature range of -20°C to +70°C, so it’s not suitable for extreme environments. The SPI interface is robust, but long wires (over 10 cm) can cause signal degradation at high speeds. Use twisted pairs or shielded cables for longer distances. The display’s FPC connector is fragile, so handle it carefully. The 1.77 inch size is popular for wearable devices, but the power consumption of 30 mA at 3.3V means a 200 mAh battery lasts about 6.6 hours with the backlight on. You can extend this by using a deep sleep mode on the MCU and turning off the display’s backlight. The display itself has no sleep mode for the GRAM, so the image stays even when the MCU is off. The backlight is the main power draw. For a battery-powered project, use a PWM pin to dim the backlight to 10% duty, which reduces current to 2 mA, extending battery life to 100 hours. The display’s contrast is about 500:1, and the viewing angle is 120 degrees, so it’s readable from most angles. The color depth of 65,536 colors is sufficient for photos, but gradients may show banding. You can dither the image to reduce banding, but that requires more processing. The display’s pixel format is RGB565, which is the standard for embedded displays. If you have an 8-bit image, you need to convert it to 16-bit by mapping the 8-bit values to 5-bit and 6-bit ranges. For example, 8-bit red (0-255) maps to 5-bit red (0-31) by dividing by 8. This is a lossy conversion, but it’s fast. You can use a lookup table to speed it up. The display’s gamma correction is handled by the ST7735S’s internal registers, which you can adjust with the 0xE0 and 0xE1 commands for positive and negative gamma. The default gamma is fine for most applications, but you can tweak it for better contrast. The gamma curve has 16 control points, each with 6-bit values. This is advanced tuning and not necessary for most users.

For software support, the TFT_eSPI library for ESP32 is the most feature-rich, with support for multiple displays, touchscreens, and file systems. It uses the SPI bus efficiently and handles the command set automatically. For Arduino Uno, the Adafruit_ST7735 library works but is limited by the small SRAM. You can use the UTFT library for older MCUs. The key is to set the correct pin mappings in the library’s user setup file. For example, in TFT_eSPI, you define TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, and TFT_BL. The library also supports SPI transactions, which are atomic and prevent interrupts from corrupting the data. The SPI clock speed is set in the library, usually 20 MHz for ESP32. For the ST7735S, you need to set the display driver to ST7735. The library also handles the column offset for different modules. For the 1.77 inch display, the offset is usually 0, but some modules have a 2-pixel offset. You can set it with the `setColOffset` function. The library also supports rotation, which sets the MADCTL register. The rotation values are 0, 1, 2, 3 for 0°, 90°, 180°, 270°. The display’s native orientation is portrait (128x160), so rotation 0 is portrait. Rotation 1 is landscape (160x128), but the image will be stretched if you don’t adjust the coordinates. The library handles this automatically. For images, the library has a `pushImage` function that takes a x, y, width, height, and uint16_t array. It sets the window and sends the data. You can also use `drawBitmap` for monochrome images. The library supports JPEG decoding via the TJpgDec library, which can decode JPEG on the fly and send to the display. This is useful for photos from an SD card. The JPEG decoding uses about 20 KB of RAM for the work buffer, so it’s not suitable for Uno. On ESP32, it works fine. The decoding speed is about 1-2 seconds for a 160x128 JPEG, depending on the compression. For faster display, use raw RGB565 files. The library also supports PNG decoding via the PNGdec library, but it’s slower. For most applications, raw RGB565 is the fastest and most reliable.

In a production environment, you need to consider the display’s reliability. The ST7735S has a mean time between

Side Project Dispatch

Get the next artist profile in your inbox.

A weekly editorial dispatch for indie hip-hop creators and superfans — long reads, producer interviews, and beat-by-beat breakdowns. Free, always.

Subscribe