Tutorial para crear un reloj en pantalla OLED con ESP32 y Arduino

Reloj en tiempo real con ESP32 y pantalla OLED SSD1306

Este tutorial muestra cómo implementar un reloj en tiempo real utilizando un módulo ESP32 y una pantalla OLED de 0.96 pulgadas (controlador SSD1306) en el entorno Arduino. Se presentan dos enfoques: uno básico sin conexión a internet y otro avanzado con sincronización NTP mediante WiFi.

1. Requisitos de hardware y software

Hardware necesraio:

  • Placa de desarrollo ESP32 (cualquier modelo).
  • Pantalla OLED de 0.96 pulgadas con interfaz I2C (SSD1306).
  • Cables dupont para conexión (VCC, GND, SDA, SCL).
  • Entorno WiFi disponible (opcional, solo para la versión avanzada).

Software y bibliotecas:

  • Arduino IDE con soporte para ESP32 instalado.
  • Bibliotecas: Adafruit SSD1306, Adafruit GFX y Wire.h.
  • La biblioteca time.h ya está incluida en el entorno ESP32.

Conexiones de hardware:

Pin OLED Pin ESP32 Descripción
VCC 3.3V Voltaje seguro para evitar daños en la pantalla
GND GND Conexión a tierra
SDA GPIO21 Línea de datos I2C
SCL GPIO22 Línea de reloj I2C

2. Implementación del código

Se ofrecen dos variantes de código. La primera establece la hora manualmente y utiliza el temporizador interno del ESP32, mientras que la segunda sincroniza la hora con un servidor NTP a través de WiFi.

Versión Básica: Reloj sin conexión a red

En esta versión, la hora se configura manualmente al iniciar el dispositivo. El ESP32 actualiza la pantalla cada segundo basándose en su reloj interno.


#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <time.h>

#define ANCHO_PANTALLA 128
#define ALTO_PANTALLA 64
#define RESET_OLED -1
#define DIRECCION_PANTALLA 0x3C

Adafruit_SSD1306 pantalla(ANCHO_PANTALLA, ALTO_PANTALLA, &Wire, RESET_OLED);

struct tm horaActual;
unsigned long marcaTiempoAnterior = 0;
const unsigned long intervaloActualizacion = 1000;

void configurarHoraInicial() {
  struct tm tiempoConfig;
  tiempoConfig.tm_year = 2023 - 1900;
  tiempoConfig.tm_mon = 11 - 1;
  tiempoConfig.tm_mday = 15;
  tiempoConfig.tm_hour = 14;
  tiempoConfig.tm_min = 45;
  tiempoConfig.tm_sec = 0;
  tiempoConfig.tm_isdst = 0;
  time_t t = mktime(&tiempoConfig);
  settimeofday((const timeval*)&t, NULL);
}

void setup() {
  Serial.begin(115200);
  if (!pantalla.begin(SSD1306_SWITCHCAPVCC, DIRECCION_PANTALLA)) {
    Serial.println(F("Error al inicializar OLED"));
    while (true);
  }
  pantalla.clearDisplay();
  pantalla.setTextColor(SSD1306_WHITE);
  configurarHoraInicial();
}

void loop() {
  unsigned long marcaTiempoActual = millis();
  if (marcaTiempoActual - marcaTiempoAnterior >= intervaloActualizacion) {
    marcaTiempoAnterior = marcaTiempoActual;
    getLocalTime(&horaActual);
    pantalla.clearDisplay();

    pantalla.setTextSize(1);
    pantalla.setCursor(10, 5);
    pantalla.printf("%02d/%02d/%04d", horaActual.tm_mday, horaActual.tm_mon + 1, horaActual.tm_year + 1900);

    pantalla.setTextSize(2);
    pantalla.setCursor(20, 25);
    pantalla.printf("%02d:%02d:%02d", horaActual.tm_hour, horaActual.tm_min, horaActual.tm_sec);

    pantalla.setTextSize(1);
    pantalla.setCursor(10, 55);
    const char* diasSemana[] = {"Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"};
    pantalla.print(diasSemana[horaActual.tm_wday]);

    pantalla.display();
  }
}

Versión Avanzada: Reloj con sincronización NTP por WiFi

Esta versión se conecta a una red WiFi y sincroniza la hora con un servidor NTP, prooprcionando mayor precisión.


#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <time.h>

#define ANCHO_PANTALLA 128
#define ALTO_PANTALLA 64
#define RESET_OLED -1
#define DIRECCION_PANTALLA 0x3C

