Skip to Content

Relógio de Tempo Real DS3231 com ESP32

Relógio de Tempo Real DS3231 com ESP32

Neste tutorial, vais aprender a usar um módulo DS3231 Real Time Clock (RTC) com um ESP32.

Um módulo RTC é um relógio externo que mantém o controlo da hora e data atuais. Funciona independentemente da alimentação principal do sistema, permitindo manter a hora correta mesmo quando a energia está desligada.

Vou mostrar-te como usar um RTC em conjunto com o ESP32 em deep-sleep, como ajustar o RTC para o horário de verão e como sincronizar o RTC com um servidor de hora na internet.

Peças necessárias

Para este projeto vais precisar de um Módulo DS3231 RTC e um ESP32. Estou a usar o ESP32 lite como microprocessador, pois tem uma interface de carregamento de bateria, que permite alimentar o ESP32 e o RTC com uma bateria LiPo. No entanto, qualquer outro ESP32 ou ESP8266 também funciona. Para mostrar a hora, escolhi um OLED, mas também podes usar um LCD display.

ESP32 lite Lolin32

ESP32 lite

USB data cable

Cabo USB de Dados

Módulo DS3231 RTC

Dupont wire set

Conjunto de fios Dupont

Half_breadboard56a

Breadboard

OLED display

Ecrã OLED

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Noções básicas do chip DS3231

O DS3231 é um chip pequeno (38 x 22 x 14mm) com um relógio de tempo real (RTC) de alta precisão e um oscilador de cristal compensado por temperatura integrado. Este chip inclui um suporte para bateria, permitindo funcionar mesmo quando a alimentação principal está desligada.

DS3231 Chip
Chip DS3231

O RTC mantém o controlo dos segundos, minutos, horas, dia da semana, data, mês e ano. Para meses com menos de 31 dias, a data é ajustada automaticamente, incluindo correções para anos bissextos. O relógio pode funcionar em formato 24 horas ou 12 horas. Fornece dois alarmes de calendário programáveis e uma saída de onda quadrada programável. A comunicação é feita via barramento I2C.

Um circuito de referência de tensão compensado por temperatura e um comparador monitorizam o estado do VCC, detetam falhas de energia, fornecem uma saída de reset e mudam automaticamente para a alimentação de reserva quando necessário. O dispositivo integra um sensor de temperatura digital, acessível via interface I2C.

A secção seguinte lista as principais características do DS3231 (source):

Especificações técnicas

  • Tensão de funcionamento: 3.3–5.5V
  • Chip do relógio: Chip de relógio de alta precisão DS3231
  • Precisão do relógio: 2ppm na faixa de 0-40°C, erro anual aproximadamente 1 minuto
  • Dois alarmes de calendário
  • Saída de onda quadrada programável
  • Relógio de tempo real que gera segundos, minutos, horas, dia da semana, data, mês e ano, com compensação para anos bissextos válida até 2100
  • Sensor de temperatura incorporado com precisão de ±3°C
  • Chip de armazenamento: AT24C32 (capacidade de armazenamento de 32K)
  • Interface de barramento IIC, velocidade máxima de transmissão 400KHz (quando a tensão de funcionamento é 5V)
  • Pode ser encadeado com outros dispositivos IIC, o endereço 24C32 pode ser modificado ligando A0/A1/A2, o endereço padrão é 0x57
  • Usa bateria CR2032 para garantir que o relógio continua a funcionar normalmente após uma falha de energia

Diagrama de blocos do DS3231

O diagrama de blocos seguinte mostra os componentes internos do DS3231. Podes ver o oscilador de cristal, o sensor de temperatura, o controlo de energia e a interface I2C.

Block Diagram of DS3231 Chip
Diagrama de blocos do chip DS3231

Os pinos do DS3231 são os seguintes: VCC e GND para alimentação geral e VBAT para a bateria de reserva. SCL e SDA são para a interface I2C.

O pino rotulado “33kHz” fornece o sinal de relógio de 32kHz. INT/SQW é um pino de interrupção que pode ser usado para sinalizar alarmes ou servir como saída de onda quadrada programável.

