Postagem em destaque

Controle PID de Potência em Corrente Alternada - Arduino e TRIAC - Parte I

Este post é o primeiro de uma série de seis que escrevi tratando de controle de potência e PID (controle proporcional, integral e derivativo...

sábado, 23 de maio de 2015

Relógio de Tempo Real - RTC: o que é e quando é necessário usar

Um amigo, o Rodrigo, está montando no sítio um sistema de irrigação automática controlado por Arduino. Conversando sobre o projeto, sugeri a ele que comprasse um RTC, já que saber a hora do dia e mesmo o dia da semana é importante nesse tipo de projeto.

Aqui no blog tem um post que ensina inclusive como montar um RTC. Acontece que, como é parte de um outro projeto, acabou que não se falou muito do RTC. Principalmente não se falou das aplicações do bicho, o que é um pecado tendo em vista a importância do dito.

 Agora vai:
Image result for bateria de relógioRTC (Real Time Clock) é um dispositivo que armazena a data e a hora de maneira precisa ao longo do tempo. É um relógio, onde vc indica a data/hora inicial e ele passa a contar o tempo a partir daí. Ele tem uma bateria para manter a data/hora de maneira que, se o equipamento a que está conectado ficar sem alimentação, ao ser religado a hora está certa.

Esse dispositivo existe dentro de muitos aparelhos, como relógios digitais, despertadores, fornos de microondas etc. A bateria que eles usam, aquela que parece uma moeda (essa aí do lado), até tem o apelido de "bateria de relógio" por isso.

E para que a gente precisa disso no Arduino? Basicamente para três funções:

1) Quando precisamos que a data/hora seja mantida correta entre desligamentos do Arduino.

Quando a gente desliga o Arduino, o "pograminha" permanece em sua memória, mas ele para de rodar. Assim, se vc tiver um contador de tempo no seu programa (a função millis() é usada para esse fim), ele começará do zero quando for religado. Aí entra o RTC, obviamente.

2) Para manter a data/hora de um equipamento ao longo de muitos dias, ou meses.

A função millis() funciona assim: a cada milésimo de segundo o Arduino incrementa um contador interno de uma unidade. Assim, quando acessamos millis() a função retorna esse número.

Acontece que o maior número inteiro que o Arduino armazena é o que é conhecido no C como unsigned long, que é um número de 32 bits, ou seja 4 bytes.

Isso significa que o maior número inteiro que o Arduino consegue armazenar é 232, ou seja, 4.294.967.296 milissegundos. Parece muito, mas:

Um dia tem 3600 s/h * 1000 ms/s * 24h/d = 86.400.000 ms/dia.

Ou seja, o Arduino tem como contar tempo durante 4.294.967.296 ms/(86.400.000 ms/dia), ou seja, 49.7 dias, ou menos de dois meses, o que é pouco para sistemas que funcionarão durante anos.

E o que acontece quando chega no fim, quer dizer, se a gente vai acessando a função millis() e passam-se os tais quase 50 dias? "Tio, O Arduino explode???? ", perguntaria vc, aflito. Não, a variável simplesmente zera e começa a contar de novo.

3) Quando precisamos de uma medida do tempo precisa.

A função millis() é de uma precisão bastante razoável quando queremos contar o tempo em alguns segundos. Acontece que tem algumas funcionalidades do Arduino que distorcem essa função, ou seja, fazem com que ela passe a dar valores menos precisos. Uma dessas funções são as interrupções, que já foram discutidas aqui. Quando o programa principal "congela" para executar uma interrupção ele também congela o incremento da variável do millis() fazendo que a função perca em precisão, "aumentando o tamanho" de um segundo.

Bom, essas são as três razões principais pelas quais vc deve usar um RTC quando for precisar de uma medida de tempo. No mais, vc pode ir de millis() mesmo que estará de bom tamanho.

O funcionamento

