Data, hora e memória extra no Arduino com o módulo Tiny RTC e o chip DS1307

Um módulo fácil de configurar oferece a data e hora corretas – e aproveita sua bateria para também oferecer memória persistente – aos seus programas com o Arduino.

Para ter a data e hora corretas do Arduino você pode recorrer ao chip DS1307 que vem instalado (juntamente com um suporte para baterias de relógio CR2032 e uma memória EEPROM dedicada a sensores) no módulo Tiny RTC. Existem várias outras possibilidades para realizar a mesma função, mas o Tiny RTC foi o que eu preferi.

O Arduino possui temporização interna capaz de contar com relativa precisão o tempo decorrido desde a última vez em que ele foi ligado, mas ele não tem como saber se isso foi às 9 da manhã ou de madrugada, em março ou em novembro. Para acrescer a data e hora corretas e persistentes há necessidade de recursos adicionais, geralmente envolvendo um chip e uma bateria.

Vou precisar de registro da hora certa num projeto que comecei a desenvolver e, assim como fiz com os acopladores ópticos e com os shift registers, resolvi estudar previamente um método para manter e consultar a data e hora corretas, e registrar em um pequeno artigo os principais detalhes que aprendi.

O chip DS1307 é um modelo popular de RTC (Real Time Clock), e o pequenino (2,5 x 2,5cm, aproximadamente) Tiny RTC é um dos vários módulos que o encapsulam e apresentam pronto para o uso. Você pode usar a pinagem dele pelo conjunto de conectores P1 (à direita na foto), mais completo, ou pelo P2 (esquerda), mais restrito.

Os pinos GND e VCC se conectam ao GND e ao 5V do Arduino, respectivamente. Os pinos SDA e SCL conectam aos pinos SDA e SCL do seu Arduino – no Arduino Uno ou Nano, são os pinos analógicos A4 e A5, respectivamente; para outros Arduinos, consulte a documentação. Os demais pinos do módulo (BAT, DS, SQ) podem ficar desconectados, pois são para funcionalidades adicionais (que não veremos hoje): o BAT está relacionado à bateria do módulo, SQ é para acesso à funcionalidade de emissão de onda quadrada com frequência configurável oferecida pelo chip, e DS é para acesso ao termômetro disponível em alguns modelos desse módulo1.

Arduino e RTC: exemplo de programa

A comunicação entre o DS1307 e o Arduino usa o protocolo I2C, que é a especialidade da biblioteca Wire, que vem com o Arduino. Para facilitar ainda mais, vale instalar a biblioteca RTCLib (eu preferi a versão da RTCLib modificada pela Adafruit, que usei nos exemplos deste artigo), que abstrai a comunicação I2C e oferece métodos diretamente relacionados à funcionalidade de relógio, como ajustar a hora ou obter a hora atual.

O programa a seguir usa a RTCLib para verificar se o relógio do Tiny RTC já está funcionando; se não estiver, ajusta-o para a data e hora da compilação do programa, para que ele passe a funcionar. A partir daí, ele entra num loop em que mantém o led interno piscando a cada segundo, e mostra no monitor serial, a pequenos intervalos, a data e hora atuais.

#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 rtc;

