#include <Wire.h> // Biblioteca para comunicacao I2C
#include <RTClib.h> // Biblioteca para o modulo de relogio RTC
#include <LiquidCrystal.h> // Biblioteca para controlar o LCD em modo paralelo
// =====================================================
// CRIACAO DO OBJETO LCD
// LiquidCrystal(rs, enable, d4, d5, d6, d7)
// =====================================================
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
// =====================================================
// CRIACAO DO OBJETO RTC
// =====================================================
RTC_DS3231 rtc;
// =====================================================
// PINOS DO ENCODER E DO BUZZER
// =====================================================
#define clkPin 2 // Pino CLK do encoder
#define dtPin 3 // Pino DT do encoder
#define swPin 4 // Pino do botao do encoder
#define buzzerPin 5 // Pino do buzzer
// =====================================================
// VARIAVEIS PARA LEITURA DO ENCODER
// =====================================================
int lastClkState; // Guarda o estado anterior do pino CLK
int currentClkState; // Guarda o estado atual do pino CLK
// =====================================================
// MODOS DE TELA
// 0 = mostrando UTC
// 1 = mostrando menu de cidades
// 2 = mostrando a cidade escolhida
// =====================================================
int screenMode = 0;
// =====================================================
// LISTA DE CIDADES
// Cada cidade possui um nome e um offset em relacao ao UTC
// =====================================================
String cityNames[] = {
"UTC",
"Sao Paulo",
"Buenos Aires",
"New York",
"Lisboa",
"Paris",
"Dubai",
"Tokyo",
"Sydney"
};
int cityOffsets[] = {
0, // UTC
-3, // Sao Paulo
-3, // Buenos Aires
-5, // New York
0, // Lisboa
1, // Paris
4, // Dubai
9, // Tokyo
10 // Sydney
};
int cityCount = 9; // Quantidade total de cidades na lista
// =====================================================
// menuIndex = cidade atualmente destacada no menu
// selectedCity = cidade confirmada pelo usuario
// =====================================================
int menuIndex = 0;
int selectedCity = 0;
// =====================================================
// CONTROLE SIMPLES DO BOTAO
// =====================================================
int lastButtonState = HIGH;
unsigned long lastButtonTime = 0;
int debounceTime = 180;
// =====================================================
// AJUSTE DE UTC
// IMPORTANTE:
// Nesta versao, estamos assumindo que o RTC esta ajustado
// com o horario de Sao Paulo.
// Para obter o UTC, somamos 3 horas.
// =====================================================
int utcReferenceOffset = 3;
// =====================================================
// SETUP
// Executa apenas uma vez ao ligar ou resetar o Arduino
// =====================================================
void setup() {
// Inicializa o LCD com 20 colunas e 4 linhas
lcd.begin(20, 4);
// Configura os pinos do encoder
// INPUT_PULLUP ajuda a deixar a leitura mais estavel
pinMode(clkPin, INPUT_PULLUP);
pinMode(dtPin, INPUT_PULLUP);
pinMode(swPin, INPUT_PULLUP);
// Configura o buzzer como saida
pinMode(buzzerPin, OUTPUT);
// Inicializa o barramento I2C
Wire.begin();
// Inicializa o RTC
rtc.begin();
// -------------------------------------------------
// Use apenas uma vez, se quiser ajustar o RTC
// com a data e hora do computador no momento do upload
// -------------------------------------------------
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// Guarda o estado inicial do pino CLK do encoder
lastClkState = digitalRead(clkPin);
// Mostra a tela inicial
showUtcScreen();
}
// =====================================================
// LOOP
// Executa repetidamente enquanto o Arduino estiver ligado
// =====================================================
void loop() {
// Le o giro do encoder
readEncoder();
// Le o clique do botao do encoder
readButton();
// Atualiza continuamente a tela UTC
if (screenMode == 0) {
showUtcScreen();
}
// Atualiza continuamente a tela da cidade escolhida
if (screenMode == 2) {
showSelectedCityScreen();
}
// Delay pequeno apenas para aliviar o processador
// Sem prejudicar a leitura do encoder
delay(2);
}
// =====================================================
// FUNCAO PARA LER O ENCODER ROTATIVO
// A cidade so muda quando estamos na tela do menu
// =====================================================
void readEncoder() {
currentClkState = digitalRead(clkPin);
// Detecta mudanca de estado no CLK
if (currentClkState != lastClkState) {
// Quando o CLK muda para LOW, analisamos a direcao
if (currentClkState == LOW) {
// Se DT for diferente de CLK, gira em um sentido
// Se for igual, gira no outro
if (digitalRead(dtPin) != currentClkState) {
menuIndex++;
} else {
menuIndex--;
}
// So altera o menu se estivermos na tela de selecao
if (screenMode == 1) {
// Se passar do ultimo item, volta ao primeiro
if (menuIndex >= cityCount) {
menuIndex = 0;
}
// Se ficar menor que zero, volta ao ultimo
if (menuIndex < 0) {
menuIndex = cityCount - 1;
}
beep();
showMenuScreen();
}
}
}
// Guarda o ultimo estado para comparar no proximo loop
lastClkState = currentClkState;
}
// =====================================================
// FUNCAO PARA LER O BOTAO DO ENCODER
// =====================================================
void readButton() {
int buttonState = digitalRead(swPin);
// Detecta transicao de HIGH para LOW
// Isso significa que o botao foi pressionado
if (buttonState == LOW && lastButtonState == HIGH) {
// Debounce simples para evitar multiplos acionamentos
if (millis() - lastButtonTime > debounceTime) {
lastButtonTime = millis();
beep();
// Se estiver na tela UTC, entra no menu
if (screenMode == 0) {
screenMode = 1;
showMenuScreen();
}
// Se estiver no menu, confirma a cidade selecionada
else if (screenMode == 1) {
selectedCity = menuIndex;
screenMode = 2;
showSelectedCityScreen();
}
// Se estiver na tela da cidade, volta para o menu
else if (screenMode == 2) {
screenMode = 1;
showMenuScreen();
}
}
}
// Guarda o ultimo estado do botao
lastButtonState = buttonState;
}
// =====================================================
// TELA 1 - MOSTRA A REFERENCIA UTC
// =====================================================
void showUtcScreen() {
// Le o horario do RTC
DateTime rtcNow = rtc.now();
// Soma 3 horas para transformar Sao Paulo em UTC
DateTime utcNow = rtcNow + TimeSpan(0, utcReferenceOffset, 0, 0);
lcd.setCursor(0, 0);
lcd.print("World Time Terminal ");
lcd.setCursor(0, 1);
lcd.print("Referencia: UTC ");
lcd.setCursor(0, 2);
printDate(utcNow.day(), utcNow.month(), utcNow.year());
lcd.setCursor(0, 3);
printTime(utcNow.hour(), utcNow.minute(), utcNow.second());
}
// =====================================================
// TELA 2 - MENU DE CIDADES
// =====================================================
void showMenuScreen() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Selecione a cidade");
lcd.setCursor(0, 1);
lcd.print("Gire o encoder");
lcd.setCursor(0, 2);
lcd.print("> ");
lcd.print(cityNames[menuIndex]);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print("Clique confirma ");
}
// =====================================================
// TELA 3 - MOSTRA DATA E HORA DA CIDADE ESCOLHIDA
// =====================================================
void showSelectedCityScreen() {
// Le o horario do RTC
DateTime rtcNow = rtc.now();
// Primeiro converte de Sao Paulo para UTC
DateTime utcNow = rtcNow + TimeSpan(0, utcReferenceOffset, 0, 0);
// Depois aplica o offset da cidade escolhida
DateTime localTime = utcNow + TimeSpan(0, cityOffsets[selectedCity], 0, 0);
lcd.setCursor(0, 0);
lcd.print("Cidade selecionada ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(cityNames[selectedCity]);
lcd.setCursor(0, 2);
printDate(localTime.day(), localTime.month(), localTime.year());
lcd.setCursor(0, 3);
printTime(localTime.hour(), localTime.minute(), localTime.second());
}
// =====================================================
// FUNCAO PARA IMPRIMIR A DATA NO FORMATO DD/MM/AAAA
// =====================================================
void printDate(int dayValue, int monthValue, int yearValue) {
if (dayValue < 10) lcd.print("0");
lcd.print(dayValue);
lcd.print("/");
if (monthValue < 10) lcd.print("0");
lcd.print(monthValue);
lcd.print("/");
lcd.print(yearValue);
lcd.print(" ");
}
// =====================================================
// FUNCAO PARA IMPRIMIR A HORA NO FORMATO HH:MM:SS
// =====================================================
void printTime(int hourValue, int minuteValue, int secondValue) {
if (hourValue < 10) lcd.print("0");
lcd.print(hourValue);
lcd.print(":");
if (minuteValue < 10) lcd.print("0");
lcd.print(minuteValue);
lcd.print(":");
if (secondValue < 10) lcd.print("0");
lcd.print(secondValue);
lcd.print(" ");
}
// =====================================================
// FUNCAO PARA TOCAR UM BIPE CURTO NO BUZZER
// =====================================================
void beep() {
tone(buzzerPin, 2000, 70);
}