RST é o pino de reset que pode ser usado para sinalizar ao microprocessador conectado que a energia foi perdida ou restaurada. Os pinos “33kHz”, INT/SQW e RST têm outras funções também. Para mais detalhes, consulta o datasheet ligado abaixo:

Noções básicas do módulo DS3231 RTC

O chip DS3231 é demasiado pequeno para ligar diretamente a um ESP32 e também falta-lhe algumas peças necessárias. Por isso, normalmente usas um módulo DS3231 RTC. O módulo tem os componentes em falta e também um suporte para uma CR2032 bateria que fornece a alimentação de reserva. A imagem abaixo mostra a frente e o verso de um módulo típico DS3231 RTC:


Front and back of DS3231 RTC Module
Frente e verso do módulo DS3231 RTC

Em vez de uma CR2032 bateria, também podes usar uma bateria recarregável LIR2032 mas tens de ter cuidado com a tensão de alimentação. Falaremos mais sobre isto mais tarde.

Pinout do módulo DS3231 RTC

O pinout de um módulo DS3231 RTC é essencialmente o mesmo que o descrito para o chip DS3231.

Pinout of DS3231 RTC Module
Pinout do módulo DS3231 RTC

VCC e GND são para alimentação, com uma tensão de 3.3–5.5V. SCL e SDA são os pinos para a interface I2C (com resistores pull-up integrados de 4.7k). Os pinos 32K e SQW são saídas para o sinal de relógio de 32kHz e o sinal de onda quadrada programável, mas não vais precisar deles aqui.

O endereço I2C do módulo DS3231 RTC é configurável via três pads de solda (A0, A1, A2) que podem ser ligados. Vê a secção destacada no canto inferior direito do módulo na imagem acima. O endereço I2C padrão é 0x57, quando não há ligações. A imagem abaixo mostra todas as configurações possíveis e os endereços I2C correspondentes.

Setting I2C address of the DS3231 RTC Module
Configuração do endereço I2C do módulo DS3231 RTC (source)

Carregamento da bateria com o módulo DS3231 RTC

O módulo DS3231 RTC permite carregar a bateria de reserva a partir da alimentação principal (VCC). A imagem abaixo mostra o diagrama do circuito do módulo DS3231 RTC. Na secção marcada a amarelo, vês um resistor de 200Ω e um diodo 1N4148 que atuam como um circuito de carregamento muito simples.

Schematic  of DS3231 RTC Module
Esquema do módulo DS3231 RTC

No entanto, precisas de desativar este circuito de carregamento quando usares uma bateria CR2032 não recarregável com uma tensão de alimentação VCC de 5V. E se usares uma bateria recarregável LIR2032, o VCC nunca deve ultrapassar 4.7 volts para um carregamento seguro.

O artigo Battery charging circuit of DS3231 module descreve os problemas do circuito de carregamento em mais detalhe e também mostra como desativá-lo. A tabela seguinte do artigo lista os diferentes cenários com diferentes tensões de alimentação e tipos de bateria, e o que deve ser feito:

Tipo de bateriaAlimentação 3.3 VAlimentação 5 V
CR2032Bateria não afetadaDesativar circuito de carregamento
LIR2032Bateria não afetada
Carregamento não funciona
Desativar circuito de carregamento, ou
Garantir que os 5 V são na verdade 4.7 V

Ligação do módulo DS3231 RTC ao ESP32 lite

Ligar o módulo DS3231 RTC a um ESP32 é simples. Primeiro liga o SCL do DS3231 ao pino 23 do ESP32. Depois liga o SDA ao pino 19 do ESP32. Finalmente, liga o terra ao GND e 3.3V ao VCC como mostrado abaixo:

Connecting DS3231 RTC Module to ESP32 lite
Ligação do módulo DS3231 RTC ao ESP32 lite

Como estamos a alimentar o módulo DS3231 RTC a 3.3V, podes ter uma bateria CR2032 inserida enquanto está ligado à alimentação.

Código de teste simples para o módulo DS3231 RTC

