Relógio com display LCD 128×64 e módulo RTC DS3231

Já faz um bom tempo que escrevi sobre o display gráfico LCD 128×64 aqui no blog, e sempre recebo mensagens de usuários perguntando como eu posso atualizar o display de acordo com dados de sensores e módulos. Assim, hoje vou mostrar como usar o módulo RTC DS3231 com Arduino e display gráfico 128×64, montando um relógio como esse, que além da data e hora mostra também a temperatura ambiente:

Relógio com display LCD 128x64 e RTC DS3231

módulo DS3231 é um módulo RTC (Real Time Clock, ou Relógio de Tempo Real), que conta com um sensor de temperatura e é muito mais preciso do que os modelos anteriores DS1302 ou DS1307, já que possui um cristal oscilador embutido. 

Esse módulo tem a comunicação por interface I2C e aceita alimentação de 3,3 à 5V, trabalhando com nível de sinal de 5V. Uma bateria no módulo garante que as informações de data e hora não sejam perdidas em caso de falta de alimentação do circuito.

Circuito relógio com DS3231

No nosso circuito vamos utilizar o display gráfico LCD 128×64, um potenciômetro para ajuste do contraste do display (usei um de 10K), o módulo RTC DS3231 e um push button.

Para maiores informações sobre a pinagem e funcionamento desse display, veja o post Display gráfico LCD 128×64 – ST7920.

O módulo RTC é conectado ao Arduino nos pinos A4 (SDA) e A5 (SCL). O botão está conectado ao pino digital 3, com o resistor pull-up interno do Arduino habilitado via software.

Programa display LCD 128×64 e RTC DS3231

O programa utiliza algumas bibliotecas que você terá que instalar antes de compilar. Descompacte as bibliotecas e coloque-as dentro da pasta LIBRARIES da IDE do Arduino. São elas:

Separei o programa em duas telas: a primeira (tela_1), mostra o relógio analógico, o relógio digital, e também a data atual e a temperatura.

A segunda tela (tela_2), mostra o relógio digital, o dia da semana, a data e as informações de temperatura atual, temperatura mínima e temperatura máxima. As telas são selecionadas ao pressionar o push button, ligado ao pino digital 3. 

A biblioteca u8glib (até onde eu sei), não permite que você desenhe apenas uma “parte” da tela. Assim, quando é selecionada a rotina de desenho da tela, toda a memória do display é reescrita, logo precisamos desenhar novamente as bordas, textos, valores, etc.

Essa biblioteca também é um pouco pesada, principalmente quando você utiliza muitas fontes dentro do programa. O código abaixo, por exemplo, ocupou quase 29 dos 32K disponíveis no Arduino Uno.

//Programa: Display LCD 128x64 e RTC DS3231
//Autor: Arduino e Cia

#include <U8glib.h>
#include <DS3232RTC.h>
#include <Streaming.h>
#include <Time.h>
#include <Wire.h>

//A linha abaixo define as conexoes do display e deve ser 
//ajustada conforme o modelo utilizado
U8GLIB_ST7920_128X64_1X u8g(6, 5, 4 , 7); //Enable, RW, RS, RESET

int X2 = 0;
int Y2 = 0;
int X3 = 0;
int Y3 = 0;
float angulo = 0;
int posicao = 0;
int posicaoh = 0;
int temperatura =0;
int min_temp = 500;
int max_temp = -500;

int ScreenWith = 128;
int ScreenWithC = 96;
int ScreenHeight = 64;
int ScreenHeightC = 32;
#define botao 3

int estado_botao = 0;

char* dia_da_semana[]={
  "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"};

void tela_1() 
{
  u8g.drawRFrame(0, 0, 128, 64, 3);
  //Mostra temperatura
  u8g.setPrintPos(20, 30);
  u8g.print(temperatura); 
  u8g.drawStr( 42, 30, "C");
  u8g.drawCircle(37, 34, 2);
  mostra_relogio_digital();
  //Mostra relogio analogico
  fundo_relogio();
  //Atualiza Ponteiro de horas
  desenha_ponteiro(hour()-1, 12.0, 10);
  //Atualiza Ponteiro de minutos
  desenha_ponteiro(minute()-5, 60.0, 19);
  //Atualiza Ponteiro de segundos
  desenha_ponteiro(second()-5, 60, 21);
}