const char* nombreRed = "Tu_Nombre_WiFi";
const char* contrasena = "Tu_Contrasena";
const char* servidorNTP = "pool.ntp.org";
const long desplazamientoGMT = 3600 * 2; // Ajustar según zona horaria
const int ajusteHorarioVerano = 0;

Adafruit_SSD1306 pantalla(ANCHO_PANTALLA, ALTO_PANTALLA, &Wire, RESET_OLED);
struct tm horaActual;
unsigned long marcaTiempoAnterior = 0;
const unsigned long intervaloActualizacion = 1000;

void conectarWiFi() {
  Serial.printf("Conectando a %s\n", nombreRed);
  WiFi.begin(nombreRed, contrasena);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConexión establecida");
}

void sincronizarHoraNTP() {
  configTime(desplazamientoGMT, ajusteHorarioVerano, servidorNTP);
  Serial.println("Esperando sincronización NTP...");
  while (!getLocalTime(&horaActual)) {
    delay(1000);
  }
  Serial.println("Hora sincronizada");
}

void setup() {
  Serial.begin(115200);
  if (!pantalla.begin(SSD1306_SWITCHCAPVCC, DIRECCION_PANTALLA)) {
    Serial.println(F("Error al inicializar OLED"));
    while (true);
  }
  pantalla.clearDisplay();
  pantalla.setTextColor(SSD1306_WHITE);
  conectarWiFi();
  sincronizarHoraNTP();
}

void loop() {
  unsigned long marcaTiempoActual = millis();
  if (marcaTiempoActual - marcaTiempoAnterior >= intervaloActualizacion) {
    marcaTiempoAnterior = marcaTiempoActual;
    getLocalTime(&horaActual);
    pantalla.clearDisplay();

    pantalla.setTextSize(1);
    pantalla.setCursor(5, 5);
    pantalla.printf("Fecha: %02d-%02d-%04d", horaActual.tm_mday, horaActual.tm_mon + 1, horaActual.tm_year + 1900);

    pantalla.setTextSize(2);
    pantalla.setCursor(15, 25);
    pantalla.printf("%02d:%02d:%02d", horaActual.tm_hour, horaActual.tm_min, horaActual.tm_sec);

    pantalla.setTextSize(1);
    pantalla.setCursor(5, 55);
    String diaSemana;
    switch (horaActual.tm_wday) {
      case 0: diaSemana = "Domingo"; break;
      case 1: diaSemana = "Lunes"; break;
      case 2: diaSemana = "Martes"; break;
      case 3: diaSemana = "Miércoles"; break;
      case 4: diaSemana = "Jueves"; break;
      case 5: diaSemana = "Viernes"; break;
      case 6: diaSemana = "Sábado"; break;
    }
    pantalla.print(diaSemana);

    pantalla.display();
  }
}

3. Explicación de conceptos clave

  • Gestión de tiempo con time.h: La biblioteca time.h permite manejar estructuras de tiempo (struct tm) y funciones como getLocalTime() para obtener la hora actual.
  • Actualización no bloqueante: Se utiliza millis() para controlar el intervalo de actualización sin usar delay(), lo que permite ejecutar otras tareas mientras tanto.
  • Formato de visualización: Se aplica setTextSize() para ajustar el tamaño de los números y printf() con especificadores como %02d para garantizar que los dígitos siempre tengan dos caracteres.
  • Sincronización NTP: En la versión avanzada, configTime() establece el servidor NTP y la zona horaria, permitiendo que el ESP32 obtenga la hora exacta de internet.

4. Solución de problemas comunes

  • Error en conexión WiFi: Verificar que el nombre y contraseña de la red sean correctos, y que la red sea de frecuencia 2.4 GHz, ya que el ESP32 no soporta 5 GHz por defecto.
  • Pantala OLED sin imagen: Comprobar la dirección I2C (usualmente 0x3C o 0x3D) y asegurar que las conexiones SDA y SCL sean correctas.
  • Fecha u hora incorrecta: Recordar que en struct tm, el año se almacena como años desde 1900, y el mes desde 0 (enero = 0).
  • Desfase de tiempo en versión básica: El reloj interno del ESP32 puede acumular errores; para mayor precisión, usar la versión con NTP.

5. Posibles mejoras y extensiones

  • Integrar sensores de temperatura o humedad (como DHT22) para mostrar datos ambientales en la pantalla.
  • Agregar funcionalidad de alarma con un zumbador o LED que se active a una hora determinada.
  • Implementar control de brillo automático mediante un sensor de luz (LDR).
  • Personalizar el diseño gráfico en la pantalla OLED, por ejemplo, añadiendo iconos o animaciones simples.

Etiquetas: ESP32 Arduino OLED SSD1306 NTP

Publicado el 7-2 16:03