Explorando o Módulo RTC DS3231 com Arduino

Conheça com detalhes o módulo DS3231, um relógio de tempo real (Real Time Clock) que traz maior precisão ao controle do tempo com Arduino


Existem muitos tutoriais sobre RTC com Arduino na Internet, mas a maioria é dedicada ao DS1307 e não existe muito material sobre o DS3231 em português que aborde suas características, como definição de alarmes e medição da temperatura.

Neste tutorial, vamos explicar como tirar o máximo desse pop star do Mundo Arduino, embora já existam novas versões pipocando por aí, como DS3234.

Apresentando o CI DS3231

RTC ds3231
RTC ds3231

O DS3231 é um chip (circuito integrado) fabricado pela Maxim Integrated com as seguintes características, entre outras :

  • Controle completo e preciso de tempo (Datas e horas);;
  • Definição de até dois alarmes;
  • Sensor de temperatura;
  • Sinal programável do oscilador de onda quadrada;
  • Interface de comunicação I2C;
  • Entrada para Bateria.

O módulo DS3231

Módulo DS3231
Módulo DS3231

Existem no mercado vários módulos diferentes disponíveis para o ds3231 de diversos fabricantes mas a maioria apresenta, pelo menos, a seguinte interface:

  1. Pinos de Alimentação: VCC e GND – 3.3V ou 5V
  2. Pinos de Comunicação: SCL e SDA – comunicação I2C – Endereço padrão: 0x57
  3. Pino de interrupção: SQW – Usado para disparo de alarmes
  4. Pino do oscilador – Saída de onda quadrada do cristal.

Observações:

  • Alguns módulos, como esse da figura, disponibilizam uma segunda interface I2C para ligar um dispositivo em cascata, como um LCD, por exemplo.
  • A bateria (CR2032 ou compatível) dura, em média, 5 anos e é usada quando ocorre queda da energia principal
  • Esse módulo possui uma memória EEPROM (AT24C32) de 32K, útil para armazenar a configuração do relógio e como DATA LOGGER. O endereço da memória pode ser configurado através dos jumpers A0, A1 e A2,
  • Devido à sua tensão de trabalho, esses módulos funcionam bem em outras plataformas como o ESP*, Attiny, etc. É preciso tomar cuidado apenas na escolha da library que deve ser compatível
  • Veja mais detalhes no diagrama desse módulo nas referências mais abaixo.

Onde comprar: Curto Circuito

Esquema de ligação

Arduino e DS3231
Arduino e DS3231

Observações:

  • Use o pino SQW se deseja controlar alarmes através de interrupções;
  • O pino 32k raramente é utilizado, apenas se quiser obter os pulsos do oscilador de cristal (para escovadores de bits);
  • No Arduino Mega, os pinos SDA e SCL são o 20 e 21, respectivamente. Consulte esse link para outras placas.

Bibliotecas

Separei aqui as principais bibliotecas disponíveis para o DS3231, com base em número de recursos e frequência de atualização:

  • RTClib – Library patrocinada pela Adafruit, o que é uma boa referência, mas não possui métodos para obter a temperatura e precário controle de alarmes. É um fork da Jeelab’s RTC library
  • DS3231 – Library mantida pela Northtern Widget
  • DS3232RT – Library de Jack Cristensen bem completa e com boa taxa de atualização. Inclui métodos para gravação na EEPROM
  • Rtc3231 – Outra biblioteca bem completa e ativa de Michael Miller
  • DS3231_Simple – Nunca foi tão fácil trabalhar com RTC. Autor: James Sleeman

Programando o DS3231

Todos exemplos de código aqui disponibilizados se baseiam na biblioteca Rtc3231 de Michael Miller. Essa biblioteca disponibiliza também o objeto RtcDateTime que representa uma estrutura de dados para armazenamento de Datas e horários, bem como o objeto RtcTemperature para obtermos a temperatura do RTC .

Obs: Para instalar essa lib, use o próprio Gerenciador de bibliotecas da IDE do Arduino no menu Sketch. Pesquise por “Makuna RTC”.