O RTC mais simples é baseado em um chip chamado DS1307. Na realidade é uma família de chips, todos com código semelhante a esse, que é um chip especializado, quer dizer, feito para esse fim.

Ele basicamente armazena o valor do tempo que, uma vez inicializado, vai sendo incrementado com muita precisão pelo chip. É necessária ainda uma bateria tipo relógio, aquela da foto aí em cima, para manter a data/hora, e também uma alimentação de 3 a 5V para que possamos nos comunicar com o chip, de maneira a preservar a bateria que fica com o fim exclusivo de manter a contagem de tempo.

Diz o fabricante que o consumo de chip é da ordem de pico Ampère, quer dizer, 10-9A. Segundo os caras, a bateria consegue manter a coisa precisa por mais de 6 anos (!). Parece um adaptador de tomadas que tem lá em casa que diz que funciona em mais de 200 países, ou seja, difícil de comprovar. Na África do Sul, por exemplo, não funciona.

Mas.. tergiverso, voltemos ao que interessa.

"Bom,tio, e cumé que usamos essa bagaça?"

Piece of cake:

arduino to RTCAcima esquema de um break RTC ligado ao Arduino. Break é quando a gente tem um dispositivo que já vem mais "mastigado", ou seja, devidamente soldado e com pinos para que a gente o conecte ao mundo externo. "É a mesma coisa que shield então né?" Não, nénão. Shield é o break que se encaixa em cima do Arduino, ou seja, todo shield é um break mas nem todo break é shield, como é o caso do RTC.

 As conexões principais, ou seja, as que são obrigatórias para o RTC funcionar são:

GND e Vcc: alimentação, positivo e negativo, fios azul e vermelhor acima.
SDA e SCL: comunicação entre o Arduino e o break. É por esses fios que os dados vão e vem entre os dois dispositivos, num protocolo chamado i2c, que já foi usado em outros projetos aqui do blog.

O Arduino se comunica via i2c usando as portas A4 e A5 analógicas, que devem ser conectadas aos pinos SDA e SCL do Arduino.

Além dessas conexões podem haver outras. Existem breaks RTC que vem com um sensor de temperatura, tipo LM35 ou outro semelhante. Nesse caso vc o conecta a uma entrada/saída digital do Arduino, conforme mostrado na conexão DS/pino 2 acima.

Existe também um meio de vc saber se a bateria está ou não ok, através do pino BAT indicado acima.

Conectado o break, faça o upload do "pograminha" abaixo. Eu fiz o download o modifiquei apenas um pouco, então vai com o crédito do sujeito que fez. A vantagem dessa implementação é que a única biblioteca que ela usa é a Wire.h, que já vem quando a gente instala o Arduino. Existem algumas libs prontas que facilitam (RTCLib é uma delas), então fica a seu gosto decidir como usar.

Basicamente ele faz duas coisas:

1) Permite que vc acerte o relógio

2) Uma vez acertado, ele passa a mostrar a hora correta (essa foi a minha alteração).

Para verificar se está tudo ok com o break, rode o programa e forneça a data/hora corretas. Em seguida, desligue o Arduino e ligue novamente. Quando o programa perguntar se que vc quer atualizar data/hora, responda que não. Se ele voltar a indicar a hora correta, vc é o feliz proprietário de um RTC que funciona!

Abracadabraço,

Mauro

///////////////////////////////////////////

// RTC data and time setter              //
//                                       //
// This sample program allows the user   //
// to set the date and time of an RTC    //
// using I2C.                            //
//                                       //
// Codes by:                             //
// eGizmo Mechatronix Central            //
// Taft, Manila, Philippines             //
// http://www.egizmo.com                 //
// April 15, 2013                        //
///////////////////////////////////////////

#include <Wire.h>

const int DS1307 = 0x68; // Address of DS1307 see data sheets

const char* days[] =
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

const char* months[] =
{"January", "February", "March", "April", "May", "June", "July", "August","September", "October", "November", "December"};

