Introduction: Arduino Tutorial - Clock - LCD

About: This is a channel for Homemakers and DIY where you will find useful tutorials about Arduino and IOT systems, some Gardening techniques and Woodworking projects.

Sejam bem vindos a mais um tutorial, neste tutorial iremos demonstrar e explicar a montagem de um relógio que utiliza um Arduino UNO, que controla uma pequena placa RTC "ReaL Time Clock" indicando assim a hora e data num LCD, esta placa RTC DS1307 tem a função de conservar a informação de hora e data.

Esta informação de hora e data é guardada na sua memória interna, sendo que para isso é necessário alimentar a placa através de uma pequena bateria de Lítio do tipo botão, não sendo esta dependente de uma alimentação externa para que a sua memoria continue a conservar a informação.

Alem de ser explicado o funcionamento da placa RTC, também será recordado o funcionamento do LCD1602, sendo que é neste que será escrita a informação relativa à hora e data, ambas fornecidas pela placa RTC.

Arduino Tutorial - LCD - Temperature Sensor

https://www.instructables.com/id/Arduino-Tutorial-LCD-Temperature-Sensor/

Caso queiram saber mais sobre este tipo de placa RTC DS1307 como as suas características e configurações mesmo do nosso LCD1602, vejam as suas datasheet (Ver ficheiros abaixo), alem disso também podem verificar na página oficial do Arduino as especificações detalhadas sobre o Arduino UNO através da ligação https://store.arduino.cc/arduino-uno-rev3.

Step 1: Montagem

Nesta montagem sendo um pouco mais complexa é utilizada uma "Breadboard" onde são colocadas as alimentações positivas e negativas fornecidas pelo Arduino, de forma a distribuir-las mais facilmente para os vários componentes da montagem.

Devemos sempre começar a montagem pelos componentes mais complexos e que necessitam de mais ligações, assim em primeiro lugar realizamos as ligações do LCD e só depois ligamos a placa RTC, por fim colocamos os botões, sendo estes responsáveis pela navegar no menu de acerto do nosso relógio (Ver imagem acima).

Material necessário:

  • 1x Arduino UNO;
  • 1x Cabo USB;
  • 1x Breadboard;
  • 1x LCD1602;
  • 1x Resistência 220Ω;
  • 1x Potenciómetro 10KΩ;
  • 1x RTC DS1307;
  • 3x Botões (Push Button);
  • 3x Resistências 1KΩ.

Instalação do LCD1602:

Como explicado num tutorial anterior para a montagem do LCD não é necessário utilizar todos os seus pinos, tendo cada um deles uma função especifica. Assim para explicar mais facilmente o funcionamento, estes foram agrupados em 3 grupos, o grupo Pinos de Alimentação, o Pinos de Comunicação e o Pinos de Informação (Ver legenda acima).

Pinos de Alimentação:

  • GND;
  • VCC;
  • V0;
  • LED - ou A (Anodo);
  • LED + ou K (Catodo).

As alimentações do LCD são realizadas através do pino GND e pelo pino VCC, tendo este ultimo que ter uma tensão de cerca de 5V.

No pino V0 será possível ajustar o contraste dos caracteres através de um potenciómetro de 10KΩ, que estará montado de forma a funcionar como um divisor de tensão alterando assim a tensão entre 0 e 5V (Ver imagem acima).

Os pinos A e K que alimentam a luz de fundo do LCD, sendo constituída por LED, são também ligados aos pinos GND e 5V do Arduino UNO, no entanto, devemos ligar em série uma resistência de 220Ω, para que o brilho não seja demasiado intenso, podendo danificar os LED internos do LCD.

Pinos de Comunicação:

  • RS (Register Select);
  • R/W (Read / Write);
  • E (Enable).

Nos pinos de comunicação apenas teremos que colocar o pino R/W ligado a GND para que seja permitido escrever no LCD e assim aparecer os caracteres, caso contrario podemos estar a ler o dados guardados na memoria interna do LCD ficando este em branco.

Pinos de Informação:

  • D0;
  • D1;
  • D2;
  • D3;
  • D4;
  • D5;
  • D6;
  • D7.

