Позвонить Telegram Viber
График работы: пн–пт 9:00–18:00

Подключение GC9A01 к ESP32-S3 — рабочая настройка TFT_eSPI и часы по Wi-Fi

DIY32

Время сборки: 30 мин

Сложность: низкая

Компоненты

  • Display GC9A01
  • ESP32 S3 WROOM 30pin
  • DuPont

Подключение дисплея GC9A01 к ESP32-S3 — рабочая настройка TFT_eSPI и часы по Wi-Fi

Используем:

Arduino IDE и библиотека TFT_eSPI

 Круглые TFT дисплеи GC9A01 с разрешением 240×240 — популярное решение для часов, мини-панелей, погодных станций и интерфейсов на ESP32. Чаще всего такие дисплеи продаются как “1.28 IPS GC9A01 SPI display” и отлично подходят для проектов с Wi-Fi и анимацией.

 В моём случае сначала планировалось подключение к ESP32-S2 Mini, так как у него достаточно свободных GPIO. Однако с подключённым дисплеем плата вела себя нестабильно — были проблемы с прошивкой и загрузкой.

 После перехода на ESP32-S3 WROOM всё заработало нормально без дополнительных манипуляций.

  •  В конце статьи — рабочая конфигурация TFT_eSPI и готовая логика часов по Wi-Fi.

Что получилось в итоге

По итогам настройки вы получите:

  • полностью рабочий дисплей GC9A01 на ESP32-S3
  • корректную работу через TFT_eSPI
  • подключение к Wi-Fi
  • автоматическую синхронизацию времени через интернет
  • переключение между аналоговыми и цифровыми часами
  • готовую базу для DIY часов или smart display проекта
  • Настройка TFT_eSPI для GC9A01

Для работы понадобится библиотека TFT_eSPI после установки библиотеки откройте файл и настройте конфигурационный файл:

C:\Users\Name\Documents\Arduino\libraries\TFT_eSPI\User_Setup.h

В User_Setup.h укажите следующие параметры:

#define GC9A01_DRIVER

#define TFT_WIDTH  240
#define TFT_HEIGHT 240

#define TFT_CS     15  // Chip Select
#define TFT_DC     23  // Data/Command
#define TFT_RST    4   // Reset
#define TFT_MOSI   12  // SPI Data
#define TFT_SCLK   14  // SPI Clock

#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF

#define SPI_FREQUENCY  27000000

#define USER_SETUP_ID 931


Соответствие пинов: GC9A01 | ESP32-S3 (продублировано на картинке)

Вывод дисплеяПодключение к ESP32‑S3
VCC3.3V
GNDGND
SCL / CLKGPIO 14
SDA / DINGPIO 12
DCGPIO 23
CSGPIO 15
RSTGPIO 4
BL3.3V

Подсветку BL лучше сразу подключить к 3.3V, иначе экран может оставаться полностью чёрным даже при успешной инициализации SPI.

Важный нюанс с ESP32-S2

Во время тестирования ESP32-S2 Mini работал нестабильно при подключённом дисплее.

Симптомы были такие:

  • плата не всегда прошивалась
  • иногда зависала загрузка
  • COM-порт мог исчезать после подключения дисплея

Точной причины определить не удалось, но после перехода на ESP32-S3 проблема полностью исчезла.

Если дисплей мешает прошивке:

попробуйте временно отключить питание дисплея

проверьте GPIO boot strap пины

либо используйте ESP32-S3

Логика работы проекта

Дисплей используется как Wi-Fi часы с двумя режимами отображения:

  • аналоговые часы
  • цифровые часы
  • Время автоматически синхронизируется через интернет по NTP.
  • На GPIO 22 можно подключить кнопку:
  • при подаче LOW уровня отображение переключается между цифровым и аналоговым интерфейсом.

Почему отказался от изображения циферблата

Изначально была попытка использовать готовое изображение циферблата в виде массива bitmap, но появились проблемы:

  • сильная пикселизация
  • большой объём массива
  • неудобно менять дизайн
  • лишний расход памяти

В итоге оказалось проще и лучше рисовать интерфейс прямо в коде:

  • линии,
  • стрелки,
  • окружности,
  • текст.

Так изображение выглядит аккуратнее и легче масштабируется под разные проекты.

Проверка после прошивки

После загрузки скетча проверьте:

  • появляется ли изображение
  • работает ли подсветка
  • подключается ли Wi-Fi
  • обновляется ли время
  • реагирует ли кнопка на GPIO 22

Возможные проблемы

Чёрный экран

Чаще всего:

  • не подключён BL
  • ошибка в SPI пинах
  • плохой контакт Dupont

Белый экран

Обычно:

  • проблема с инициализацией SPI
  • неверное подключение DC или CS
  • Не прошивается ESP32

Возможен конфликт boot GPIO при подключённом дисплее.

Артефакты или мусор на экране

Итог

GC9A01 — один из самых удобных круглых SPI дисплеев для ESP32 проектов.

Что важно учитывать:

  • ESP32-S3 работает с ним заметно стабильнее
  • TFT_eSPI полностью решает задачу
  • подсветка BL обязательна
  • графику удобнее рисовать кодом, а не bitmap массивами

Главное правило:

Если дисплей определяется, но экран пустой — сначала проверяйте BL, SPI пины и частоту SPI, а не сам код.

Code for Arduino ADE
#include <wifi.h>
#include <time.h>
#include <tft_espi.h>

TFT_eSPI tft = TFT_eSPI();

// Настройки часов
#define CENTER_X 120
#define CENTER_Y 120
#define RADIUS 100
#define DOT_RADIUS 3
#define TRACK_RADIUS 110

// Пины
#define PIN_BUTTON 22
#define DEBOUNCE_DELAY 200

// Wi-Fi настройки
const char* ssid = "YouWiFi";
const char* password = "YouPASS";
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3 * 3600;
const int daylightOffset_sec = 0;

// Переменные времени
struct tm timeinfo;
uint8_t last_second = 61;
bool showAnalogClock = true;

// Позиции стрелок
int16_t last_hx, last_hy, last_mx, last_my;

// Прототипы функций
void drawClockFace();
void updateAnalogClock(uint8_t prevSecond);
void drawDotAtSecond(uint8_t sec, uint16_t color);
void displayDigitalTime();

void setup() {
  Serial.begin(115200);
  pinMode(PIN_BUTTON, INPUT_PULLUP);

  tft.init();
  tft.setRotation(2);
  tft.fillScreen(TFT_BLACK);

  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  tft.setTextSize(2);
  tft.drawString("CONNECTING...", CENTER_X, CENTER_Y);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  tft.fillRect(CENTER_X - 80, CENTER_Y - 10, 160, 20, TFT_BLACK);
  tft.drawString("CONNECTED!", CENTER_X, CENTER_Y);
  delay(1000);

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  while (!getLocalTime(&timeinfo)) {
    Serial.println("Waiting for time...");
    delay(500);
  }

  drawClockFace();
}

void loop() {
  static uint32_t lastUpdate = 0;

  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(DEBOUNCE_DELAY);
    if (digitalRead(PIN_BUTTON) == LOW) {
      showAnalogClock = !showAnalogClock;
      tft.fillScreen(TFT_BLACK);
      if (showAnalogClock) drawClockFace();
      while (digitalRead(PIN_BUTTON) == LOW) delay(10);
    }
  }

  if (millis() - lastUpdate >= 1000) {
    lastUpdate = millis();

    if (getLocalTime(&timeinfo)) {
      if (timeinfo.tm_sec != last_second) {
        uint8_t prev_second = last_second;
        last_second = timeinfo.tm_sec;

        if (showAnalogClock) {
          updateAnalogClock(prev_second);
        } else {
          displayDigitalTime();
        }
      }
    }
  }
}

void drawClockFace() {
  tft.fillScreen(TFT_BLACK);
  tft.drawCircle(CENTER_X, CENTER_Y, RADIUS, TFT_WHITE);

  // Цифры 12, 3, 6, 9
  const char* labels[] = {"12", "3", "6", "9"};
  int angles[] = {270, 0, 90, 180};  // поправлено

  tft.setTextSize(2);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);

  for (int i = 0; i < 4; i++) {
    float angle = angles[i] * PI / 180.0;
    int x = CENTER_X + (RADIUS - 25) * cos(angle);
    int y = CENTER_Y + (RADIUS - 25) * sin(angle);
    tft.drawString(labels[i], x, y);
  }

  // Минутные метки
  for (int i = 0; i < 60; i++) {
    float angle = i * 6 * PI / 180;
    int length = (i % 5 == 0) ? 12 : 6;
    int x1 = CENTER_X + (RADIUS - length) * cos(angle);
    int y1 = CENTER_Y + (RADIUS - length) * sin(angle);
    int x2 = CENTER_X + RADIUS * cos(angle);
    int y2 = CENTER_Y + RADIUS * sin(angle);
    tft.drawLine(x1, y1, x2, y2, (i % 5 == 0) ? TFT_WHITE : TFT_DARKGREY);
  }
}

void updateAnalogClock(uint8_t prevSecond) {
  // Стираем старые стрелки и точку
  tft.drawLine(CENTER_X, CENTER_Y, last_hx, last_hy, TFT_BLACK);
  tft.drawLine(CENTER_X, CENTER_Y, last_mx, last_my, TFT_BLACK);
  drawDotAtSecond(prevSecond, TFT_BLACK);

  // Часовая стрелка
  float hourAngle = (timeinfo.tm_hour % 12 + timeinfo.tm_min / 60.0) * PI / 6;
  last_hx = CENTER_X + RADIUS * 0.5 * sin(hourAngle);
  last_hy = CENTER_Y - RADIUS * 0.5 * cos(hourAngle);

  // Минутная стрелка
  float minAngle = timeinfo.tm_min * PI / 30;
  last_mx = CENTER_X + RADIUS * 0.7 * sin(minAngle);
  last_my = CENTER_Y - RADIUS * 0.7 * cos(minAngle);

  // Рисуем часовую и минутную стрелки
  tft.drawLine(CENTER_X, CENTER_Y, last_hx, last_hy, TFT_WHITE);
  tft.drawLine(CENTER_X, CENTER_Y, last_mx, last_my, TFT_WHITE);

  // Рисуем текущую точку-секунду
  drawDotAtSecond(timeinfo.tm_sec, TFT_RED);
}

void drawDotAtSecond(uint8_t sec, uint16_t color) {
  float angle = sec * PI / 30;
  int x = CENTER_X + TRACK_RADIUS * sin(angle);
  int y = CENTER_Y - TRACK_RADIUS * cos(angle);
  tft.fillCircle(x, y, DOT_RADIUS, color);
}

void displayDigitalTime() {
  static char timeStr[9];
  sprintf(timeStr, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

  tft.setTextSize(3);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  tft.drawString(timeStr, CENTER_X, CENTER_Y);
}

С этим покупают:

  • Круглый TFT-дисплей 1.28" GC9A01 для Arduino, ESP32, STM32

    Дисплей 1.28" TFT LCD GC9A01 — купить для Arduino, ESP32, STM32 | IPS, SPI, 240×240
    320 грн
    ПОДРОБНЕЙ
  • Кабель DuPont мама-мама 20 см (2.54 мм) для Arduino и макетных плат

    Кабель DuPont мама-мама 20 см (2.54 мм) для Arduino и макетных плат
    2 грн
    ПОДРОБНЕЙ
  • Кабель DuPont папа > мама 20 см (2,54 мм)

    Кабель DuPont папа > мама 20 см (2,54 мм)
    2 грн
    ПОДРОБНЕЙ
  • Корпус для ESP32 WROOM 32D 30 pin (с доступом к пинам)

    Корпус для ESP32 WROOM 32D 30 pin (с доступом к пинам)
    60 грн
    ПОДРОБНЕЙ

Комментарии к статье

Артем22.05.2026
Не заработало пока не догадался указать настройки своей Wi Fi в скетче :)
Модератор- 22.05.2026
Дякуємо за відгук!

Добавить комментарий