Vais precisar de uma biblioteca de software para comunicar com o módulo DS3231 RTC. Existem várias, mas gosto mais da Arduino-DS3231 biblioteca de Korneliusz Jarzębski. Para a instalar, vai a github repo e clica no botão verde “Code”. Depois seleciona “Download Zip” no menu, como mostrado abaixo:

Download ZIP file for Arduino-DS3231library
Descarregar ficheiro ZIP da biblioteca Arduino-DS3231

Isto vai descarregar um ficheiro chamado “Arduino-DS3231-dev.zip” para o teu computador. Para instalar esta biblioteca ZIP, segue os passos habituais. Clica em Sketch -> Add .ZIP Library, e depois seleciona o ficheiro Arduino-DS3231-dev.zip que acabaste de descarregar.

Installing a .ZIP Arduino library
Instalar uma biblioteca Arduino a partir de um ficheiro .ZIP

Com a biblioteca instalada, podemos testar a função do módulo DS3231 RTC. O código simples seguinte define a hora e data no módulo RTC para a hora de compilação do sketch e depois imprime a hora e data num ciclo:

#include "Wire.h"
#include "DS3231.h"

DS3231 rtc;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(__DATE__, __TIME__);
}

void loop() {
  RTCDateTime  dt = rtc.getDateTime();
  Serial.printf("%4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
  delay(1000);
}

O código começa por incluir a biblioteca Wire.h para comunicação I2C e a biblioteca DS3231.h para comunicar com o DS3231 RTC.

De seguida, criamos uma instância da classe DS3231. Na função setup(), inicializamos o RTC e definimos a data e hora atuais usando rtc.setDateTime(). As macros __DATE__ e __TIME__ inserem automaticamente a data e hora em que o sketch foi compilado. Nota que a hora não será atual quando o ESP32 for reiniciado após o código ser compilado e carregado, mas vamos tratar disso mais tarde.

Na função loop, obtemos a data e hora atuais do RTC usando RTCDateTime dt = rtc.getDateTime() e usamos printf para imprimir a data e hora no Monitor Serial.

Exemplo de saída

Se carregares o código e abrires o Monitor Serial, deverás ver a data e hora impressas da seguinte forma:

Data e hora do RTC impressas no Monitor Serial

Horário de Verão com o módulo DS3231 RTC

Embora o DS3231 mantenha o controlo preciso da data e hora, não ajusta automaticamente para o Horário de Verão (DST). Isso significa que o DS3231 vai mostrar a hora (e data) errada quando o teu país muda para DST ou volta ao Horário Normal (ST).

Existem basicamente duas formas de lidar com isto. 1) podes adicionar um botão que muda manualmente entre DST e ST e vice-versa. Obviamente, isto não é o ideal. 2) adicionas código que faz a mudança automaticamente para o fuso horário do teu país.

Nesta secção, mostro-te como lidar automaticamente com o horário de verão, usando o módulo DS3231 RTC. Vamos usar a biblioteca ezTime Library para isso. Podes instalá-la via Library Manager como de costume:

Install ezTime Library via Library Manager
Instalar a biblioteca ezTime via Library Manager

Depois de instalada, podes usá-la para ajustar o DS3231 RTC ao horário de verão com o seguinte código:

#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne

Timezone loc;
DS3231 rtc;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(2024,12,4,3,16,30);  // UTC
  loc.setPosix(TIMEZONE);
}

void loop() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                
               
  Serial.println();

  delay(5000);
}

No código acima, fazemos a interface de um ESP32 com um módulo DS3231 Real-Time Clock (RTC) para manter o controlo da hora atual, incluindo ajustes para o horário de verão. O programa inicializa o RTC, define uma data e hora específicas, e depois recupera e mostra continuamente a hora UTC e local a cada 5 segundos.

Vamos decompor o código em componentes para uma melhor compreensão.

Bibliotecas incluídas

Começamos por incluir as bibliotecas necessárias para o projeto. A biblioteca Wire.h é usada para comunicação I2C, que é como o ESP32 comunica com o DS3231 RTC. A biblioteca DS3231.h fornece funções específicas para interagir com o módulo DS3231, e ezTime.h é usada para gerir fusos horários e ajustes de horário de verão.