Nesta montagem apenas utilizamos apenas 4 dos 8 possíveis pinos de informação, mas para isso será necessário utilizar no código a biblioteca LiquidCrystal.h, permitindo ler o "Byte" de informação dividido em 2 partes, ou seja, são lidos 4bits de cada vez.

Para isso esta biblioteca controla os pinos de comunicação, permitindo que o LCD saiba qual é a forma que irá receber a informação vinda do Arduino, através de 8bits e assim utilizando os 8 pinos de informação ou através de 4btis com apenas 4 pinos.

Instalação da RTC DS1307:

Este componente tem a função de determinar a informação de data e hora, fornecendo essa informação de forma precisa, mesmo quando a alimentação externa é desligada por algum motivo. Nesta montagem utilizámos uma RTC DS1307 que contem 2 conjuntos de pinos, contendo ambos os grupos de Pinos de Alimentação e os Pinos de Comunicação, no entanto iremos utilizar o conjunto com menos pinos, pois para esta montagem apenas são necessários os pinos GND, VCC, SDA e SCL (Ver imagem acima).

Pinos de Alimentação:

  • GND;
  • VCC;
  • BAT.

Apesar da placa RTC conter a sua própria alimentação, através de uma pequena bateria do tipo botão, esta também tem pinos para a sua alimentação externa através dos pinos GND e VCC, tendo este ultimo que ter uma tensão de cerca de 5V.

O pino BAT apesar de não ser um pino de alimentação foi colocado neste grupo, pois está ligado directamente à bateria tipo botão da RTC, sendo muito utilizado para monitorização da carga desta bateria.

Pinos de Comunicação:

  • SCL;
  • SDA;
  • DS;
  • SQ.

Os pinos de comunicação SCL e SDA da placa RTC fazem parte de um sistema de comunicação com o protocolo I2C. Este sistema permite comunicar com um ou mais equipamentos através de apenas 2 únicas linhas de comunicação, sendo o pino SDA ou "Serial Data" a linha que transmite e recebe a informação, no entanto, o pino SCL ou "Serial Clock" é o responsável por sincronizar a comunicação entre os equipamento, ou seja, permite ao equipamento saber quando enviar ou receber a informação sem se descoordenarem (Ver imagem acima).

Relativamente ao Arduino UNO, este tem pinos dedicados para utilização deste protocolo de comunicação sendo nestes que devemos ligar os pinos da placa RTC. No Arduino UNO estes 2 pinos encontra-se no grupo dos pinos de entrada analógica acumulando assim funções, deste modo encontramos o pino SCL no pino A5 e o pino SDA no pino A4, dependendo da versão da placa do Arduino estes pinos podem estar identificados no caso do Arduino UNO isso não acontece(Ver imagem acima).

Caso queiram saber mais sobre o sistema de comunicação I2C, vejam a explicação mais detalhada através o seguinte tutorial:

Arduino Tutorial - SD Card - LCD I2C

https://www.instructables.com/id/Arduino-Tutorial-SD-Card-LCD-I2C/

Um dos outros pinos é o pino DS, este está ligado a um pequeno sensor de temperatura instalado na nossa placa RTC, sendo através deste pino que são transmitidos os dados relativos à temperatura do sensor, no entanto nesta montagem esta função não será utilizada (Ver ficheiro abaixo).

Já o pino SQ normalmente não é utilizado, pois apenas contem apenas um sinal de onda quadrada, não tendo uma função essencial ou relevante para a utilização da RTC, no entanto, existem algumas montagens onde este pode ser utilizado, sendo este ligado a um pequeno LEDindicando assim que a RTC está a funcionar correctamente.

Com os equipamentos mais complexos montados e ligados, colocamos na "Breadboard" os 3 botões e as suas respectivas resistências de 1KΩ e ligamos estes aos pinos de sinal digital do Arduino. Sem estes botões na montagem apenas podemos visualizar a hora e data no LCD não sendo possível alterar ou acertar o relógio ou o calendário.

Estes botões devem ser identificados e dispostos de forma a que seja fácil e intuitivo navegar pelo menu, assim os botões terão a identificações MAIS, MENU e MENOS.

