ElectronicZoologyfield notes from the garage
Display • ESP32

GC9A01 Round TFT Display
with ESP32 Dev Board

Display: GC9A01 - 240x240 round IPS TFT
Board: ESP32 Dev Board (38-pin)
Interface: SPI
✓ Confirmed Working

What You Need

Parts

Software

  • Arduino IDE with ESP32 board support
  • Arduino_GFX_Library by Moon On Our Nation

Install via Arduino IDE Library Manager - search Arduino_GFX.

How It Works

The GC9A01 is a round 240x240 IPS TFT driven over SPI. It's write-only - no MISO line needed. The breakout module includes onboard voltage regulation so it runs happily from either 3.3V or 5V. The ESP32 pushes pixel data fast enough for smooth animations.

The display controller expects commands and data over 4-wire SPI: clock (SCL), data (SDA), chip select (CS), and data/command select (DC). A reset pin (RST) handles hardware resets on startup.

Wiring

Display PinESP32 PinNotes
VCC3.3V or 5VModule has onboard regulator - accepts 3.3V or 5V
GNDGND
SCLGPIO 18SPI clock (SCLK)
SDAGPIO 23SPI data (MOSI)
DCGPIO 27Data / Command select
CSGPIO 5Chip select
RSTGPIO 4Reset

No MISO connection needed - the GC9A01 is write-only. The module includes onboard voltage regulation so VCC accepts either 3.3V or 5V.

Library Setup

Open Arduino IDE, go to Tools → Manage Libraries, search for Arduino_GFX and install Arduino_GFX Library by Moon On Our Nation.

This library supports the GC9A01 natively and is significantly faster than Adafruit alternatives for this display. It also has a clean API for drawing primitives, text, and bitmaps.

Test Sketch

Flash this to confirm your wiring is correct. Renders a 150-star warp speed starfield - stars stream toward you from the centre. Smooth animation means everything is working.

/*
 * We stand on the shoulders of giants when we build
 * with knowledge gained from others' efforts.
 * That doesn't make us giants. Be humble.
 * Create with care. Open source is the way.
 *
 * GC9A01 Round TFT Display - Starfield Test Sketch
 * -------------------------------------------------
 * Renders a 150-star warp speed starfield on a
 * GC9A01 240x240 round IPS TFT display via SPI.
 * Stars stream from centre to edge with perspective.
 * Confirms wiring, library, and animation are working.
 *
 * Board:   ESP32 Dev Board (38-pin)
 * Library: Arduino_GFX_Library by Moon On Our Nation
 *
 * Wiring:
 *   VCC -> 3.3V or 5V   GND -> GND
 *   SCL -> GPIO 18       SDA -> GPIO 23
 *   DC  -> GPIO 27       CS  -> GPIO 5
 *   RST -> GPIO 4
 *
 * Open source - MIT Licence
 * Full guide and wiring diagram:
 * https://electroniczoology.com/guides/gc9a01-round-display-esp32
 *
 * Electronic Zoology - field notes from the garage
 */

#include <Arduino_GFX_Library.h>

#define TFT_DC  27
#define TFT_CS   5
#define TFT_RST  4

Arduino_DataBus *bus = new Arduino_HWSPI(TFT_DC, TFT_CS);
Arduino_GFX *gfx = new Arduino_GC9A01(bus, TFT_RST, 0, true);

#define CX       120
#define CY       120
#define NUM_STARS 150
#define SPEED    3.5f

struct Star { float x, y, z; int px, py; };
Star stars[NUM_STARS];

void resetStar(Star &s) {
  s.x  = random(-120, 120);
  s.y  = random(-120, 120);
  s.z  = random(60, 240);
  s.px = -1;
  s.py = -1;
}

// Pack a grey brightness value into RGB565
uint16_t grey(uint8_t v) {
  return ((v >> 3) << 11) | ((v >> 2) << 5) | (v >> 3);
}

void drawTitle() {
  gfx->setTextColor(0xFFFF);
  gfx->setTextSize(2);
  gfx->setCursor(60, 100);
  gfx->print("Electronic");
  gfx->setCursor(54, 122);
  gfx->print("Zoology.com");
}

void setup() {
  Serial.begin(115200);
  randomSeed(analogRead(0));
  gfx->begin();
  gfx->fillScreen(0x0000);
  for (int i = 0; i < NUM_STARS; i++) {
    resetStar(stars[i]);
    stars[i].z = random(1, 240); // spread z on startup
  }
}

void loop() {
  for (int i = 0; i < NUM_STARS; i++) {
    Star &s = stars[i];

    // Erase previous position
    if (s.px >= 0) {
      gfx->fillCircle(s.px, s.py, s.z > 120 ? 1 : 2, 0x0000);
    }

    s.z -= SPEED;
    if (s.z <= 1) { resetStar(s); continue; }

    // Perspective projection
    int sx = CX + (int)(s.x * 120.0f / s.z);
    int sy = CY + (int)(s.y * 120.0f / s.z);

    if (sx < 0 || sx >= 240 || sy < 0 || sy >= 240) {
      resetStar(s); continue;
    }

    // Closer = brighter and bigger
    uint8_t brightness = (uint8_t)((1.0f - s.z / 240.0f) * 220 + 35);
    int     radius     = s.z < 80 ? 2 : 1;

    gfx->fillCircle(sx, sy, radius, grey(brightness));
    s.px = sx;
    s.py = sy;
  }

  // Redraw title on top of stars each frame
  drawTitle();
}

Troubleshooting

White screen / no output

  • Check wiring - DC and CS are the most commonly swapped pins
  • Confirm RST is connected, not floating
  • Confirm VCC has power - module accepts 3.3V or 5V

Garbled pixels, green lines, wrong colours

  • Bad display batch - a known issue with some AliExpress AU depot stock
  • If after many hours of exhausting debugging nothing works 😩 try a different supplier
  • The code may not be the problem - try a different display before deep debugging

Display initialises but nothing draws

  • Make sure you're using Arduino_GFX_Library, not TFT_eSPI or Adafruit_GC9A01A
  • Constructor arguments differ between libraries - copy the exact sketch above

Works on desk, crashes when enclosed

  • Heat - the ESP32 WiFi radio plus SPI generates real heat in an enclosure
  • Bond a small heatsink or aluminium offcut to the ESP32