// Initializes all values:
byte second = 0;
byte minute = 0;
byte hour = 0;
byte weekday = 0;
byte monthday = 0;
byte month = 0;
byte year = 0;

void setup() {
  Wire.begin();
  Serial.begin(9600);
  delay(2000); // This delay allows the MCU to read the current date and time.

  Serial.print("The current date and time is: ");
  printTime();
  Serial.println("Please change to newline ending the settings on the lower right of the Serial Monitor");
  Serial.println("Would you like to set the date and time now? Y/N");

  while (!Serial.available()) delay(10);

  if (Serial.read() == 'y' || Serial.read() == 'Y')
  // This set of functions allows the user to change the date and time
  {
    Serial.read();
    setTime();
    Serial.print("The current date and time is now: ");
    printTime();
  }
  Serial.println("Thank you.");
}


// Continuous function for converting bytes to decimals and vice versa
void loop() {
  readTime();
  printTime();
  delay(1000);
}

byte decToBcd(byte val) {
  return ((val/10*16) + (val%10));
}

byte bcdToDec(byte val) {
  return ((val/16*10) + (val%16));
}

void setTime() {
  Serial.print("Please enter the current year, 00-99. - ");
  year = readByte();
  Serial.println(year);
  Serial.print("Please enter the current month, 1-12. - ");
  month = readByte();
  Serial.println(months[month-1]);
  Serial.print("Please enter the current day of the month, 1-31. - ");
  monthday = readByte();
  Serial.println(monthday);
  Serial.println("Please enter the current day of the week, 1-7.");
  Serial.print("1 Sun | 2 Mon | 3 Tues | 4 Weds | 5 Thu | 6 Fri | 7 Sat - ");
  weekday = readByte();
  Serial.println(days[weekday-1]);
  Serial.print("Please enter the current hour in 24hr format, 0-23. - ");
  hour = readByte();
  Serial.println(hour);
  Serial.print("Please enter the current minute, 0-59. - ");
  minute = readByte();
  Serial.println(minute);
  second = 0;
  Serial.println("The data has been entered.");

  // The following codes transmits the data to the RTC
  Wire.beginTransmission(DS1307);
  Wire.write(byte(0));
  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(weekday));
  Wire.write(decToBcd(monthday));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.write(byte(0));
  Wire.endTransmission();
  // Ends transmission of data
}



byte readByte() {
  while (!Serial.available()) delay(10);
  byte reading = 0;
  byte incomingByte = Serial.read();
  while (incomingByte != '\n') {
    if (incomingByte >= '0' && incomingByte <= '9')
      reading = reading * 10 + (incomingByte - '0');
    else;
    incomingByte = Serial.read();
  }

  Serial.flush();
  return reading;
}


void printTime() {
  char buffer[3];
  const char* AMPM = 0;
  readTime();
  Serial.print(days[weekday-1]);
  Serial.print(" ");
  Serial.print(months[month-1]);
  Serial.print(" ");
  Serial.print(monthday);
  Serial.print(", 20");
  Serial.print(year);
  Serial.print(" ");
  if (hour > 12) {
    hour -= 12;
    AMPM = " PM";
  }
  else AMPM = " AM";
  Serial.print(hour);
  Serial.print(":");
  sprintf(buffer, "%02d", minute);
  Serial.print(buffer);
  Serial.print(":");
  sprintf(buffer, "%02d", second);
  Serial.print(buffer);
  Serial.println(AMPM);
}


void readTime() {
  Wire.beginTransmission(DS1307);
  Wire.write(byte(0));
  Wire.endTransmission();
  Wire.requestFrom(DS1307, 7);
  second = bcdToDec(Wire.read());
  minute = bcdToDec(Wire.read());
  hour = bcdToDec(Wire.read());
  weekday = bcdToDec(Wire.read());
  monthday = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year = bcdToDec(Wire.read());
}