Para relembrar o funcionamento ou saber mais sobre os pinos de sinal digital sendo utilizados como entradas, vejam o seguinte tutorial:

Arduino Tutorial - Digital Input - Button - Monitor Serial

https://www.instructables.com/id/Arduino-Tutorial-Digital-Input-Button-Monitor-Seri/

Step 2: Código

Com a montagem terminada basta ligar o cabo USB e carregar código no Arduino, mas antes aqui vai uma pequena explicação do código. Para começar iniciamos o código importando as bibliotecas LiquidCrystal.h para o LCD e as bibliotecas TimeLib.h, a Wire.h e a DS1307RTC.h para funcionar com a placa RTC, através do gestor de bibliotecas do software do Arduino.

A biblioteca LiquidCrystal.h facilita muito a programação do código para o LCD, sendo apenas necessário configurar 2 funções para que este fique a funcionar em pleno, o que caso contrario seria muitas mais.

//Definir os pinos de comunicação e informação do LCD:
LiquidCrystal lcd ("RS", "E", "D7", "D6", "D5", "D4");

e

void setup(){
	//Inicia a comunicação com LCD 16x2:
	lcd.begin(2, 16);
}

Para escrever no LCD será necessário primeiro definir o local onde se começará a escrever, ou seja, qual a coluna e a linha, só depois imprimimos o texto que queremos, tendo em atenção que este LCD apenas tem 16 colunas e 2 linhas, no caso do texto passar esses limites do LCD não serão visíveis todos caracteres.

void loop(){
	//Define a coluna (em 16) e a linha (em 2) do LCD onde escrever:
	lcd.setCursor(0, 0);
	
	//Escreve no LCD:
	lcd.print("Bateria da RTC INOP");
}

Já as bibliotecas TimeLib.h, Wire.h e DS1307RTC.h permitem interagir com a placa RTC, no entanto, como podem presumir têm funções diferentes. A TimeLib.h activa as funcionalidades de tempo, como por exemplo, as variáveis para os segundos, minutos, hora, dia , mês, etc, facilitando assim a determinação da hora e data.

void loop(){
	//Variáveis com valores de tempo:
	second();
	minute();
	hour();
	day();
	month();
	year();
	now();	//Hora e data que está defina neste momento.
	
	//Verifica se tempo está definido ou sincronizado:
	timeStatus()
		//Devolve os seguintes valores:
		timeNotSet	//O valor de tempo não está definido;
		timeNeedsSync	//O Valor de tempo foi definido mas a sincronização falhou.
		timeSet		//O Valor de tempo está definido e sincronizado.	
	
	int h, m, s, D, M, A;		// Variáveis para alteração da hora e data.
	setTime (h, m, s, D, M, A);	//Alterar ou definir a hora e data.

	//Configura o valor de tempo sincronizado de outro equipamento.
	setSyncProvider(getTimeFunction);
}

A biblioteca Wire.h é responsável pelo controlo do sistema de comunicação com o protocolo I2C, caso queiram saber mais sobre esta biblioteca vejam a ligação https://www.arduino.cc/en/Reference/Wire. Por fim, a biblioteca DS1307RTC.h activa as funcionalidades que permitem a leitura e escrita dos dados de tempo guardados na memória da RTC.

void loop(){
	
	int h, m, s, D, M, A;   // Variáveis para alteração da hora e data.
	//Define uma nova hora e data:
	setTime(h, m, s, D, M, A);
	//Grava na RTC os dados de tempo:
	RTC.set(now());
	//Lê na RTC os dados de tempo:
	RTC.get();
}	

Em baixo podem ver o código completo para a nossa montagem, onde são utilizados os conceitos descritos anteriormente, agora uma das coisas mais importantes num relógio é possibilidade de acertar o relógio ou o calendário, para isso são utilizados 3 botões, permitindo seleccionar e acertar cada um dos parâmetros individualmente.

#include <TimeLib.h>	//Importar a "TimeLib" library.
#include <Wire.h>	//Importar a "Wire" library.
#include <DS1307RTC.h>   //Importar a "DS1307RTC" library.
 