A seguir, vamos descrever alguns comandos da library e depois alguns exemplos de código…

Controlando o status do rtc

Durante o funcionamento do relógio, muitas vezes precisamos monitorar ou gerenciar seu status. A lib RtcDS3231 disponibiliza os seguintes métodos para isso:

begin – Inicia a sincronização do relógio

IsDateTimeValid – Retorna um valor booleano indicando se a data está válida.
A data pode ficar inválida quando há perda de energia da bateria ou porque a data nunca foi setada.

GetIsRunning – Retorna um valor booleano indicando se o RTC está ativado, ou seja, está computando as horas.

SetIsRunning – Ativa (true) ou desativa (false) o relógio

Enable32kHzPin – Habilita/Desabilita o pino nerd com o pulso do oscilador a uma taxa de 32KHz. Nem todas as bibliotecas oferecem essa opção de “baixo nível”

SetSquareWavePin – Possibilita configurar o pino de interrupção para usar com os alarmes ou outras funções. existe um método para configurar a frequência desse pino também.

Setando e obtendo data e hora

Existem várias ocasiões em que necessitaremos configurar a data e hora do RTC, ou seja, acertar o relógio:

  1. Quando ligamos o módulo pela primeira vez;
  2. Quando acaba a energia da bateria
  3. Horário de verão
  4. Etc.

Para isso,usamos o comando SetDateTime e passamos para ele um objeto RtcDateTime com a data e hora desejados.

Exemplos:

//Acerta a data em 1 de Dezembro de 2017 as 5 da manhã:
RtcDateTime data = RtcDateTime(2017,12,1,5,0,0);

Rtc.SetDateTime(data);

//Acerta o RTC com a data da compilação do sketch:
RtcDateTime compileDateTime(__DATE__,__TIME__);
Rtc.SetDateTime(compileDateTime);

Para obter a data e horário atuais, fazemos o contrário:

RtcDateTime now = Rtc.GetDateTime();
Serial.print(now.day());
Serial.print(now.month());
Serial.print(now.year());

Trabalhando com Alarmes

Para trabalharmos com alarmes, precisamos definir uma interrupção do Arduino que será acionada quando o alarme for atingido. Os seguintes métodos estão disponíveis:

SetSquareWavePin – Esse método habilita o pino SQW, definindo qual dos dois alarmes iremos utilizar.

SetAlarmOne e SetAlarmTwo – Define a data/horário de cada alarme através de um objeto DateTime.

LatchAlarmsTriggeredFlags – Método necessário para que os alarmes se efetivem.

Temperatura

Para exibirmos a temperatura do RTC, simplesmente fazemos como no exemplo abaixo:

RtcTemperature temp = Rtc.GetTemperature();
Serial.print(temp.AsFloat());

GRAVANDO DADOS NA EEPROM DO MÓDULO

Nem todos os módulos do DS3231 disponibilizam uma EEPROM e, por isso, nem todas as bibliotecas oferecem funções de gravação de dados. Portanto não vamos abordar aqui esse recurso mas, se quiser ter acesso à memória do módulo, pode usar as bibliotecas abaixo que oferecem até opções para fazer um pequeno datalog:

 Exemplos

Você pode baixar os exemplos também no github.

Exemplo 1: Operações de inicialização e exibição do horário do RTC:

#include <Wire.h> //Built-in
#include <RtcDS3231.h> //Library Manager
RtcDS3231<TwoWire> Rtc(Wire); 

void setup ()
{
  Serial.begin(9600);
  Rtc.Begin();
  // Obtém a data da compilação
  RtcDateTime data_compilacao = RtcDateTime(__DATE__, __TIME__);
  if (!Rtc.IsDateTimeValid()) //Se relogio está desatualizado
  {
    Serial.println("Relógio está desatualizado!!!");
    Rtc.SetDateTime(data_compilacao); // acerta o relógio com a data da compilação
  }
  if (!Rtc.GetIsRunning()) // Ativa o RTC
  {
    Rtc.SetIsRunning(true);
  }
  RtcDateTime now = Rtc.GetDateTime(); 
  if (now < data_compilacao) // Verifica se a data está consistente
  {
    Rtc.SetDateTime(data_compilacao);
  }
  // Reset
  Rtc.Enable32kHzPin(false);
  Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
  Serial.println("Relógio ativado\n");
}

