Reloj Digital WeMos ESP8266 Wifi

Reloj WeMos ESP8266 y pantalla OLED

Vamos a montar de una manera sencilla y paso a paso un reloj digital con WeMos. Cogerá la hora en tiempo real via WIFI de un servidor en internet y la mostrara en la pantalla OLED.

Con este proyecto pretendo explicar de manera sencilla como explorar varias posibilidades que tiene WeMos y el ESP8266 que incorpora. Como es conectarse a internet vía WIFI, comunicarse vía puerto serie con el ordenador y mostrar información a través de una pantalla OLED.

Funcionamiento del Reloj

Como ya he comentado vamos a coger la información de internet. Por lo que lo primero que realizara es conectarse por WIFI a internet. Después de conectarse procederá a comprobar cada 10 segundos la fecha y hora de internet y actualizarla. Esta información la enviara tanto por puerto serie como a la pantalla WeMoS. Veremos en ambos casos tanto el proceso de conexión, como la información de fecha y hora actualizada.

Componentes

Componentes Reloj WeMos

El montaje es bastante sencillo, necesitamos pocos componentes. Ni siquiera hace falta una protoboard, todo gracias a la facilidad de conexión que tiene WeMos. Los componentes necesarios serian los siguientes:

Se puede usar cualquier WeMos D1 Mini, tanto el pro como el lite.

Librerías para IDE Arduino

Necesitamos ciertas librerías en el IDE de Arduino para el funcionamiento del ESP8266. Para coger la información de la fecha de internet, y para mostrar la información en la pantalla. A continuación tenéis el listado de librerías necesarias y donde conseguirlas.

Las introducimos como cualquier otra librería en Arduino.

Preparación parte electrónica de WeMos

Montaje pantalla OLED en WeMoS

Los WeMos cuando los compras vienen sin soldar los pines de conexión. Vienen varios pines dependiendo de como queramos hacer la conexión. En este caso he decidido soldar los necesarios para poner la pantalla justo encima del ESP8266. Igualmente se puede poner de otras maneras mientras respetemos el patillaje.

Programación del reloj ESP8266

Como he comentado anteriormente vamos a recabar varias posibilidades del WeMos en un único código. Para que resulte más sencillo entenderlo voy a explicar cada proceso por separado. Aunque el código esta pensado para WeMos, se puede usar para hacer un reloj ESP8266 con pequeñas modificaciones con cualquier otra placa que lo incorpore. Al final tenéis todo el código completo para el que quiera pasar directamente a la acción.

Conectarse al WIFI

Primero debemos introducir la información de nuestro wifi para que pueda conectarse. Para ello en las siguientes lineas del código debemos sustituir las xxxx por nuestro ssid y password.

//Configurar wifi
const char* ssid = "xxxx";           // añade tu ssid 
const char* password = "xxxx";    // añade tu password

Después deberemos realizar la conexión al WIFI. Con el siguiente código realizaremos la conexión con los datos introducidos anteriormente, y mandara puntos por puerto serie hasta que se realice la conexión.

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) 
{
  delay(500);
  Serial.print(".");
}

Tenemos algún otro código semejante introducido para controlar posibles desconexiones, y que en ese caso vuelva a realizar la conexión.

Recoger información de la fecha y hora de internet

A continuación vamos a ver una parte importante del código que necesitamos. Vamos a recoger la información de internet y procesarla posteriormente.

Para ello necesitamos algunos códigos de configuración del cliente y la dirección donde nos vamos a conectar. Además necesitamos indicar la diferencia horaria que tenemos respecto a la hora UTC. No he usado la configuración original para Europa ya que no funcionaba correctamente.

Para que funcionara en otro país correctamente habría que introducir la diferencia horaria donde pone el 60 y el 0. Eso respectivamente son más 60 minutos y horario original. En cambio en el caso de America habría que poner en negativo los minutos que correspondan ya que la diferencia horaria es negativa.