#include <LiquidCrystal.h>	//Importar a LCD library.
 
//Definir os pinos de comunicação e informação do LCD.
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

int Botao_mais = 2;	//Variável para selecção do pino de entrada.
int Botao_menu = 3;	//Variável para selecção do pino de entrada.
int Botao_menos = 4; 	//Variável para selecção do pino de entrada.

int Num_Menu = 0;	//Variável para selecção da página de acerto do relógio.

int h, m, s, D, M, A;	//Variáveis para alteração da hora e data.

//Variável array de selecção do nome da página do menu de acerto do relógio.
char Lista_menu[] = {' ', 'h', 'm', 's', 'D', 'M', 'Y'};

//Correr a função SETUP apenas uma vez após ser pressionado Reset:
void setup() {
	//Iniciar a comunicação com LCD 16x2:
  	lcd.begin(2, 16);  
	//Definição do tipo de pino (Saída ou Entrada):
	pinMode(Botao_mais, INPUT);
	//Definição do tipo de pino (Saída ou Entrada):
	pinMode(Botao_menu, INPUT);
	//Definição do tipo de pino (Saída ou Entrada):
	pinMode(Botao_menos, INPUT);
	//Função para obter o tempo da RTC.
	setSyncProvider(RTC.get);
	//Verifica se o valor de tempo não está definido.
	if (timeStatus() != timeSet) {
		//Escreve no LCD:
		lcd.print("BAT. do RTC INOP");
		//Atraso para o próximo passo.
		delay(3000);
		//Apaga a informação do LCD.
		lcd.clear();
		//Alteração dos dados de hora e data.
		h = 1;
		m = 1;
		s = 1;
		D = 1;
		M = 1;
		A = 2001;
		//Grava na RTC uma nova hora e data.
		setTime(h, m , s , D , M , A );
		RTC.set(now());
	}
	else {
		//Escreve no LCD.
		lcd.print("RTC OK");
		//Atraso para o próximo passo.
		delay(3000);
		//Apaga a informação do LCD.
		lcd.clear();
	}
}

//Correr a função LOOP repetidamente:
void loop() {
	//Correr a função:
	Selector_Menu();
	
	//Correr a função:
	Relogio();
}

//>>> Selecção do menu:
void Selector_Menu() {
	//Condição para activar o acerto da hora e data do relógio:
	if (digitalRead(Botao_menu) == HIGH) {
		//Apaga a informação do LCD.
		lcd.clear();
		//Altera o valor de selecção do menu.
		Num_Menu = Num_Menu + 1;
		//Condição para seleccionar a página de acerto do relógio.
		if (Num_Menu < 7) {
			//Define a coluna (em 16) e a linha (em 2) do LCD onde escrever:
			lcd.setCursor(15, 0);
			//Escreve no LCD.
			lcd.print(Lista_menu[Num_Menu]);
		}
		//Condição para voltar para a página do relógio.
		else if (Num_Menu > 6) {
			Num_Menu = 0;
		}
	}
}