void loop ()
{
  if (!Rtc.IsDateTimeValid()) //Bateria arriou?
  {
    Serial.println("Relógio está desatualizado!!!");
  }
  RtcDateTime now = Rtc.GetDateTime(); // Obtém o horário atual
  Serial.println(formatDate(now,"d/m/y"));
  Serial.println();
  Serial.println(formatTime(now,"h:m:s"));
  Serial.println();
  RtcTemperature temp = Rtc.GetTemperature(); // Obtém temperatura
  Serial.print(temp.AsFloat());
  Serial.println("C");
  delay(5000); 
}

// Funções para formatação dos dados
String formatDate(const RtcDateTime& dt, String format) 
{
  String d = dt.Day() < 10 ? "0" + String(dt.Day()) : String(dt.Day()) ; 
  String m = dt.Month() < 10 ? "0" + String(dt.Month()) : String(dt.Month()) ;
  String y = String(dt.Year()) ;
  format.replace("d",d);
  format.replace("m",m);
  format.replace("y",y);
  return format;
}

String formatTime(const RtcDateTime& dt, String format)
{
  String h = dt.Hour() < 10 ? "0" + String(dt.Hour()) : String(dt.Hour()) ;
  String m = dt.Minute() < 10 ? "0" + String(dt.Minute()) : String(dt.Minute()) ;
  String s = dt.Second() < 10 ? "0" + String(dt.Second()) : String(dt.Second()) ;
  format.replace("h",h);
  format.replace("m",m);
  format.replace("s",s);
  return format;
}

Exemplo 2: Trabalhando com alarmes:

#include <Wire.h> 
#include <RtcDS3231.h>
RtcDS3231<TwoWire> Rtc(Wire);

// o Pino SQW deve ser ligado ao pino 2 no UNO. Isso varia de placa para placa
#define sqw_pin 2 // UNO. No Mega, mude para 19
#define int_number 0 // UNO. No Mega, mude para 4

volatile bool int_flag = false; // Variável que indica se hounve interrupção

void interrompeu() // Rotina chamada quando houver uma interrupção
{
  int_flag = true;
}

void setup ()
{
  Serial.begin(9600);

  // Pullup no pino de interrupção
  pinMode(sqw_pin, INPUT_PULLUP);

  // Configuração inicial do RTC
  Rtc.Begin();
  RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
  if (!Rtc.IsDateTimeValid())
  {
    Rtc.SetDateTime(compiled);
  }
  if (!Rtc.GetIsRunning())
  {
    Rtc.SetIsRunning(true);
  }
  RtcDateTime now = Rtc.GetDateTime();
  if (now < compiled)
  {
    Rtc.SetDateTime(compiled);
  }
  Rtc.Enable32kHzPin(false);
  Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeAlarmBoth); //Habilta 2 alarmes

  // Alarme1 daqui a 30 segundos
  RtcDateTime alarmTime = now + 30;
  DS3231AlarmOne alarm1(
    alarmTime.Day(),
    alarmTime.Hour(),
    alarmTime.Minute(),
    alarmTime.Second(),
    DS3231AlarmOneControl_HoursMinutesSecondsMatch);
  Rtc.SetAlarmOne(alarm1);

  // Alarm 2 a cada 1 minuto
  DS3231AlarmTwo alarm2(
    0,
    0,
    0,
    DS3231AlarmTwoControl_OncePerMinute);
  Rtc.SetAlarmTwo(alarm2);

  // Efetiva os alarmes
  Rtc.LatchAlarmsTriggeredFlags();

  // Configura interrupção
  attachInterrupt(int_number, interrompeu, FALLING);
}