// Definir propiedades NTP
#define NTP_OFFSET   60 * 60      // En segundos
#define NTP_INTERVAL 60 * 1000    // En milisegundos
#define NTP_ADDRESS  "pool.ntp.org"  // URL NTP

// Configura el cliente NTP UDP
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, NTP_ADDRESS, NTP_OFFSET, NTP_INTERVAL);

// Horario Europa Central
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 60};     // Hora de Verano de Europa Central
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 0};      // Hora Estandar de Europa Central
Timezone CE(CEST, CET);

time_t local, utc;

Posteriormente para recoger la información vamos a comprobar si estamos correctamente conectados al WIFI. Después transformaremos la fecha y hora a nuestro huso horario y lo enviaremos a la funciones correspondientes para enviarlo por puesto serie, y a la pantalla OLED.

Lo que enviamos es la marca de tiempo. Posteriormente la vamos a convertir a una fecha interpretable directamente por un ser humano.

if (WiFi.status() == WL_CONNECTED) //comprobar estado de conexion del WIFI
  {   
     
    // Actualizar el cliente NTP y obtener la marca de tiempo UNIX UTC
    timeClient.update();
    unsigned long utc =  timeClient.getEpochTime();

    //Convertir marca de tiempo UTC UNIX a hora local
    local = CE.toLocal(utc);

    // Enviar Fecha y hora por puerto serie
    printTime(local);
    
    // Enviar Fecha y hora a OLED
    printTimeOLED(local);
    
  }

Para finalizar vamos a convertir esa marca de tiempo en texto. Hay varias funciones en el código que lo hacen pero vamos a ver las 2 principales. Por una parte vamos a sacar la fecha y por otra sacaremos la hora. En Ambos casos lo convertimos en un String.

// Funcion para formatear en texto la fecha
String convertirTimeATextoFecha(time_t t)
{
  String date = "";
  date += days[weekday(t)-1];
  date += ", ";
  date += day(t);
  date += " ";
  date += months[month(t)-1];
  date += ", ";
  date += year(t);
  return date;
}
// Funcion para formatear en texto la fecha sin dia de la semana
String convertirTimeATextoFechaSinSemana(time_t t)
{
  String date = "";
  date += months[month(t)-1];
  date += "   ";
  date += year(t);
  return date;
}

Enviar información por puerto serie

Primero debemos configurar a que velocidad se va a realizar la comunicación por puesto serie con el siguiente código.

  // Inicializar puerto serie
  Serial.begin(9600);

Después con la siguiente función enviaremos la información. Con print enviamos cosas en la misma linea y con println hacemos un salto de línea después de enviarlo.

// Funcion para mandar hora por puerto serie
void printTime(time_t t)
{
  Serial.println("");
  Serial.print("Fecha local: ");
  Serial.print(convertirTimeATextoFecha(t));
  Serial.println("");
  Serial.print("Hora local: ");
  Serial.print(convertirTimeATextoHora(t));
}

Enviar información a pantalla OLED

En este caso es un poco mas complejo que enviarlo por puerto serie pero es semejante.

Primero debemos configurar la pantalla.

// Configurar OLED
#define OLED_RESET 0  // GPIO0
Adafruit_SSD1306 display(OLED_RESET);

Seguidamente vamos a inicializarla. Para ello tenemos que seleccionar la dirección que tiene configurada para conectarse por I2C. Normalmente va a tener la 0x3C pero se podría poner también con la 0x3D cambiando un punto de soldadura que viene indicado por la parte trasera de la shield. Finalmente borraremos la información de la pantalla.

  // Inicializar OLED
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Inicializa con la direccion I2C 0x3C (para 64x48)
  display.clearDisplay();