//>>> Função de escrita dos dados do relógio no LCD:
void Relogio() {
	//Verifica se o valor de tempo está definido.
	if (timeStatus() == timeSet) {
		//Define a coluna (em 16) e a linha (em 2) do LCD onde escrever:
		lcd.setCursor(0, 0);
		//Escreve no LCD:
		if (hour() < 10)
			lcd.print('0');
			lcd.print(hour());
			lcd.print(":");
		if (minute() < 10)
			lcd.print('0');
			lcd.print(minute());
          		lcd.print(":");
        	if (second() < 10)
          		lcd.print('0');
          		lcd.print(second());
      
        	//Define a coluna (em 16) e a linha (em 2) do LCD onde escrever:
        	lcd.setCursor(0, 1);
        	//Escreve no LCD:
        	if (day() < 10)
          		lcd.print('0');
          		lcd.print(day());
          		lcd.print("/");
        	if (month() < 10)
          		lcd.print('0');
          		lcd.print(month());
          		lcd.print("/");
          		lcd.print(year());
      
          	//Atraso para o próximo passo.
          	delay(500);
      	}
      	else {
          	//Escreve no LCD.
          	lcd.print("Bateria do RTC INOP");
          	//Atraso para o próximo passo:
          	delay(3000);
          	//Alteração dos dados de hora e data.
          	h = 1;
          	m = 1;
          	s = 1;
          	D = 1;
          	M = 1;
          	A = 2001;
          	//Grava na RTC uma nova hora e data.
          	setTime(h, m, s, D, M, A);
          	RTC.set(now());
      	}
    
    	//Condição para acerto das "horas":
    	if (Num_Menu == 1) {
        	//Condição para incrementar +1 hora:
        	if (digitalRead(Botao_mais) == HIGH) {
            		h = hour() + 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(h, minute(), second(), day(), month(), year());
            		RTC.set(now());
        	}
        	//Condição para retirar -1 hora:
        	if (digitalRead(Botao_menos) == HIGH) {
            		h = hour() - 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(h, minute(), second(), day(), month(), year());
            		RTC.set(now());
        	}
    	}
    	//Condição para acerto dos "minutos":
    	else if (Num_Menu == 2) {
        	//Condição para incrementar +1 minuto:
        	if (digitalRead(Botao_mais) == HIGH) {
            		m = minute() + 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), m, second(), day(), month(), year());
            		RTC.set(now());
        	}
        	//Condição para retirar -1 minuto:
        	if (digitalRead(Botao_menos) == HIGH) {
            		m = minute() - 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), m, second(), day(), month(), year());
            		RTC.set(now());
        	}
    	}
    	//Condição para acerto dos "segundos":
    	else if (Num_Menu == 3) {
        	//Condição para incrementar +1 segundo:
        	if (digitalRead(Botao_mais) == HIGH) {
            		s = second() + 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), minute(), s, day(), month(), year());
            		RTC.set(now());
      		}
        	//Condição para retirar -1 segundo:
        	if (digitalRead(Botao_menos) == HIGH) {
            		s = second() - 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), minute(), s, day(), month(), year());
            		RTC.set(now());
        	}
    	}
    	//Condição para acerto dos "dias":
    	else if (Num_Menu == 4) {
        	//Condição para incrementar +1 dia:
        	if (digitalRead(Botao_mais) == HIGH) {
            		D = day() + 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), minute(), second(), D, month(), year());
            		RTC.set(now());
        	}
        	//Condição para retirar -1 dia:
        	if (digitalRead(Botao_menos) == HIGH) {
            		D = day() - 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), minute(), second(), D, month(), year());
            		RTC.set(now());
        	}
    	}
    	//Condição para acerto dos "meses":
    	else if (Num_Menu == 5) {
        	//Condição para incrementar +1 mês:
        	if (digitalRead(Botao_mais) == HIGH) {
            		M = month() + 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), minute(), second(), day(), M, year());
            		RTC.set(now());
        	}
        	//Condição para retirar -1 mês:
        	if (digitalRead(Botao_menos) == HIGH) {
            		M = month() - 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), minute(), second(), day(), M, year());
            		RTC.set(now());
        	}
    	}
    	//Condição para acerto dos "anos":
    	else if (Num_Menu == 6) {
        	//Condição para incrementar +1 ano:
        	if (digitalRead(Botao_mais) == HIGH) {
            		A = year() + 1;
            		//Grava na RTC uma nova hora e data.
           		setTime(hour(), minute(), second(), day(), month(), A);
            		RTC.set(now());
        	}
        	//Condição para retirar -1 ano:
        	if (digitalRead(Botao_menos) == HIGH) {
            		A = year() - 1;
            		//Grava na RTC uma nova hora e data.
            		setTime(hour(), minute(), second(), day(), month(), A);
            		RTC.set(now());
        	}
    	}
}

Caso queiram saber mais ou tenham dúvidas sobre estas ou outras referências no código, vejam a sua explicação detalhada no site oficial do Arduino, através da ligação https://www.arduino.cc/en/Reference/HomePage.

Procurem os nossos tutoriais sobre o Arduino, onde são explicadas diversas montagens e o funcionamento dos seus componentes, já agora visitem também o nosso canal no Youtube, Instagram, Facebook ou Twitter.

Abraço e bons projectos.