#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"

Constantes e variáveis

De seguida, definimos uma constante para o fuso horário. Neste caso, usamos AEST-10AEDT,M10.1.0,M4.1.0/3, que corresponde a Melbourne, Austrália. Esta string indica o desvio do horário padrão e as regras para o horário de verão.

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne

As partes desta definição de fuso horário são as seguintes

  • AEST: Australian Eastern Standard Time
  • -10: Desvio UTC de 10 horas à frente do Tempo Universal Coordenado (UTC)
  • AEDT: Australian Eastern Daylight Time
  • M10.1.0: A transição para o horário de verão ocorre no 1º domingo de outubro
  • M4.1.0/3: A transição de volta para o horário padrão ocorre no 1º domingo de abril, com uma diferença de 3 horas em relação ao UTC.

Para outras definições de fuso horário, consulta o Posix Timezones Database. Basta copiar a string que lá encontras e alterar a constante TIMEZONE em conformidade.

Também criamos instâncias das classes Timezone e DS3231. A variável loc vai guardar a informação da hora local, enquanto rtc vai gerir o módulo RTC.

Timezone loc;
DS3231 rtc;

Função setup

Na função setup(), inicializamos a comunicação serial a 9600 baud para fins de depuração. Depois inicializamos o RTC e definimos uma data e hora específicas (4 de dezembro de 2024, às 03:16:30 UTC). Finalmente, definimos o fuso horário usando a constante definida anteriormente.

void setup() {
  Serial.begin(9600);
  rtc.begin();
  rtc.setDateTime(2024,12,4,3,16,30);  // UTC
  loc.setPosix(TIMEZONE);
}

Função loop

A função loop() corre continuamente. Primeiro, obtemos a data e hora atuais do RTC usando rtc.getDateTime(). Depois definimos a hora UTC usando os valores obtidos.

  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

De seguida, imprimimos a hora UTC atual no monitor serial numa string formatada. Isto inclui o ano, mês, dia, hora, minuto e segundo.

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

Também imprimimos a hora local usando a variável loc, que foi ajustada para o fuso horário e horário de verão.

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                

Finalmente, adicionamos uma linha em branco para melhor legibilidade na saída serial e introduzimos um atraso de 5000 milissegundos (5 segundos) antes do ciclo repetir.

  Serial.println();
  delay(5000);

Exemplo de saída

Se carregares o código e abrires o Monitor Serial, deverás ver a hora UTC do RTC e a hora local (LOC) impressas:

UTC and local time of RTC on Serial Monitor
Hora UTC e local do RTC no Monitor Serial

Sincronizar o DS3231 RTC com servidor SNTP

Embora o código acima agora trate automaticamente o ajuste para o horário de verão, ainda precisas de definir manualmente a hora do RTC ao iniciar o ESP32. Pior ainda, tens de ligar o ESP32 a um computador, alterar o código e reprogramar o ESP32. Isso é chato!

Poderias adicionar botões para editar a hora e data enquanto o ESP32 está a funcionar. Se quiseres fazer isso, vê o tutorial Arduino and RTC Module DS3231, onde usamos dois botões para definir a hora.

A melhor opção, no entanto, é sincronizar automaticamente o RTC com um Provedor de Hora na Internet (SNTP). Para mais informações, vê o tutorial How to synchronize ESP32 clock with SNTP server.

Se tiveres acesso consistente à internet e não te importares com o consumo de energia, não precisarias de um RTC, pois podes sincronizar regularmente o relógio interno do ESP32. Isso definiria automaticamente o relógio e também trataria do horário de verão. Vê os tutoriais Automatic Daylight Savings Time Clock e Digital Clock on e-Paper Display.

No entanto, para um projeto alimentado por bateria, queres evitar usar WiFi com muita frequência, pois consome muita energia. Um caso comum é um data logger, por exemplo para temperatura, que queres que funcione o máximo possível com bateria, mas que também precisa de carimbos de tempo precisos.

O código seguinte mostra como fazer isso. Usa WiFi apenas quando o ESP32 é reiniciado para sincronizar o RTC. Caso contrário, o ESP32 está em modo deep-sleep para poupar energia e só acorda ocasionalmente para obter a hora do RTC e imprimi-la:

#include "WiFi.h"
#include "esp_sntp.h"
#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne
const char* SSID = "SSID";
const char* PWD = "PASSWORD";
const int SLEEP = 10; // sec
 
DS3231 rtc; 
Timezone loc;

void syncTime() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED)
    ;
  configTzTime("UTC", "pool.ntp.org");
  setRtcTime();
}

void setRtcTime() {
  struct tm t;
  getLocalTime(&t);
  rtc.setDateTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
}

void printTime() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                
               
  Serial.println();

}

bool isReset() {
  return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER;
}

void setup() {
  Serial.begin(9600);
  rtc.begin();
  loc.setPosix(TIMEZONE);

  if (isReset()) {
    syncTime();
  }   
  printTime();

  esp_sleep_enable_timer_wakeup(SLEEP * 1000000);                
  esp_deep_sleep_start();
}

void loop() {
}

Vamos decompor o código em componentes para melhor compreensão.

Bibliotecas e constantes

Começamos por incluir as bibliotecas necessárias para Wi-Fi, SNTP (Simple Network Time Protocol), comunicação I2C e o DS3231 RTC. Também definimos algumas constantes para o fuso horário, credenciais Wi-Fi e duração do sono.

#include "WiFi.h"
#include "esp_sntp.h"
#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";  // Melbourne
const char* SSID = "SSID";
const char* PWD = "PASSWORD";
const int SLEEP = 10; // sec

Obviamente, terás de substituir as credenciais Wi-Fi pelas tuas próprias.

Objetos RTC e fuso horário

Criamos instâncias da classe DS3231 para o RTC e da classe Timezone para gerir os cálculos da hora local.

DS3231 rtc; 
Timezone loc;

Função de sincronização da hora

A função syncTime() liga-se à rede WiFi e configura o fuso horário para o servidor NTP. Depois chama setRtcTime() para atualizar o RTC com a hora atual.

void syncTime() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED)
    ;
  configTzTime("UTC", "pool.ntp.org");
  setRtcTime();
}

Função para definir a hora do RTC

Na função setRtcTime(), obtemos a hora local e definimos a data e hora do RTC em conformidade. O ano é ajustado somando 1900 porque o campo tm_year retorna o número de anos desde 1900.

void setRtcTime() {
  struct tm t;
  getLocalTime(&t);
  rtc.setDateTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
}

Nota que também temos de somar 1 a tm_mon para obter o mês correto, pois a estrutura de dados struct tm usa intervalos inconsistentes. Por exemplo, o dia do mês começa em 1 mas o mês do ano começa em 0:

  Member    Type  Meaning                   Range
  tm_sec    int   seconds after the minute  0-61*
  tm_min    int   minutes after the hour    0-59
  tm_hour   int   hours since midnight      0-23
  tm_mday   int   day of the month          1-31
  tm_mon    int   months since January      0-11
  tm_year   int   years since 1900
  tm_wday   int   days since Sunday         0-6
  tm_yday   int   days since January 1      0-365
  tm_isdst  int   Daylight Saving Time flag

Função para imprimir a hora

A função printTime() obtém a data e hora atuais do RTC e imprime-as no Monitor Serial. Também imprime a hora local usando o objeto do fuso horário.

void printTime() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);

  Serial.printf("RTC: %4d-%02d-%02d %02d:%02d:%02d\n",
                dt.year, dt.month, dt.day, 
                dt.hour, dt.minute, dt.second);

  Serial.printf("LOC: %4d-%02d-%02d %02d:%02d:%02d\n",
                loc.year(), loc.month(), loc.day(), 
                loc.hour(), loc.minute(), loc.second());                

  Serial.println();
}

Função para verificar o reset

A função isReset() verifica se o ESP32 acordou do deep sleep devido a um temporizador ou outra causa. Isto ajuda a determinar se deve sincronizar a hora ou não.

bool isReset() {
  return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER;
}

Função setup