Finalmente para sacar la información por pantalla deberemos usar varios códigos. Vamos a ver como ejemplo la parte en la que enviamos la información de la hora a la pantalla. Usamos varios códigos:

  • Primero elegimos el tamaño de las letras con display.setTextSize(1). Con el número seleccionamos el tamaño en lineas de las letras. Esta pantalla tendría un tamaño de 6 lineas.
  • Después seleccionamos el color con setTextColor(WHITE).
  • A continuación elegimos la posición de escritura setCursor(0,0).
  • Luego indicamos que queremos mostrar por pantalla println(«Jorge Sanz»). Comando que funciona igual que en el caso del puerto serie.
  • Posteriormente indicamos que mande a la pantalla todo lo anterior display().
  • Y para finalizar por seguridad le mandamos clearDisplay().
// Funcion para mostrar hora en pantalla OLED
void printTimeOLED(time_t t)
{
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("Jorge Sanz");
  display.println("");
  display.setTextSize(2);
  display.println(convertirTimeATextoHora(t));
  display.setTextSize(1);
  display.print(days[weekday(t)-1]);
  display.print(" ");
  display.println(day(t));
  display.println(convertirTimeATextoFechaSinSemana(t));
  display.display();
  display.clearDisplay();
}

Código final

Y para finalizar todo el código completo para ver y descargar.

/*****************************************************************************************************
 *  Reloj wifi con WeMoS (ESP8266) y OLED Shield WeMoS (64x48 pixels)
 *  Reloj digital que coge la hora y fecha internacional de internet cada 10 segundos y la adapta 
 *  al horario del pais deseado.
 *  
 *  Autor: Jorge Sanz Sanfructuoso
 *  Web: https://jorgesanz.es/
 *  
 ****************************************************************************************************/

//Librerías necesarias
#include <ESP8266WiFi.h>
#include <WifiUDP.h>

#include <NTPClient.h>
#include <Time.h>
#include <TimeLib.h>
#include <Timezone.h>

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//Configurar wifi
const char* ssid = "xxxx";           // añade tu ssid 
const char* password = "xxxx";    // añade tu password

// Definir propiedades NTP
#define NTP_OFFSET   60 * 60      // En segundos
#define NTP_INTERVAL 60 * 1000    // En milisegundos
#define NTP_ADDRESS  "pool.ntp.org"  // URL NTP

// Configura el cliente NTP UDP
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, NTP_ADDRESS, NTP_OFFSET, NTP_INTERVAL);

//Horario Europa Central
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 60};     // Hora de Verano de Europa Central
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 0};      // Hora Estandar de Europa Central
Timezone CE(CEST, CET);

time_t local, utc;

//Configurar Fecha y hora
const char * days[] = {"Domingo", "Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado"} ;
const char * months[] = {"Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"} ;

//Configurar OLED
#define OLED_RESET 0  // GPIO0
Adafruit_SSD1306 display(OLED_RESET);

void setup() 
{
  //Inicializar OLED
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Inicializa con la direccion I2C 0x3C (para 64x48)
  display.clearDisplay();
  
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("jorgesanz.es");
  display.setTextSize(2);
  display.println("RELOJ");
  display.setTextSize(1);
  display.display();
  
  //Inicializar puerto serie
  Serial.begin(9600);

  // Conectar a wifi //
  // Puerto serie
  Serial.println("");
  Serial.print("conectando a ");
  Serial.print(ssid);
  // Pantalla OLED
  display.println("conectando");
  display.print(ssid);
  display.display();
  display.clearDisplay();
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Conectando WiFi a ");
  Serial.print(WiFi.localIP());
  Serial.println("");
}

void loop() 
{
  if (WiFi.status() == WL_CONNECTED) //comprobar estado de conexion del WIFI
  {   
     
    // Actualizar el cliente NTP y obtener la marca de tiempo UNIX UTC
    timeClient.update();
    unsigned long utc =  timeClient.getEpochTime();

    //Convertir marca de tiempo UTC UNIX a hora local
    local = CE.toLocal(utc);

    // Enviar Fecha y hora por puerto serie
    printTime(local);
    
    // Enviar Fecha y hora a OLED
    printTimeOLED(local);
    
  }
  else // Intenta conectarse a wifi de nuevo si está desconectado
  {
    WiFi.begin(ssid, password);
    delay(1000);
  }
    
  delay(10000);    // Enviar una solicitud para actualizar cada 10 seg (= 10,000 ms)

}

