/*
 * Modder:  @red9030
 * Title:   BIGNUMBER NTP Clock  + LCD1602 I²C ESP32 version
 * Reference: https://arduinogetstarted.com/tutorials/arduino-lcd-clock
 *            https://steemit.com/utopian-io/@lapilipinas/arduino-big-digits-0-99-with-i2c-16x2-lcd
 *  _   _     _   _ 
 * |_| |_| . |_| |_|
 * |_| |_| . |_| |_|
 * 
 * This example code is in the public domain
 */

/*
 * FILAS    ----
 * COLUMNAS ||||
*/

/*
 *****************************************************
 *    LIBRERIAS
 *****************************************************
*/
#include <Wire.h>               //Librería para conexión I2C
#include <WiFi.h>               //Librería para conexión wifi                
#include <LiquidCrystal_I2C.h>  //Librería para uso de pantallas de cristal liquido.
#include <ESP32Time.h>          //Librería para uso del RTC interno ESP32
/*
 *****************************************************
 *    VARIABLES
 *****************************************************
*/
#define boton1 5
#define boton2 18  
#define I2C_address 0x27    // 0x27 , 0x3F , 0x20 , 0x38
const int lcdFilas = 2;     //FIlas del LCD
const int lcdColumnas = 16; //Columnas del LCD

LiquidCrystal_I2C lcd(I2C_address, lcdColumnas, lcdFilas);  // I2C address 0x27 (from DIYables LCD), 16 column and 2 rows

ESP32Time rtc; //RTC ESP32
//Declaramos las variables donde se almacenarán los datos de la fecha y la hora, día, mes, año.
//Variables para conexión del servidor.
String tiempo; //Declaramos una clase llamada tiempo.

#define NTP_SERVER     "pool.ntp.org"   //Servidor ntp para obtención de datos 
#define UTC_OFFSET     -18000           //UTC PAIS  x  3600seconds = hora del paìs buscado
#define UTC_OFFSET_DST 0                //

int dia, mes, anio, hora, minuto, segundo;
byte i;

byte LT[8] = { B00111, B01111, B11111, B11111, B11111, B11111, B11111, B11111 };
byte UB[8] ={ B11111, B11111, B11111, B00000, B00000, B00000, B00000, B00000 };
byte RT[8] ={ B11100, B11110, B11111, B11111, B11111, B11111, B11111, B11111 };
byte LL[8] ={ B11111, B11111, B11111, B11111, B11111, B11111, B01111, B00111 };
byte LB[8] ={ B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111 };
byte LR[8] ={ B11111, B11111, B11111, B11111, B11111, B11111, B11110, B11100 };
byte MB[8] ={ B11111, B11111, B11111, B00000, B00000, B00000, B11111, B11111 };
byte block[8] ={ B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111 };