void tela_2()
{
  u8g.drawRFrame(0, 0, 128, 64, 3);
  u8g.drawRFrame(68, 4, 55, 56, 2);
  mostra_relogio_digital();
  u8g.setFont(u8g_font_5x8); 
  u8g.drawStr( 78, 35, "MIN");
  u8g.drawStr( 78, 53, "MAX");
  u8g.setFont(u8g_font_6x13);
  u8g.setPrintPos(25, 41);
  u8g.print(dia_da_semana[(weekday()-1)]);
  //Mostra temperatura atual
  u8g.setPrintPos(83, 19);
  u8g.print(temperatura); 
  u8g.drawStr( 105, 19, "C");
  u8g.drawCircle(100, 12, 2);
  //Mostra temperatura minima
  u8g.setPrintPos(98, 36);
  u8g.print(min_temp); 
  u8g.drawCircle(113, 29, 2);
  //Mostra temperatura maxima
  u8g.setPrintPos(98, 54);
  u8g.print(max_temp); 
  u8g.drawCircle(113, 47, 2);
}

void desenha_ponteiro(float valor, float rotacao, int Radius) 
{
  angulo = valor * 2.0 * 3.1415 / rotacao - 1,5707;
  X2 = ScreenWithC + Radius * cos(angulo);
  Y2 = ScreenHeightC + Radius * sin(angulo);
  u8g.drawLine(ScreenWithC, ScreenHeightC, X2, Y2);
}

void fundo_relogio() 
{ 
  u8g.drawCircle(ScreenWithC, ScreenHeightC, 27);
  u8g.drawCircle(ScreenWithC, ScreenHeightC, 1);
  u8g.setFont(u8g_font_6x13);
  u8g.setFontPosTop();
  u8g.drawStr(90, 9, "12");
  u8g.drawStr(114, 25, "3");
  u8g.drawStr(94, 44, "6");
  u8g.drawStr(74, 25, "9");

  for(int traco_minuto = 0; traco_minuto<12; traco_minuto++) 
  { 
    //Desenha linhas relogio analogico
    angulo = traco_minuto / 12.0 * 2 * 3.1415;
    X2 = ScreenWithC + 25 * cos(angulo);
    Y2 = ScreenHeightC + 25 * sin(angulo);
    X3 = ScreenWithC + 25 * cos(angulo);
    Y3 = ScreenHeightC + 25 * sin(angulo);
    u8g.drawLine(X2, Y2, X3, Y3);
  }
}

void mostra_relogio_digital()
{
  //Mostra a data 
  u8g.setFont(u8g_font_5x8); 
  u8g.setPrintPos(8, 55);
  u8g.print(day());
  u8g.drawStr( 19, 55, "/");
  u8g.setPrintPos(24, 55);
  u8g.print(month());
  u8g.drawStr( 35, 55, "/");
  u8g.setPrintPos(41, 55);
  u8g.print(year());
  //Mostra hora e minutos
  u8g.drawRBox(3, 4, 62, 21,2);
  u8g.setColorIndex(0);
  u8g.setFont(u8g_font_fub17);
  u8g.drawStr(29,21,":");
  //Acerta a posicao do digito caso a hora
  //seja menor do que 10
  if (hour() < 10)
  {
    u8g.drawStr(3,23,"0");
    posicaoh = 16;
  }
  else posicaoh = 3;
  u8g.setPrintPos(posicaoh, 23);
  u8g.print(hour());
  //Acerta a posicao do digito caso o minuto
  //seja menor do que 10
  if (minute() < 10)
  {
    u8g.drawStr(38,23,"0");
    posicao = 51;
  }
  else posicao = 38;
  u8g.setPrintPos(posicao ,23);
  u8g.print(minute());
  u8g.setColorIndex(1);
}

