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á!
- 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. ↩
- 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. ↩
- 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. ↩
- Ainda que com pequena chance de errar, caso algum outro programa tenha gravado exatamente esse mesmo valor no mesmo local. ↩
Comentar