/*
 *****************************************************
 *    INICIO
 *****************************************************
*/
void setup() {
  Serial.begin(115200);
  
  lcd.begin(lcdColumnas,lcdFilas);                      // initialize the lcd
  lcd.createChar(0,LT);
  lcd.createChar(1,UB);
  lcd.createChar(2,RT);
  lcd.createChar(3,LL);
  lcd.createChar(4,LB);
  lcd.createChar(5,LR);
  lcd.createChar(6,MB);
  lcd.createChar(7,block);
  // Print a message to the LCD.
  lcd.backlight();
  int temp = 10;
  lcd.clear();
  printDigits(0,0);
  printDigits(1,4);
  printDigits(2,8);
  printDigits(3,12);
  lcd.clear();

  pinMode(boton1, INPUT_PULLUP);
  pinMode(boton2, INPUT_PULLUP);

  mensajesLCD(0);      //Mensaje en pantalla LCD: estableciendo conexión wifi
   //Conexión wifi
  WiFi.begin("Wokwi-GUEST", "", 6);
  while (WiFi.status() != WL_CONNECTED) {
    delay(250);
    spinner();
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  mensajesLCD(1);             //Mensaje en pantalla LCD: Conexión wifi exitosa                  
  //configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER); //Configuracion del time


}

/*
 *****************************************************
 *    REPETICIÓN
 *****************************************************
*/

//FUNCION QUE CREA UN EMOTICON DE CONEXION
void spinner() {                
  static int8_t counter = 0;
  const char* glyphs = "\xa1\xa5\xdb";
  lcd.setCursor(15, 1);
  lcd.print(glyphs[counter++]);
  if (counter == strlen(glyphs)) {
    counter = 0;
  }
}

//IMPRIME CIERTOS MENSAJES EN LA PNATALLA LCD
void mensajesLCD(int opc) {     
  switch(opc){
        case 0:              //Mensaje 1 (estableciendo conexión wifi)
                lcd.setCursor(0, 0);
                lcd.print("Connecting to ");
                lcd.setCursor(0, 1);
                lcd.print("WiFi ");
        break;
        case 1:             //Mensaje 2
                lcd.clear();
                lcd.setCursor(0, 0);
                lcd.println("Online");
                lcd.setCursor(0, 1);
                lcd.println("Updating time...");
        break;
        case 2:              //Mensaje 3

        break;
  }
}

void loop() {
  if(digitalRead(boton1) == LOW){
    setearDatosAlRtc();
 }else{
    cargarDatos(); //Llamamos a la función encargada de cargar los datos de la fecha y la hora.
    imprimirDatos(); //Llamamos a la función encargada de imprimir en pantalla los datos de la fecha y la hora.
    delay(1000); //Producimos una espera de 1 segundo. 
 }
}

/*
 *****************************************************
 *    FUNCIONES
 *****************************************************
*/

//Función para cambiar los datos de la hora y fecha (setear) desde código
void setearDatosAlRtc()
{
//rtc.setDOW(4); //Seteamos el día de la semana.
//Seteamos la Hora:Minuto:Segundo (La Hora debe estar en el formato de 24Hs).
 rtc.setTime(30, 33, 12, 9, 4, 2021);  // 17th Jan 2021 15:24:30
//rtc.setTime(1609459200);  // 1st Jan 2021 00:00:00 
//rtc.setDate(14,8,2024); //Seteamos el Día/Mes/Año.
delay(1000); //Producimos una espera de 1 segundo.
}

//Función que carga los datos de la fecha y hora actual del reloj en la clase tiempo.
void cargarDatos()
{
tiempo = rtc.getTime("%H:%M:%S"); //Obtenemos los datos de la fecha y hora actual del reloj en la clase tiempo.
//dia = rtc.getTime("%A"); //Cargamos el día.
mes = rtc.getMonth(); //Cargamos el mes.
anio = rtc.getYear(); //Cargamos el año.
hora = rtc.getHour(); //Cargamos la hora.
minuto = rtc.getMinute(); //Cargamos el minuto.
segundo = rtc.getSecond(); //Cargamos el segundo.
}

//Función que imprimime los datos en pantalla LCD.
void imprimirDatos() {
 /*
 //Imprime día
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Hoy:");
  Serial.print("Hoy es:");
  //Serial.print(boton1);
  lcd.setCursor(5,0);
  switch(tiempo.dow){ //La variable dow contiene el número del día de la semana.
  case 1: lcd.print("Domingo");   Serial.println(" Domingo");break;
  case 2: lcd.print("Lunes");     Serial.println(" Lunes");break;
  case 3: lcd.print("Martes");    Serial.println(" Martes");break;
  case 4: lcd.print("Miercoles"); Serial.println(" Miercoles");break;
  case 5: lcd.print("Jueves");    Serial.println(" Jueves");break;
  case 6: lcd.print("Viernes");   Serial.println(" Viernes");break;
  case 7: lcd.print("Sabado");    Serial.println(" Sabado");break;
  }
  //IMPRESION DE LA FECHA  
  lcd.setCursor(0,1); // lcd.setCursor(Columna, Fila);
  lcd.print("Fecha:"); 
  if(dia < 10){lcd.print("0"); lcd.print(dia);}
  else lcd.print(dia);
  lcd.print("/");
  if(mes < 10){lcd.print("0"); lcd.print(mes);}
  else lcd.print(mes);
  lcd.print("/"); lcd.print(anio);
  delay(3000);
  lcd.clear(); */ 
  
  //IMPRESION DE LA HORA: MINUTO: SEGUNDOS
  if(hora < 10){
        printNumber(0,0); // printNumer(Valor, Columna)
        printNumber(hora,0);
      }else 
        printNumber(hora,0);
        lcd.setCursor(6, 0);
        lcd.print(".");
        lcd.setCursor(6, 1);
        lcd.print(".");
      if(minuto < 10){
        printNumber(0,7);
        printNumber(minuto,7);
      }else
        printNumber(minuto,7);
        lcd.setCursor(13, 0);
        lcd.print(".");
        lcd.setCursor(13, 1);
        lcd.print(".");
      if(segundo < 10){
        lcd.print("0");
        lcd.print(segundo);
      }else
        lcd.print(segundo);
    }

//Imprime el numero actualizado
void printNumber(int val,int col){
     printDigits(val/10,col);
     printDigits(val%10,col+3);
}

void custom0(int x){
  lcd.setCursor(x,0);
  lcd.write((byte)0); 
  lcd.write(1); 
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.write(3); 
  lcd.write(4); 
  lcd.write(5);
}
void custom1(int x){
  lcd.setCursor(x,0);
  lcd.write(1);
  lcd.write(2);
  lcd.print(" ");
  lcd.setCursor(x,1);
  lcd.write(4);
  lcd.write(7);
  lcd.write(4);
}
void custom2(int x){
  lcd.setCursor(x,0);
  lcd.write(6);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.write(3);
  lcd.write(4);
  lcd.write(4);
}
void custom3(int x){
  lcd.setCursor(x,0);
  lcd.write(6);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.write(4);
  lcd.write(4);
  lcd.write(5);
}
void custom4(int x){
  lcd.setCursor(x,0);
  lcd.write(3);
  lcd.write(4);
  lcd.write(7);
  lcd.setCursor(x, 1);
  lcd.print(" ");
  lcd.print(" ");
  lcd.write(7);
}
void custom5(int x){
  lcd.setCursor(x,0);
  lcd.write(3);
  lcd.write(6);
  lcd.write(6);
  lcd.setCursor(x, 1);
  lcd.write(4);
  lcd.write(4);
  lcd.write(5);
}
void custom6(int x){
  lcd.setCursor(x,0);
  lcd.write((byte)0);
  lcd.write(6);
  lcd.write(6);
  lcd.setCursor(x, 1);
  lcd.write(3);
  lcd.write(4);
  lcd.write(5);
}
void custom7(int x){
  lcd.setCursor(x,0);
  lcd.write(1);
  lcd.write(1);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.print(" ");
  lcd.print(" ");
  lcd.write(7);
}
void custom8(int x){
  lcd.setCursor(x,0);
  lcd.write((byte)0);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.write(3);
  lcd.write(4);
  lcd.write(5);
}
void custom9(int x){
  lcd.setCursor(x,0);
  lcd.write((byte)0);
  lcd.write(6);
  lcd.write(2);
  lcd.setCursor(x, 1);
  lcd.print(" ");
  lcd.print(" ");
  lcd.write(7);
}
void printDigits(int digits, int x){
  // utility function for digital clock display: prints preceding colon and leading 0
  switch (digits) {
  case 0: 
    custom0(x);
    break;
  case 1: 
    custom1(x);
    break;
  case 2: 
    custom2(x);
    break;
  case 3: 
    custom3(x);
    break;
  case 4: 
    custom4(x);
    break;
  case 5: 
    custom5(x);
    break;
  case 6: 
    custom6(x);
    break;
  case 7: 
    custom7(x);
    break;
  case 8: 
    custom8(x);
    break;
  case 9: 
    custom9(x);
    break;
  }
}