void setup() 
{
  pinMode(3, INPUT_PULLUP);
  Serial.begin(9600);
  if ( u8g.getMode() == U8G_MODE_R3G3B2 )
    u8g.setColorIndex(255);     // white
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT )
    u8g.setColorIndex(3);         // max intensity
  else if ( u8g.getMode() == U8G_MODE_BW )
    u8g.setColorIndex(1);         // pixel on
  setSyncProvider(RTC.get);
  Serial << F("RTC Sync");
  if (timeStatus() != timeSet) Serial << F(" FAIL!");
  Serial << endl;
}

void loop() 
{
  temperatura = RTC.temperature() / 4.;
  if (temperatura >= max_temp)
  {
    max_temp = temperatura;
  }
  if (temperatura <= min_temp)
  {
    min_temp = temperatura;
  }
  static time_t tLast;
  time_t t;
  tmElements_t tm;

  //Verifica se foi setado um novo horario
  //Formato: ano, mês, dia, hora, minuto, segundo
  if (Serial.available() >= 12) {
    int y = Serial.parseInt();
    if (y >= 100 && y < 1000)
      Serial<<F("Erro: Ano deve ter dois ou quatro digitos!") <<endl;
    else {
      if (y >= 1000)
        tm.Year = CalendarYrToTm(y);
      else    //(y < 100)
      tm.Year = y2kYearToTm(y);
      tm.Month = Serial.parseInt();
      tm.Day = Serial.parseInt();
      tm.Hour = Serial.parseInt();
      tm.Minute = Serial.parseInt();
      tm.Second = Serial.parseInt();
      t = makeTime(tm);
      RTC.set(t);
      setTime(t);
      Serial << F("Horario modificado para: ");
      printDateTime(t);
      Serial << endl;
      while (Serial.available() > 0) Serial.read();
    }
  }
  t = now();
  if (t != tLast) {
    tLast = t;
    printDateTime(t);
    Serial << endl;
  }

  //Verifica se o botao foi pressionado
  boolean valor_botao = digitalRead(3);
  if (valor_botao != 1)
  {
    while(digitalRead(3) != 1)
    {
      delay(100);
    }
    // Inverte o estado
    estado_botao = !estado_botao;
  }

  //picture loop
  u8g.firstPage(); 
  do {
    if (estado_botao == 0)
    {
      tela_1();
    }
    if (estado_botao == 1)
    {
      tela_2();
    }
  } 
  while( u8g.nextPage() );
  delay(10);
}

//Mostra data e hora na serial
void printDateTime(time_t t)
{
  printI00(day(t), 0);
  Serial << monthShortStr(month(t)) << _DEC(year(t));
  Serial << ' ';
  printI00(hour(t), ':');
  printI00(minute(t), ':');
  printI00(second(t), ' ');
}

//Correcao para imprimir "00" ao inves de "0" caso
//o valor seja menor do que 10
void printI00(int val, char delim)
{
  if (val < 10) Serial << '0';
  Serial << _DEC(val);
  if (delim > 0) Serial << delim;
  return;
}

Carregue o programa e pressione o botão para alternar entre as telas. Para setar a data e a hora do módulo RTC DS3231, utilize o serial monitor e digite as informações no formato

ano, mês, dia, hora, minuto, segundo

Se você digitou as informações corretamente, a mensagem “Horario modificado para….” será exibida:

Para finalizar, um vídeo do projeto em funcionamento:


Gostou? Confira outros posts com módulos RTC aqui no Arduino e Cia!

5/5 - (2 votos)

Related posts

Como usar display LCD 20×4 com Arduino

by Arduino e Cia
10 anos ago

Como ligar um display LCD 16×2 na Raspberry Pi

by Arduino e Cia
10 anos ago

Acelerômetro e Giroscópio MPU6050

by Arduino e Cia
10 anos ago
Sair da versão mobile