//Funcion para formatear en texto la fecha
String convertirTimeATextoFecha(time_t t)
{
  String date = "";
  date += days[weekday(t)-1];
  date += ", ";
  date += day(t);
  date += " ";
  date += months[month(t)-1];
  date += ", ";
  date += year(t);
  return date;
}

//Funcion para formatear en texto la fecha sin dia de la semana
String convertirTimeATextoFechaSinSemana(time_t t)
{
  String date = "";
  date += months[month(t)-1];
  date += "   ";
  date += year(t);
  return date;
}

//Funcion para formatear en texto la hora
String convertirTimeATextoHora(time_t t)
{
  // Formatear la hora sin segundos
  String hora ="";
  if(hour(t) < 10)
    hora += "0";
  hora += hour(t);
  hora += ":";
  if(minute(t) < 10)  // Agregar un cero si el minuto es menor de 10
    hora += "0";
  hora += minute(t);
  return hora;
}

//Funcion para mandar hora por puerto serie
void printTime(time_t t)
{
  Serial.println("");
  Serial.print("Fecha local: ");
  Serial.print(convertirTimeATextoFecha(t));
  Serial.println("");
  Serial.print("Hora local: ");
  Serial.print(convertirTimeATextoHora(t));
}

//Funcion para mostrar hora en pantalla OLED
void printTimeOLED(time_t t)
{
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("Jorge Sanz");
  display.println("");
  display.setTextSize(2);
  display.println(convertirTimeATextoHora(t));
  display.setTextSize(1);
  display.print(days[weekday(t)-1]);
  display.print(" ");
  display.println(day(t));
  display.println(convertirTimeATextoFechaSinSemana(t));
  display.display();
  display.clearDisplay();
}

Montaje y resultado final de funcionamiento del Reloj WeMos con pantalla OLED

Con esto ya tenemos en funcionamiento nuestro reloj. Aquí tenéis como quedaría.

