13 dezembro 2015

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

Já faz um bom tempo que escrevi sobre o display gráfico LCD 128x64 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.

Relógio com display LCD 128x64 e RTC DS3231

Por isso, hoje vou mostrar como você pode montar o relógio da imagem acima, utilizando o display gráfico e também o módulo RTC DS3231.

O 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. 

Modulo RTC DS3231

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 128x64, 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 128x64 - 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.

Circuito Arduino display LCD 128x64 e DS3231


Programa display LCD 128x64 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.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
//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:

Serial monitor - Setar data e Hora

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




29 comentários:

  1. Bom dia, gostaria de saber como faço pra saber o dia da semana? Gostaria de saber se posso implementar com os dados obtidos de um gps.

    ResponderExcluir
    Respostas
    1. Boa noite Fran,

      Poderia sim, eu só não sei te dizer de antemão como você poderia pegar as informações de data e hora à partir do GPS Shield, por exemplo. Vou pesquisar isso.

      Quanto ao dia da semana, a "tela 2" do programa mostra o dia da semana, usando a função weekday().

      Abraço!

      Excluir
  2. Quando coloco para compilar, dá erro.

    ResponderExcluir
    Respostas
    1. Arduino e Cia, poderia responder minha pergunta e dos amigos que estão com o mesmo problema? Grato!!

      Excluir
    2. Oi Luan,

      Ajudo sim, se você me disser qual erro está apresentando. :)

      Abraço!

      Excluir
    3. O Arduíno da erro quando está compilando. Fiz todo procedimento correto em relação a biblioteca. Me ajude a gravar o programa. Agradeço.

      Excluir
    4. Não consigo finalizar a compilação, diz que tem algum erro. Salvei todas às bibliotecas corretamente, mas mesmo assim não compila. Pode me ajudar?

      Excluir
    5. Se caso você me vender um CI com o programa gravado, eu compro.

      Excluir
    6. Oi Luan,

      Entre em contato utilizando o formulário de contato no lado direito da página. Combinamos por email.

      Abraço!

      Excluir
  3. Amigo, você pode me ajudar a gravar esse programa no arduino, porque não estou conseguindo, só dá erro.

    ResponderExcluir
  4. Lo he montado y funciona muy bien. Gracias.
    Ustedes pueden hacer con este LCD display un analizador de audio, es decir un VUMeter?

    Saludos y nuevamente gracias.

    ResponderExcluir
  5. boa tarde amigo, tambem não consigo carregar, pois dá o seguinte erro
    Erro compilando.
    #include
    fatal error: U8glib.h: No such file or directory
    Biblioteca inválida encontrada em C:\Documents and Settings\Desktop\BIBLIOTECA RELOGIO\libraries\u8glib-master:
    Só Que as pastas estão Lá, em livraries

    ResponderExcluir
  6. Olá bom dia, muito obrigada por compartilhar, deu certinho funciona legal. Vc poderia me tirar uma duvida? seria possivel eu eliminar o computador, e usar um modulo bluetoth para fazer os ajustes? tipo esse modulos baratinho HC5 ou HC6!

    ResponderExcluir
    Respostas
    1. Boa noite,

      Preciso fazer testes, mas a princípio é uma boa solução para ajuste remoto do relógio. vou pesquisar! :)

      Abraço e obrigado!

      Excluir
    2. Oii, você conseguiu gravar certinho o programa no Arduíno? Eu não estou conseguindo, dá erro ao compilar.

      Excluir
  7. Boa noite, qual simulador você usa para montar o projeto da imagem?
    Obrigada

    ResponderExcluir
    Respostas
    1. Oi Priscila,

      Para desenhar os circuitos, eu uso o Fritzing: fritzing.org

      Abraço!

      Adilson

      Excluir
  8. olá, montei o projeto testei por varios dias, e constatei que ele começa a atrasar depois de algumas horas de uso, no final de 3 dias ele ficou 10 segundos atrasados em relação ao meu relogio. ja troquei o modulo RTC, ja mudei algumas coisas no codigo mas não resolve. isso é normal nos RTC ja que são de presição.

    ResponderExcluir
    Respostas
    1. Oi Sidnei,

      Os módulos RTC são sensíveis à variação de temperatura (principalmente o DS1307). O DS3231 e menos sensível, mas ainda assim eu já tive notícia de algumas variações durante o uso.

      Abraço!

      Excluir
  9. Olá, como imprimo uma tela simples ? Ex; Tenho tela 1 e tela 2. quero um botão para escolher entre ambas.

    ResponderExcluir
  10. o que é esse u8g.firstPage(); e o u8g.nextPage()

    ResponderExcluir
  11. ola, como faço para colocar o digito do calendário (tela 2) ao centro quando os meses nao tem dois dígitos (ex Agosto = 08)

    ResponderExcluir
    Respostas
    1. Oi André,

      Você pode montar uma lógica para centralizar o número, de acordo com a quantidade de pixels, ou substituir a linha 118 do programa, esta:

      u8g.drawStr( 19, 55, "/");

      por outras assim:
      if (month() <=10)
      {u8g.drawStr( 19, 55, "/0");}

      Assim você insere a barra e um "zero" antes de imprimir o mês.

      Acho que fica melhor. :)

      Abraço!

      Excluir
  12. Olá, você poderia me ajudar, preciso realizar um projeto que apareça a mensagem de boa vinda em um modulo matrix max7219 e depois apareça a data, hora e temperatura no modulo ds3231 rtc.. poderia me ajudar?

    ResponderExcluir
    Respostas
    1. Oi Adenilson,

      Qual módulo você está usando ? Aquele com apenas 1 display ?

      Abraço!

      Excluir
    2. Irei usar três dislay e um modulo rtc DS3231.. consegue me ajudar?

      Excluir
  13. Boa tarde
    E o seguinte eu tenho o modulo ds3231
    Quando eu fecho o serial ele para de ler
    ai quando eu abro o serial e começa a contar do zero
    o que tenho que fazer pra parar de acontecer isto ?

    Se puder ajudar eu agradeço

    ResponderExcluir
    Respostas
    1. Oi Yago,

      O horário deveria ser alterado somente após você enviar os comandos pela serial. Você alterou alguma coisa no código ?

      Abraço!

      Excluir
  14. printI00(hour(t), ' : '); esta linha aparece grifada e da erro ao compilar alguem pode me dizer o que fazer :?

    ResponderExcluir