Projeto
Projeto: relógio de pulso com a placa redonda ESP32-2424S012 (ESP32-C3 e tela de 1,28")
Passo a passo de um relógio analógico na placa redonda ESP32-2424S012, com protótipo no OLED do simulador, hora pela internet e tela que acende no botão.
Por Prof. Rodrigo Faustino · Atualizado em · 11 min de leitura
Métricas deste artigo
- Palavras
- 2.014
- Caracteres
- 10.909
- Tempo de leitura
- ~11 min
Um relógio de pulso feito em sala de aula é um daqueles projetos que fazem a turma inteira querer participar. A placa ESP32-2424S012 torna isso possível sem solda: ela é redonda, tem uma tela colorida de 1,28" com 240 × 240 pixels, uma ESP32-C3 atrás, conector para bateria, carregador e chave liga/desliga. Falta só a pulseira.
Este artigo constrói um relógio analógico — com ponteiros desenhados por seno e cosseno — que acerta a hora pela internet, apaga a tela para economizar bateria e acende quando o botão é apertado. Como a placa redonda ainda não está no simulador, o projeto começa por um protótipo: a mesma ESP32-C3 (na Super Mini) com a tela OLED da bancada, que o simulador conhece bem. A matemática, a lógica do tempo e o desenho são os mesmos; na placa redonda, só muda a tela.
Conhecendo a placa redonda
A ESP32-2424S012 usa um módulo ESP32-C3-MINI-1U, com o mesmo chip da Super Mini. Isso quer dizer que o MicroPython é o mesmo firmware (ESP32_GENERIC_C3), e tudo o que vale para a C3 — o ADC só nos GPIO0 a GPIO4, um único SPI de uso geral, o LED e o botão — vale aqui também. A diferença está no que vem ligado por dentro:
- a tela GC9A01, por SPI, nos GPIO6 (relógio), 7 (dados), 10 (seleção) e 2 (dado/comando);
- a luz de fundo da tela, no GPIO3, por um transistor — é ela que mais gasta bateria;
- nas versões com toque, o controlador CST816D, por I2C, nos GPIO4 e 5;
- o botão BOOT, no GPIO9, que o programa pode ler como um botão comum depois que a placa ligou;
- o carregador de bateria e a chave liga/desliga.
Esses pinos vêm da configuração da placa no projeto HomeDing, a fonte mais completa que encontramos. Como os anúncios nem sempre deixam claro qual versão está à venda, o primeiro passo com a placa nas mãos é conferir: o programa de teste da tela (mais adiante) responde se os pinos da tela estão certos, e um I2C(0, scl=Pin(5), sda=Pin(4)).scan() mostra o endereço 0x15 se a placa tiver toque.
Outra diferença importante: a placa não tem barra de pinos. Não dá para ligar sensores externos como na Super Mini. Ela é um relógio pronto, e o que se programa é a tela, o botão, o toque e o rádio.
Da ementa ao semestre planejado
- Plano de ensino e calendário montados sozinhos
- Diário de aulas pronto para colar no SIGA
- Slides e listas de exercícios com gabarito
Versão Community grátis · Windows
O que você vai precisar
Para o protótipo no simulador e na bancada:
Para o relógio de verdade:
- a placa ESP32-2424S012;
- uma bateria de lítio pequena, com o conector da placa (confira o conector no anúncio e na própria placa antes de comprar a bateria);
- uma pulseira de relógio comum e uma caixinha impressa em 3D, ou feita de EVA, para prender a placa;
- o driver em Python da tela GC9A01 — usamos o gc9a01py, de Russ Hughes, com licença MIT.
Passo 1: da hora ao ponteiro
Um ponteiro é uma linha que sai do centro do mostrador. O centro é fácil: numa tela de 240 × 240, é o ponto (120, 120). A ponta é que dá trabalho, e é aí que entra a trigonometria:
- o ponteiro dos minutos anda 6° por minuto (360° ÷ 60);
- o das horas anda 30° por hora (360° ÷ 12) e mais 0,5° por minuto, para não pular de uma hora para a outra;
- com o ângulo
amedido a partir das 12 horas, no sentido do relógio, a ponta fica emx = cx + r·sen(a)ey = cy − r·cos(a). O sinal de menos aparece porque, nas telas, o y cresce para baixo.
O exemplo calcula as pontas para 10h10min30s — a hora preferida das fotos de relógio — e imprime as coordenadas. Ele roda sem circuito.
import math
CX, CY = 120, 120 # centro da tela redonda de 240 x 240
def ponta(angulo_graus, raio):
a = math.radians(angulo_graus)
return round(CX + raio * math.sin(a)), round(CY - raio * math.cos(a))
def ponteiros(h, m, s):
return {
'horas': ponta(30 * (h % 12) + 0.5 * m, 55),
'minutos': ponta(6 * m + 0.1 * s, 85),
'segundos': ponta(6 * s, 95),
}
for nome, (x, y) in ponteiros(10, 10, 30).items():
print('%-8s -> (%3d, %3d)' % (nome, x, y))Confira o resultado com o desenho: às 10h10, o ponteiro das horas aponta para cima e para a esquerda (x menor que 120, y menor que 120), e o dos minutos para cima e para a direita. O dos segundos, aos 30 s, aponta direto para baixo: x igual a 120 e y bem maior. O minuto também anda um pouquinho com os segundos (0,1° por segundo), pelo mesmo motivo da hora.
Passo 2: o protótipo com a tela OLED
Na bancada, a Super Mini liga a tela OLED por I2C com SDA no GPIO4 e SCL no GPIO5 — os mesmos pinos do I2C do toque na placa redonda, para o hábito já ficar certo. A tela tem 128 × 64 pixels, então o mostrador cabe num círculo de raio 30 no lado esquerdo, e sobra espaço para a hora em números à direita.
O simulador ainda não tem o machine.RTC para acertar o relógio da placa. O programa faz o que um relógio sem internet faria: começa de uma hora inicial escrita no código e soma o tempo que passou desde que ligou. O exemplo desenha o mostrador por 3 segundos e para; abra-o com o circuito e veja os ponteiros na tela.
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import math, time
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
oled = SSD1306_I2C(128, 64, i2c)
CX, CY, R = 32, 32, 30
HORA_INICIAL = 10 * 3600 + 10 * 60 + 30 # 10:10:30, acertada à mão
ligou = time.time()
def ponta(graus, raio):
a = math.radians(graus)
return int(CX + raio * math.sin(a)), int(CY - raio * math.cos(a))
def desenhar(h, m, s):
oled.fill(0)
oled.ellipse(CX, CY, R, R, 1)
for marca in range(12): # os 12 tracinhos das horas
x1, y1 = ponta(30 * marca, R - 4)
x2, y2 = ponta(30 * marca, R - 1)
oled.line(x1, y1, x2, y2, 1)
for graus, raio in ((30 * (h % 12) + 0.5 * m, 15), (6 * m + 0.1 * s, 24), (6 * s, 27)):
x, y = ponta(graus, raio)
oled.line(CX, CY, x, y, 1)
oled.text('%02d:%02d' % (h, m), 72, 20)
oled.text(':%02d' % s, 80, 34)
oled.show()
for _ in range(3):
total = (HORA_INICIAL + time.time() - ligou) % 86400
h, m, s = total // 3600, total % 3600 // 60, total % 60
desenhar(h, m, s)
print('%02d:%02d:%02d' % (h, m, s))
time.sleep(1)O % 86400 faz o relógio "virar" à meia-noite: 86400 é o número de segundos de um dia. Repare também que o programa apaga e redesenha a tela inteira a cada segundo. Na OLED, isso é instantâneo, porque o desenho acontece na memória e só o show() manda a imagem pronta. Na tela colorida da placa redonda vai ser diferente, como explica o passo 4.
A IA que lembra da sua vida e mora no seu computador
- Memória de longo prazo com a fonte de cada resposta
- Funciona offline: seus dados ficam no seu PC
- Lê PDFs, slides, fotos e gravações de reunião
Plano Essencial grátis para sempre
Passo 3: a hora certa pela internet
Um relógio que começa de uma hora escrita no código atrasa ou adianta, e "esquece" tudo quando a bateria acaba. O jeito de ter a hora certa é perguntar a um servidor de hora pela internet, com o protocolo NTP. O MicroPython da ESP32 traz o módulo ntptime: com o Wi-Fi conectado, ntptime.settime() acerta o relógio interno da placa.
Só que o NTP entrega a hora UTC, a hora de referência mundial. O horário de Brasília é UTC−3, e o Brasil não usa horário de verão desde 2019 — então basta subtrair 3 horas. Outras regiões do país têm outros fusos: confira o seu. O exemplo faz a conta com um instante qualquer, de madrugada em UTC, para mostrar que o dia também pode mudar.
import time
FUSO_HORAS = -3 # Brasília; ajuste para o seu fuso
def hora_local(segundos_utc):
return time.localtime(segundos_utc + FUSO_HORAS * 3600)
utc = time.mktime((2026, 11, 10, 2, 5, 0, 0, 0)) # 10/11, 02:05 em UTC
a, mes, d, h, m, s, _, _ = hora_local(utc)
print('UTC: 10/11/2026 02:05')
print('local: %02d/%02d/%d %02d:%02d' % (d, mes, a, h, m))Às 2h05 de 10/11 em UTC, ainda são 23h05 de 9/11 em Brasília. Um erro comum em relógios caseiros é mostrar a hora certa com a data errada justamente nesse intervalo.
Na placa, o acerto fica assim — uma vez ao ligar e depois uma vez por dia basta, porque o relógio interno segura bem a hora entre um acerto e outro:
import network, ntptime, time
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('nome-da-rede', 'senha-da-rede')
for _ in range(20): # até uns 10 s
if wlan.isconnected():
break
time.sleep_ms(500)
if wlan.isconnected():
try:
ntptime.settime() # relógio da placa em UTC
except OSError:
pass # sem resposta: fica a hora que já tinha
wlan.active(False) # rádio desligado: economiza bateriaPasso 4: na placa redonda
Com a placa em mãos, o programa troca a OLED pela GC9A01. O driver gc9a01py tem métodos parecidos com os da OLED: fill, pixel, line, rect, fill_rect, text (com um módulo de fonte do próprio repositório do driver), e a função color565 para fazer cores. O construtor recebe o SPI e os pinos da tela.
from machine import Pin, SPI
import gc9a01py as gc9a01
spi = SPI(1, sck=Pin(6), mosi=Pin(7)) # na C3, só existe o SPI(1)
tela = gc9a01.GC9A01(
spi,
dc=Pin(2, Pin.OUT),
cs=Pin(10, Pin.OUT),
backlight=Pin(3, Pin.OUT),
rotation=0,
)
# TODO(fidelidade): a fonte dos pinos não informa um pino de reset da tela; sem ele, o driver segue sem reset.
PRETO = gc9a01.color565(0, 0, 0)
BRANCO = gc9a01.color565(255, 255, 255)
AMARELO = gc9a01.color565(232, 195, 90)
tela.fill(PRETO)
tela.line(120, 120, 175, 64, AMARELO) # um ponteiro de teste
tela.line(120, 120, 120, 30, BRANCO)Se aparecerem as duas linhas, os pinos da tela estão certos. Se a tela ficar preta, confira se a luz de fundo acendeu (o GPIO3) e teste baixar a velocidade do SPI — o argumento baudrate do SPI(); os exemplos do driver mostram valores de partida.
Na tela colorida, apagar e redesenhar tudo a cada segundo é lento: são 240 × 240 pixels, com 2 bytes cada, passando pelo SPI. A técnica clássica é apagar só os ponteiros velhos: desenhar cada ponteiro antigo de novo, na cor do fundo, e depois o novo na cor certa. O mostrador e os tracinhos ficam desenhados de uma vez só, no começo.
anteriores = {}
def ponteiro(nome, graus, raio, cor):
x, y = ponta(graus, raio) # a mesma função do passo 1
velho = anteriores.get(nome)
if velho and velho != (x, y):
tela.line(120, 120, velho[0], velho[1], PRETO) # apaga o antigo
tela.line(120, 120, x, y, cor)
anteriores[nome] = (x, y)Passo 5: economizar bateria
O artigo baterias na ESP32 dedica uma seção inteira aos relógios, e a regra principal é esta: a luz de fundo da tela é a maior conta. Um relógio de pulso não precisa ficar aceso: a pessoa olha por alguns segundos e baixa o braço.
O plano do programa fica assim:
- ao apertar o BOOT (GPIO9, que lê 0 quando apertado), acende a luz de fundo e desenha a hora;
- depois de 5 segundos sem apertar de novo, apaga a luz de fundo;
- com a tela apagada, a placa pode dormir em light sleep e acordar a cada poucos segundos para conferir o botão — ou, melhor, ser acordada pelo próprio botão;
- o Wi-Fi liga só uma vez por dia, para acertar a hora, e desliga em seguida.
Como a luz de fundo está num pino, dá até para controlar o brilho com PWM, como no artigo PWM na ESP32, e usar o mínimo que ainda dá para ler. Com o relógio pronto, meça o consumo com a tela acesa e apagada — é o único jeito de saber quanto a sua bateria vai durar.
Passo 6: do protótipo ao pulso
A parte física do relógio é onde o projeto ganha cara de produto, e vale planejar com a mesma atenção do programa:
- a bateria primeiro: ligue a bateria no conector da placa com a chave desligada, confira a polaridade (o fio vermelho é o positivo; os conectores pequenos nem sempre seguem a mesma ordem entre fabricantes) e só então ligue a chave. Uma bateria ligada ao contrário pode estragar o carregador da placa;
- a caixa: a placa redonda precisa de uma moldura que segure a borda da tela sem apertar o vidro, com espaço atrás para a bateria e uma abertura para a USB-C e outra para alcançar o botão. Uma caixa impressa em 3D é o caminho mais bonito; uma de EVA em camadas, coladas e recortadas, também funciona e é ótima para a primeira versão;
- a pulseira: uma pulseira comum de relógio, presa por duas alças na caixa, ou uma tira de velcro, que permite ajustar a qualquer pulso;
- a bateria presa: ela não pode ficar solta dentro da caixa, nem ser apertada ou dobrada. Um pedaço de fita dupla face e uma camada de espuma resolvem;
- teste de um dia: carregue, use o relógio por um dia normal e anote quanto tempo ele aguentou. Compare com a conta do artigo de baterias e descubra o que gastou mais do que o previsto.
Antes de fechar a caixa, lembre de deixar o acesso à USB-C: é por ela que a bateria carrega e que se grava um programa novo. Um relógio que precisa ser desmontado para atualizar desanima qualquer um depois da terceira vez.
Ideias para ir além
- Mostradores diferentes: digital, com números grandes; um anel que vai se completando ao longo do minuto; cores que mudam com a hora do dia. Cada um é um exercício de desenho com coordenadas.
- Toque: nas versões com o CST816D, o toque pode trocar de mostrador. O primeiro passo é o
scan()do I2C mostrar o endereço 0x15; para ler os toques, use um driver do CST816 para MicroPython. - Cronômetro e timer: a lição Timer mostra como contar o tempo sem travar o programa.
- Mensagens no pulso: o próximo artigo da série, mensageiro de pulso, transforma dois destes relógios num par de comunicadores por ESP-NOW.
O que este projeto ensina
O relógio é uma aula de matemática disfarçada: ângulos, seno e cosseno, resto da divisão para virar o dia e fusos horários com somas e subtrações. É também uma aula de engenharia: protótipo antes do produto, conferir os pinos antes de confiar no anúncio, desenhar só o que mudou porque a tela é lenta, e economizar energia porque a bateria é pequena. E, no fim, é um objeto que a pessoa usa no próprio pulso — difícil pensar em motivação melhor.
Para continuar
A trilha Displays ensina a tela OLED (varredura I2C e OLED); a trilha Tempo sem travar traz os ticks e o Timer; e a trilha Wi-Fi e HTTP mostra como conectar. O artigo I2C, OLED e LCD na ESP32 aprofunda a tela do protótipo.
Fontes
- Pinos da ESP32-2424S012: configuração da placa no projeto HomeDing; carregador e botão confirmados na desmontagem da CircuitDigest. A conferir numa placa de verdade.
- Referência do driver gc9a01py e repositório (licença MIT).
- SPI da ESP32-C3 no MicroPython v1.29.0:
ports/esp32/machine_hw_spi.c(só oSPI(1), que é o SPI2 do chip). - Documentação do MicroPython:
ntptimee a referência rápida da ESP32.
Para praticar no PyPlaca
Trilhas
Lições
- I2C: dois fios, vários módulos
- OLED: desenhe na memória, depois mostre
- Já deu a hora? ticks_ms e ticks_diff
- machine.Timer: o relógio que chama você
- Wi-Fi: a placa entra na rede