20 comentarios en “Reloj WeMos ESP8266 y pantalla OLED”

  1. Hola Jorge, estoy intentando hacer el proyecto pero me da error en , no encuentro esa librería no se como se descarga. Me puedes echar una mano?. Gracias

  2. En este tutorial no esta explicada la parte de añadir la placa Esp8266 al entorno arduino. Al añadirla mete varias cosas necesarias para el funcionamiento de la propia placa, entre ellas WifiUDP.h .
    La misma esta en https://github.com/esp8266/Arduino . Explican como añadirlo tanto desde el gestor de tarjetas incorporado en arduino, como añadiéndolo manualmente. Creo que no lo tengo explicado en otros artículos así que próximamente explicare como se hace.

  3. Hola he conseguido que funcione cambiando las letras por WiFiUdp.h . En el monitor serie salen bien los datos pero en la pantalla solo sale la mitad de pantalla, seguire investigando

  4. Hola Jorge, estupendo tutorial.
    Después de haberme descargado las librería y el código me aparece, al compilar, el siguiente mensaje «ATENCIÓN: la librería Timezone-master pretende ejecutarse sobre arquitectura(s) (avr) y puede ser incompatible con tu actual tarjeta la cual corre sobre arquitectura(s) (esp8266).». Me puedes ayudar a solucionarlo. Gracias.

    1. Es un error porque oficialmente la librería no es compatible con esp8266 pero funciona igualmente. Lo único que no funcionaria es guardar o leer en la memoria EEPROM, ya que el esp8266 no tiene, pero en este caso y en la mayoría de usos no es necesario. Aunque de el error te tiene que subir el código sin problemas y funcionar.

      Puedes modificar la librería para que no salte el error pero no es necesario. Sería cambiar en el archivo library.properties de la librería architectures=avr por architectures=*

      Un saludo.

  5. Muy buenas y muy bueno tu tutorial, estoy teniendo problemas para darme cuenta de como extraer el valor de la hora para poder hacer una comparacion. Ejemplo si uso un rtc puedo hacer
    if (hour== 8 && minute == 01)
    { digitalWrite pinLed, HIGH);
    }
    Incluso al querer usar el valor de UNIX, no doy con la tecla, te agradeceria mucho si me dieras una pista.
    Saludos.

  6. Gracias por responder, ya lo he intentado y al compilar me dice que (t) no esta declarado el scketch, voy a ponerlo otra vez y ver si vario la estructura de la escritura. Gracias de nuevo.

  7. BUENAS TARDES,
    Que pena la molestia, pero como hago para poner la hora de Colombia, he intentado cambiando esto pero no he podido obtener el resultado esperado
    TimeChangeRule CEST = {«CEST», Last, Sun, Mar, 2, 60};
    espero por favor tu ayuda.
    mil gracias

    1. Tienes que cambiar el 60 por el número que corresponda a Colombia. No se como van los horarios de invierno y verano allí. En principio sería poner -240 y -300
      TimeChangeRule CEST = {«CEST», Last, Sun, Mar, 2, -240};
      TimeChangeRule CET = {«CET «, Last, Sun, Oct, 3, -300};

  8. hola Buenas Tardes

    estoy intentando subir tu programa a una oled de las pequeñas, pero no he podido modificar el codigo para tener los resultados esperados, serias tan gentil de colaborarme por favor.

  9. Buenas tardes.

    Creo que en las librerias estas usando «SPI.h» pero para I2C lo logico seria poner «Wire.h» ¿Es correcto?

    Yo hice es cambio y en pequeño cambio en la pantalla (La mia es OLED 128×64) y funciona perfecto. Un pequeño cambio antes del SETUP y no hay problema.

    Perdonar pero no se poner el codigo de otra forma

    Gracias por tu proyecto Jorge

    //—————————————–
    // Librerías necesarias
    //—————————————–
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include

    //—————————————–
    // Configurar wifi
    //—————————————–
    const char* ssid = «XXXXXXXXXXXX»; // añade tu ssid
    const char* password = «XXXXXXXXXXXX»; // añade tu password

    //—————————————–
    // Configurar OLED
    //—————————————–
    #define SCREEN_WIDTH 128 // OLED display width, in pixels
    #define SCREEN_HEIGHT 64 // OLED display height, in pixels
    Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

    //—————————————–
    // Definir propiedades NTP
    //—————————————–
    #define NTP_OFFSET 60 * 60 // En segundos
    #define NTP_INTERVAL 60 * 1000 // En milisegundos
    #define NTP_ADDRESSE «hora.roa.es» // URL NTP España
    #define NTP_ADDRESSI «pool.ntp.org» // URL NTP Internacional

    //—————————————–
    // Configura el cliente NTP UDP
    //—————————————–
    WiFiUDP ntpUDP;
    NTPClient timeClient(ntpUDP, NTP_ADDRESSI, NTP_OFFSET, NTP_INTERVAL);

    //—————————————–
    // Horario Europa Central
    //—————————————–
    TimeChangeRule CEST = {«CEST», Last, Sun, Mar, 2, 60}; // Hora de Verano de Europa Central
    TimeChangeRule CET = {«CET «, Last, Sun, Oct, 3, 0}; // Hora Estandar de Europa Central
    Timezone CE(CEST, CET);

    time_t local, utc;

    //—————————————–
    // Configurar Fecha y hora
    //—————————————–
    const char * days[] = {«Domingo», «Lunes», «Martes», «Miercoles», «Jueves», «Viernes», «Sabado»} ;
    const char * months[] = {«Enero», «Febrero», «Marzo», «Abril», «Mayo», «Junio», «Julio», «Agosto», «Septiembre», «Octubre», «Noviembre», «Diciembre»} ;

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.