Na função setup(), inicializamos a comunicação Serial, o RTC e definimos o fuso horário. Se o ESP32 estiver a iniciar do zero (não a acordar do deep sleep), sincronizamos a hora. Finalmente, imprimimos a hora e colocamos o ESP32 em modo deep sleep pela duração especificada.

void setup() {
  Serial.begin(9600);
  rtc.begin();
  loc.setPosix(TIMEZONE);

  if (isReset()) {
    syncTime();
  }   
  printTime();

  esp_sleep_enable_timer_wakeup(SLEEP * 1000000);                
  esp_deep_sleep_start();
}

Função loop

A função loop() está vazia porque o ESP32 não executa código enquanto está em deep sleep. Só acorda para executar a função setup() novamente após o tempo de sono.

void loop() { }

Mostrar a hora do RTC no OLED

Como exemplo final, quero mostrar-te como adicionar um OLED para mostrar a hora e data do RTC num ecrã. Isto é apenas uma extensão simples do código acima.

Ligar o OLED é fácil, pois também é um dispositivo I2C. Basta ligar SDA, SCL, VCC e GND do OLED em paralelo com o DS3231, como mostrado abaixo

Connecting OLED and DS3231 to ESP32 lite
Ligação do OLED e DS3231 ao ESP32 lite

Abaixo está o código que mostra a hora e data no OLED. Nota que usa a biblioteca Adafruit_SSD1306, que podes instalar via Library Manager como de costume.

#include "WiFi.h"
#include "esp_sntp.h"
#include "Wire.h"
#include "DS3231.h"
#include "ezTime.h"
#include "Adafruit_SSD1306.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";
const char* SSID = "SSID";
const char* PWD = "PASSWORD";
const int SLEEP = 10; // sec
 
DS3231 rtc; 
Timezone loc;
Adafruit_SSD1306 oled(128, 64, &Wire, -1);

void oled_init() {
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.setTextSize(2);
  oled.setTextColor(WHITE);
}

void syncTime() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED)
    ;
  configTime(0, 0, "pool.ntp.org");
  setRtcTime();
}

void setRtcTime() {
  struct tm t;
  getLocalTime(&t);
  rtc.setDateTime(t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
}

void displayTime() {
  RTCDateTime dt = rtc.getDateTime();
  UTC.setTime(dt.hour, dt.minute, dt.second, dt.day, dt.month, dt.year);
  oled.clearDisplay();
  oled.setCursor(10, 15);
  oled.println(loc.dateTime("H:i:s"));  
  oled.setCursor(2, 45);
  oled.println(loc.dateTime("d/m/Y"));  
  oled.display();
}

bool isReset() {
  return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER;
}

void setup() {
  rtc.begin();
  loc.setPosix(TIMEZONE);
  oled_init();

  if (isReset()) {
    syncTime();
  }   
  displayTime();

  esp_sleep_enable_timer_wakeup(SLEEP * 1000000);                
  esp_deep_sleep_start();
}

void loop() {
}

As únicas adições e alterações ao código são a função oled_init(), que inicializa o OLED, e a função displayTime(), que mostra a hora e data no OLED. Se carregares o código no teu ESP32, deverás ver o seguinte no OLED:

Time and date displayed on OLED
Hora e data mostradas no OLED

E é tudo! Agora deves ser capaz de usar o DS3231 RTC juntamente com um ESP32.

Conclusões

Neste tutorial aprendeste a usar um módulo DS3231 Real Time Clock (RTC) com um ESP32.

O ESP32 lite com um relógio de tempo real é especialmente adequado para projetos alimentados por bateria que precisam de manter a hora correta sem consumir muita energia. Casos comuns são data loggers ou relógios eletrónicos. Para estes últimos, recomendo ecrãs e-Paper, pois consomem quase nenhuma energia. Vê os nossos tutoriais, Analog Clock on e-Paper Display e Digital Clock on e-Paper Display.

Se quiseres aprender um pouco mais sobre relógios com diferentes tipos de ecrã e sincronização de hora, vê o Digital Clock on e-Paper Display,

Se tiveres alguma dúvida, não hesites em deixar nos comentários.

Boas experiências a criar ; )