void loop ()
{
  RtcDateTime now = Rtc.GetDateTime();
  Serial.print(formatDate(now,"d/m/y") + " ");
  Serial.println(formatTime(now,"h:m:s"));
  Serial.println();

  if (int_flag)
  {
    int_flag = false; // Reseta flag
    DS3231AlarmFlag flag = Rtc.LatchAlarmsTriggeredFlags(); //Variável para testar qual alarme disparou
    if (flag & DS3231AlarmFlag_Alarm1)
    {
      Serial.println(">>> Alarm 1 disparou <<<");
    }
    if (flag & DS3231AlarmFlag_Alarm2)
    {
      Serial.println(">>> Alarme 2 disparou <<<");
    }
  }
  delay(5000);
}

// Funções para formatação dos dados
String formatDate(const RtcDateTime& dt, String format) 
{
  String d = dt.Day() < 10 ? "0" + String(dt.Day()) : String(dt.Day()) ; 
  String m = dt.Month() < 10 ? "0" + String(dt.Month()) : String(dt.Month()) ;
  String y = String(dt.Year()) ;
  format.replace("d",d);
  format.replace("m",m);
  format.replace("y",y);
  return format;
}

String formatTime(const RtcDateTime& dt, String format)
{
  String h = dt.Hour() < 10 ? "0" + String(dt.Hour()) : String(dt.Hour()) ;
  String m = dt.Minute() < 10 ? "0" + String(dt.Minute()) : String(dt.Minute()) ;
  String s = dt.Second() < 10 ? "0" + String(dt.Second()) : String(dt.Second()) ;
  format.replace("h",h);
  format.replace("m",m);
  format.replace("s",s);
  return format;
}

Outras alternativas

Existe um módulo para o DS3231 específico para o RaspBerry Pi, pois se encaixa com facilidade nos pinos. Esse modelo não expõe de forma fácil o pino SQW e nem disponibiliza uma EEPROM.

A Adafruit também possui um breakout que não disponibiliza a EEPROM, mas em compensação possui um pino BAT para acesso da bateria e outro RST para controle de RESET dos dispositivos

Conclusão

Isso é tudo pessoal. Espero ter ajudado.

Espere para breve aqui no blog um projeto de relógio completo com o DS3231 e ESP32 com  alarmes, temperatura, qualidade do ar, cronômetro e tela touch…

Referências

 

7 comentários em “Explorando o Módulo RTC DS3231 com Arduino”

  1. Bom dia amigo José Cintra,
    Excelente tutorial sobre o DS3231 ! Parabéns.
    Sugestões para acrescentar no seu tutorial.
    Se quiser, pode usar o diagrama que fiz do módulo DS321:
    https://www.flickr.com/photos/jgustavoam/27030656519/in/album-72157663264163678/
    Mais fotos no álbum: (processo fotográfico para fazer diagrama) https://www.flickr.com/photos/jgustavoam/albums/72157663264163678
    Recomendo que cite o link do datasheet da EEPROM 24C32:
    http://www.atmel.com/images/doc0336.pdf
    A EEPROM pode ter o endereço configurável através dos jumpers A0,A1 e A2. Veja o diagrama.
    A frequencia do PINO 32 K é de 32,768 KHz.
    Os pinos da interface I2C (SCLe SDA) e os pinos SQW e 32K tem resistores de pullup de 4,7K ohms.
    A Bateria do meu módulo é uma LIR2032 (3,6V) de Li-Ion recarregável.
    Grande abraço !
    Gustavo Murta

  2. O pino 32k, se não me engano, pode ter programada a frequencia de saída como um submultiplo de 32768 Hz, onde por exemplo se pode derivar alguma frequencia que vai acionar um pino de interrupção e assim tendo um gerador externo de timer sem que se precise programar o arduino, por exemplo, para ter uma int de timer interna.

  3. Primeiramente, obrigado pelo excelente tutorial.
    No entanto, nunca li ou vi alguém explicando sobre os outros 4 pinos de interface I2C para ligar um dispositivo em cascata; portanto, eu pergunto: como deve ser conectado um segundo dispositivo no RTC?
    Deve ser ligados os dois VCC e dois GND ou somente um de cada, mesmo que seja usado os dois lados do RTC? Obrigado

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *