Já pensou em criar o seu próprio relógio, entendendo exatamente como cada parte funciona do hardware ao código? Neste tutorial, vou mostrar passo a passo como construí um relógio digital funcional usando:
- ESP32
- Display redondo GC9A01
- RTC DS3231 (para hora precisa, com segundos reais)
Ao final, você terá um relógio que: mostra hora, minuto e segundo tem segundos “ticando” de verdade mantém a hora mesmo desligado da energia e usa um layout moderno, estilo smartwatch.
Conceito do projeto:
A ideia aqui não é comprar um smartwatch pronto, mas sim:
- entender como um relógio realmente funciona
- aprender comunicação SPI e I²C
- trabalhar com gráficos em display
- usar um RTC profissional (DS3231)
Esse tipo de projeto é excelente para quem quer sair do básico e começar a desenvolver sistemas eletrônicos reais.

Componentes utilizados
- ESP32-S3 (ou ESP32 comum)
- Display redondo GC9A01 (240×240, SPI)
- RTC DS3231
- Protoboard e jumpers
- Cabo USB
- Bateria CR2032 ou LIR2032 (para o RTC)
Ligações do hardware
Display GC9A01 → ESP32
Display ESP32
VCC 3V3
GND GND
SCK / SCL GPIO 12
SDA / MOSI GPIO 11
CS GPIO 10
DC GPIO 9
RST GPIO 8
Importante: o display usa SPI, mesmo que alguns pinos estejam escritos como “SCL” e “SDA”.
RTC DS3231 → ESP32
Neste projeto, o RTC é alimentado por um GPIO configurado como saída HIGH, pois não
havia outro ponto 3V3 disponível.
RTC ESP32
VCC GPIO 6
GND GND
SDA GPIO 4
SCL GPIO 5
Bibliotecas necessárias
Instale as seguintes bibliotecas pelo Gerenciador da IDE Arduino:
- Adafruit GFX Library
- Adafruit GC9A01A
- RTClib (Adafruit)
Lógica do funcionamento
O projeto funciona assim:
- O RTC DS3231 fornece a hora real (HH:MM:SS)
- O ESP32 lê a hora via I²C
- O display GC9A01 é controlado via SPI
- A tela é atualizada somente quando o segundo muda
- Um “tick” visual mostra o avanço dos segundos no aro do relógio
Isso garante: animação suave; zero atraso; menor consumo de processamento
Código completo do projeto
IMPORTANTE: A linha rtc.adjust(…) deve ser usada apenas uma vez para ajustar a hora
inicial. Depois disso, comente essa linha.
include <SPI.h>
include <Wire.h>
include <Adafruit_GFX.h>
include <Adafruit_GC9A01A.h>
include <RTClib.h>
include <math.h>
// ================== PINOS DO DISPLAY ==================
define PIN_SCK 12
define PIN_MOSI 11
define PIN_CS 10
define PIN_DC 9
define PIN_RST 8
// ======================================================
// ================== RTC (DS3231) ======================
define I2C_SDA 4
define I2C_SCL 5
define RTC_VCC_PIN 6
// ======================================================
Adafruit_GC9A01A tft(PIN_CS, PIN_DC, PIN_RST);
RTC_DS3231 rtc;
static const int CX = 120;
static const int CY = 120;
static int lastSecond = -1;
define C_BG 0x0000
define C_WHITE 0xFFFF
define C_LGREY 0xC618
define C_DGREY 0x7BEF
define C_RED 0xF800
int prevTickX = -1, prevTickY = -1;
void drawBezel() {
tft.fillScreen(C_BG);
tft.drawCircle(CX, CY, 119, C_DGREY);
tft.drawCircle(CX, CY, 118, C_DGREY);
tft.drawCircle(CX, CY, 117, C_DGREY);
for (int i = 0; i < 60; i++) {
float a = (i * 6.0f - 90.0f) * DEG_TO_RAD;
int r1 = (i % 5 == 0) ? 96 : 102;
int r2 = 110;
int x1 = CX + cos(a) * r1;
int y1 = CY + sin(a) * r1;
int x2 = CX + cos(a) * r2;
int y2 = CY + sin(a) * r2;
tft.drawLine(x1, y1, x2, y2,
(i % 5 == 0) ? C_LGREY : C_DGREY);
}
tft.fillRoundRect(28, 86, 184, 72, 16, C_BG);
tft.drawRoundRect(28, 86, 184, 72, 16, C_DGREY);
tft.setTextDatum(MC_DATUM);
tft.setTextSize(1);
tft.setTextColor(C_LGREY, C_BG);
tft.drawString("RTC TIME", CX, 92);
}
void drawTime(int hh, int mm, int ss) {
tft.fillRect(28, 100, 184, 56, C_BG);
char buf[12];
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hh, mm, ss);
tft.setTextDatum(MC_DATUM);
tft.setTextColor(C_WHITE, C_BG);
tft.setTextSize(3);
tft.drawString(buf, CX, CY + 8);
}
void erasePrevTick() {
if (prevTickX >= 0) {
tft.fillCircle(prevTickX, prevTickY, 4, C_BG);
}
}
void drawTickDot(int ss) {
float a = (ss * 6.0f - 90.0f) * DEG_TO_RAD;
int r = 112;
int x = CX + cos(a) * r;
int y = CY + sin(a) * r;
erasePrevTick();
tft.fillCircle(x, y, 4, C_RED);
prevTickX = x;
prevTickY = y;
}
void setup() {
Serial.begin(115200);
pinMode(RTC_VCC_PIN, OUTPUT);
digitalWrite(RTC_VCC_PIN, HIGH);
Wire.begin(I2C_SDA, I2C_SCL);
rtc.begin();
// Ajuste inicial (use uma vez)
// rtc.adjust(DateTime(2026, 1, 26, 17, 57, 0));
SPI.begin(PIN_SCK, -1, PIN_MOSI, PIN_CS);
tft.begin();
tft.setRotation(0);
drawBezel();
DateTime now = rtc.now();
drawTime(now.hour(), now.minute(), now.second());
drawTickDot(now.second());
lastSecond = now.second();
}
void loop() {
DateTime now = rtc.now();
if (now.second() != lastSecond) {
lastSecond = now.second();
drawTime(now.hour(), now.minute(), now.second());
drawTickDot(now.second());
}
}Próximos passos
Com essa base, você pode evoluir facilmente para:
- cronômetro
- modo corrida
- contagem de passos (sensor)
- sincronização por Wi-Fi (NTP)
- múltiplos watchfaces
Saiba mais sobre a parceria Saravati e Sara Educação
Este “Guia de Montagem” é uma colaboração especial entre a Saravati e a Sara Educação, criado pelo Professor Felipe Rosa. Nosso objetivo é enriquecer a comunidade de entusiastas da eletrônica, IoT e automação com recursos educacionais de alta qualidade. Através dessa parceria, buscamos inspirar e capacitar criadores em seus projetos, disponibilizando esses guias em nosso blog e nas redes sociais.