void setup () {
  Wire.begin();
  rtc.begin();

  Serial.begin(9600);
  pinMode(13,OUTPUT);

  if (!rtc.isrunning()) {
    Serial.println("RTC parado, vou ajustar com a hora da compilacao...");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
}

void printnn(int n) {
  // imprime um numero com 2 digitos
  // acrescenta zero `a esquerda se necessario
  String digitos= String(n);
  if (digitos.length()==1) {
    digitos="0" + digitos;
  }  
  Serial.print(digitos); 
}  

void loop () {
    // Obtem a data e hora correntes e armazena em tstamp
    DateTime tstamp = rtc.now();

    // Mostra no monitor serial a data e hora correntes
    printnn(tstamp.day());
    Serial.print('/');
    printnn(tstamp.month());
    Serial.print('/');
    Serial.print(tstamp.year(), DEC);
    Serial.print(' ');
    printnn(tstamp.hour());
    Serial.print(':');
    printnn(tstamp.minute());
    Serial.print(':');
    printnn(tstamp.second());
    Serial.println();
    
    // espera alguns segundos piscando o led interno
    for (short i=0; i<=4; i++) {
      digitalWrite(13,HIGH);
      delay(1000);
      digitalWrite(13,LOW);
      delay(1000);
    }  
}

Destaquei em cores alguns trechos relevantes. As 5 linhas em vermelho no começo nunca faltarão: são a inclusão das 2 bibliotecas relacionadas à comunicação com o RTC, a instanciação do objeto rtc por meio do qual ocorrerá essa comunicação e, dentro da função setup(), a inicialização genérica das 2 bibliotecas.

Logo em seguida, em verde e ainda dentro da função setup(), temos um trecho que é prático mas pode ser insuficiente para o que você deseja fazer: por meio da função isrunning(), o programa verifica se o relógio do módulo já está funcionando ou não. Se não estiver, em seguida ele usa a função adjust() para ajustar o relógio do módulo para a data e hora que seu computador estava marcando no momento da compilação, de modo a que o relógio do módulo passe a funcionar. A partir desse ajuste, ele continuará enquanto a bateria interna do módulo durar, ou que haja novo ajuste.

Para completar, mais abaixo e em marrom, o programa usa a função now() para gravar na variável tstamp a data e hora atuais. Essa variável é uma estrutura do tipo DateTime, que possui propriedades que representam os vários elementos da data e hora, em inglês: veja as referências, nas linhas seguintes (que imprimem a data e hora na serial), a elementos como tstamp.minute ou tstamp.day.

De brinde, veja também a minha função printnn(), que formata números com 2 dígitos, acrescentando o zero à esquerda se necessário.

Usando a memória interna não-volátil do Tiny RTC

O chip DS1307 presente no módulo Tiny RTC não oferece apenas a hora certa: ele também disponibiliza 56 bytes (endereços 0 a 55) de memória RAM para uso livre pelo programa. Essa memória é alimentada pela mesma bateria do relógio, portanto seu conteúdo não se apaga2 quando o Arduino é desligado3.

Para quem está acostumado à vastidão de memória dos PCs, 56 bytes podem parecer uma quantidade irrisória, mas pense nas aplicações típicas de um chip de relógios: ele pode armazenar as horas para quais estão programados os seus despertadores, a temperatura máxima e mínima (nos modelos que contam com esse sensor), uma lista de fusos horários alternativos nos quais a hora pode ser exibida, a preferência do usuário quanto à luminosidade do display, e ainda sobrarão vários bytes ;-)

Existem várias maneiras de acessar essa memória interna, que você pode consultar no código da biblioteca RTC. A maneira que eu vou demonstrar é o acesso byte a byte. Para isso, substituí a função setup() do programa que vimos acima pela versão abaixo:


void setup () {
  Wire.begin();
  rtc.begin();

  Serial.begin(9600);
  pinMode(13,OUTPUT);

  if (! rtc.isrunning()) {
    Serial.println("RTC esta parado, ajustando com a data e hora da compilacao...");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  // se o byte 44 da memoria permanente do modulo contiver 
  // o valor 65 (colocado la anteriormente por este mesmo programa), 
  // sabemos que podemos exibir o dia e hora do ultimo reset.
  if (rtc.readnvram(44)==65) {
    Serial.print("O ultimo reset deste sistema foi no dia ");
    Serial.print(rtc.readnvram(40));
    Serial.print(" `as ");
    printnn(rtc.readnvram(41));
    Serial.print(":");
    printnn(rtc.readnvram(42));
    Serial.print(":");
    printnn(rtc.readnvram(43));
    Serial.println(".");
  }  

  // grava dia/hora desta inicializacao na memoria permanente do Tiny RTC
  DateTime tstamp = rtc.now();
  rtc.writenvram(40, tstamp.day());
  rtc.writenvram(41, tstamp.hour());
  rtc.writenvram(42, tstamp.minute());
  rtc.writenvram(43, tstamp.second());
  rtc.writenvram(44, 65);
}

O trecho em cinza é o mesmo da versão anterior (já explicada mais acima, neste mesmo artigo).

Vou começar pelo final, explicando o trecho em laranja. Note que ele usa a função now(), que já vimos acima, para obter a data e hora corrente. Nas 4 linhas seguintes, ele usa a função writenvram() para gravar, nos endereços 40 a 43 da memória do módulo, o dia, hora, minuto e segundo atuais.

Como este trecho está na função setup() do Arduino, ele será executado após cada vez que o sistema é ligado ou resetado, e assim teremos sempre um registro do horário em que isso aconteceu.

Falta explicar a última linha do trecho em laranja, a que grava o valor 65 no endereço 44. Trata-se de um método de assinatura, para que este mesmo programa possa saber, no futuro, que há uma data e hora gravadas nos bytes 40 a 43. Se houver o valor 65 no endereço 44, o programa pode assumir que sim4. Se houver qualquer outro valor lá, o programa pode assumir que se trata de um módulo que nunca havia sido usado por ele, e no qual ainda não foi gravado o registro de horário do reset.

Vale destacar que os endereços 40 a 44 e o valor (65) da assinatura são arbitrários, escolhidos por mim. Além disso, a forma como gravei os dados de dia e hora na memória está longe de ser a mais eficiente e precisa, mas tem a vantagem de ser fácil de entender ao ler o código. Para completar, se essa fosse uma funcionalidade real e devido à forma como o boot do Arduino acontece, o ideal seria gravar a data e horário dos 2 últimos resets, e não só do último. Mas o último basta para mostrar como essa memória funciona.

Agora vamos ao trecho em verde, bem mais simples. Ele começa lendo o que está no byte 44 da memória do módulo. Se for 65 (nossa assinatura, lembra?), ele prossegue, senão ele nem faz nada. Sendo 65, o que ele faz é simples: lê o conteúdo dos bytes 40 a 43, onde gravamos anteriormente o dia e a hora, e os exibe.

E era isso por hoje. Em breve voltarei ao tema, aí tratando sobre uma aplicação prática desse módulo. Até lá!

 
  1.  Os modelos que têm termômetro contam com um sensor DS18B20 fixado nas 3 esperas no canto ao lado do nome "Tiny RTC" na foto do módulo exibida no artigo.

  2.  A funcionalidade é similar ao uso da memória permanente do próprio Arduino, mas há diferenças, a começar pela tecnologia: a do módulo é uma memória RAM alimentada pela bateria, e a do Arduino é uma memória ROM.

  3.  Ou quando o módulo é removido, etc. Você pode até mesmo plugar o módulo a outro Arduino, ou outro tipo de controlador, e os dados ainda estarão disponíveis na memória permanente.

  4.  Ainda que com pequena chance de errar, caso algum outro programa tenha gravado exatamente esse mesmo valor no mesmo local.

Comentar

Dos leds ao Arduino, ESP8266 e mais

Aprenda eletrônica com as experiências de um geek veterano dos bits e bytes que nunca tinha soldado um led na vida, e resolveu narrar para você o que descobre enquanto explora esse universo – a partir da eletrônica básica, até chegar aos circuitos modernos.

Por Augusto Campos, autor do BR-Linux e Efetividade.net.

Recomendados

Livro recomendado


Artigos já disponíveis

Comunidade Arduino

O BR-Arduino é integrante da comunidade internacional de entusiastas do Arduino, mas não tem relação com os criadores e distribuidores do produto, nem com os detentores das marcas registradas.

Livros recomendados