¿Cuántas veces te pasa que el reloj de la cocina se desincroniza cada vez que se corta la luz? Este proyecto resuelve ese problema con un ESP32 que consulta la hora oficial por internet cada hora, la muestra en un display de 7 segmentos y ajusta solo el cambio al horario de verano chileno sin que tengas que tocar nada. Al terminar vas a tener un reloj físico de escritorio que cuesta menos que un almuerzo, se conecta a tu WiFi, y nunca más pierde la hora.

En esta guía vas a aprender tres cosas que se aplican a muchos otros proyectos: cómo pedir la hora a un servidor NTP público, cómo se convierte un string POSIX de zona horaria (<-04>4<-03>,M9.1.6/24,M4.1.6/24) en la hora chilena con horario de verano incluido, y cómo controlar el módulo TM1637. el display de 4 dígitos con dos puntos parpadeando, típico de los relojes de mesa noventeros. con solo dos cables de datos.

Cómo funciona el protocolo NTP en 30 segundos

NTP (Network Time Protocol) es un protocolo de red diseñado en 1985 que sigue vigente: es el que sincroniza la hora de tu computador, tu celular y prácticamente todo dispositivo con internet. La idea es simple: existen servidores públicos (pool.ntp.org agrupa miles de ellos) que responden con la hora UTC actual, corregida por retardos de red. Tu ESP32 hace de cliente NTP, pide la hora al arrancar, y la deja disponible para que la leas en cualquier momento.

Diagrama del protocolo NTP entre cliente y servidor de tiempo

La ventaja frente a un módulo RTC tipo DS3231 es doble: no necesitas batería de respaldo (después de un corte de luz el reloj se reajusta solo al reconectar) y la hora que muestras coincide con la hora oficial del país al segundo. La desventaja es que si tu WiFi se cae por horas, el ESP32 queda derivando con su cristal interno, que tiene error del orden de segundos por día. Por eso el código incluye una resincronización cada 60 minutos, suficiente para que el drift no se note.

El módulo TM1637 y por qué usarlo en vez de un OLED

El TM1637 es un driver dedicado a manejar displays de 7 segmentos. Lo destacable no es el chip en sí sino el módulo que se vende con él: viene con 4 dígitos rojos grandes y dos puntos separadores en el medio (segundo y tercer dígito), exactamente el layout de un reloj digital clásico. Ese detalle del colon parpadeante es lo que le da personalidad de reloj y no de "termómetro de laboratorio".

Módulo TM1637 con display de 4 dígitos y dos puntos

Comparado con un OLED SSD1306 o un LCD 16x2, el TM1637 tiene tres ventajas concretas:

  • Se lee de lejos: los dígitos miden ~14mm, contra ~5mm del OLED. Puedes ver la hora desde el otro lado de la pieza sin acercarte.
  • Consume menos y no se degrada: los OLED con contenido estático pierden brillo con el tiempo (burn in); un display de 7 segmentos LED puede quedarse mostrando la misma hora años sin sufrir.
  • Solo 2 pines de datos: CLK y DIO, no ocupa el bus I2C ni SPI. Te deja libre el resto de la placa para agregar sensores o botones.

La contra: solo muestra 4 dígitos, así que no puedes agregar segundos ni fecha en la misma pantalla. Para eso está la sección de variantes al final.

Materiales que vas a necesitar

Piezas requeridas: ESP32, TM1637 y cables jumper

  • 1x placa ESP32 DevKit (cualquier variante con WiFi sirve)
  • 1x módulo display TM1637 de 4 dígitos y 7 segmentos con dos puntos centrales
  • 4x cables jumper macho hembra (o macho macho si vas a soldarlo a la placa)
  • 1x cable USB-A a micro USB para programar y alimentar el ESP32

No necesitas resistencias externas ni fuente extra: el TM1637 ya trae su propio driver y consume ~40 mA, dentro de lo que entrega el pin VIN del ESP32.

Conexión ESP32 ↔ TM1637

El cableado son 4 hilos, sin protoboard obligatoria si soldás directo. La tabla de conexiones:

TM1637 ESP32
VCC VIN (5V)
GND GND
CLK GPIO 19
DIO GPIO 18

Esquema de cableado ESP32 con TM1637

Cualquier GPIO de propósito general del ESP32 te sirve para CLK y DIO. si vas a usar los pines 19 y 18 para otra cosa, cámbialos por 22 y 23, o 32 y 33. Solo evita los pines de solo entrada (34-39) y los que se usan al bootear (0, 2, 12, 15) para que no aparezcan glitches al reiniciar.

Un error común: si conectas VCC al 3V3 en vez del VIN, los dígitos se ven visiblemente más apagados. El TM1637 funciona ahí pero no queda a plena intensidad. Usa siempre VIN (5V del USB) para que el reloj se vea bien de día.

Instala la librería TM1637 en el Arduino IDE

Existen varias librerías para el TM1637; la más estable y compacta es TM1637.h de avishorp. Aunque el autor no la actualiza hace años, funciona perfecto con el core ESP32 actual.

Instalar la libreria TM1637 de avishorp desde el Gestor de Librerias

En el Arduino IDE ve a Programa → Incluir Librería → Administrar Bibliotecas, busca TM1637, y presiona Instalar en la que dice autor "avishorp". Si tienes dudas de tener el core ESP32 instalado, verifica en Herramientas → Placa que aparezca "ESP32 Dev Module". si no, agrega la URL https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json en las preferencias del IDE.

Estructura del código: incluir librerías y definir pines

Empezamos incluyendo las tres librerías: WiFi (viene con el core ESP32), time.h (POSIX estándar) y TM1637Display de avishorp.

C++
#include <WiFi.h>
#include "time.h"
#include <TM1637Display.h>

Los pines se definen como constantes de preprocesador y se instancia el objeto display una sola vez, en global.

C++
// TM1637 pins
#define CLK 19
#define DIO 18

TM1637Display display(CLK, DIO);

El string de zona horaria POSIX (la parte crítica para Chile)

El tutorial original usa la zona de Lisboa ("WET0WEST,M3.5.0/1,M10.5.0"). Para Chile Continental necesitas cambiarla a:

C++
// Chile Continental (Santiago / Valparaiso / Concepcion)
// Ajusta automaticamente al horario de verano (CLST) y de invierno (CLT).
const char* TZ_INFO = "<-04>4<-03>,M9.1.6/24,M4.1.6/24";

Ese string se lee así: la hora estándar (CLT, invierno) es UTC-4, la de verano (CLST) es UTC-3, empieza el primer sábado de septiembre a la medianoche y termina el primer sábado de abril, también a la medianoche. Es la convención vigente desde 2019 (Ley N° 21.181) y pool.ntp.org la respeta automáticamente vía POSIX. Si estás en Isla de Pascua, usa "<-06>6<-05>,M9.1.6/24,M4.1.6/24" en su lugar.

Para otras regiones, el archivo de referencia oficial vive en nayarsystems/posix_tz_db. Copia el valor de la columna "TZ" para la zona que necesites.

Inicializar el tiempo con NTP

La función initTime() hace tres cosas: le dice al ESP32 qué servidor NTP consultar, aplica el string de zona horaria al entorno, y llama a tzset() para que las conversiones tomen la nueva configuración.

C++
void initTime() {
  configTime(0, 0, "pool.ntp.org");
  setenv("TZ", TZ_INFO, 1);
  tzset();
}

Nota: el primer parámetro de configTime (0) es el offset en segundos desde UTC. Lo dejamos en 0 porque el offset ya viene del string POSIX que definimos arriba. es el enfoque limpio que hace que el mismo código funcione en cualquier país con solo cambiar TZ_INFO.

Código completo

Este es el sketch completo. Copia tu SSID, tu password, cambia TZ_INFO al string de Chile mostrado arriba antes de subirlo al ESP32:

C++
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-tm1637-4-digit-7-segment-display-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <WiFi.h>
#include "time.h"
#include <TM1637Display.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// TM1637 pins
#define CLK 19
#define DIO 18

TM1637Display display(CLK, DIO);

// Timezone (Lisbon / Portugal)
// Change if needed - See list of timezones strings: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
const char* TZ_INFO = "WET0WEST,M3.5.0/1,M10.5.0";

// Init Wifi
void initWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
}

// Initialize Time with the specified timezone
void initTime() {
  configTime(0, 0, "pool.ntp.org");
  setenv("TZ", TZ_INFO, 1);
  tzset();
}

// display the time on the screen
void displayTime() {

  // blinking colon
  static bool colon = false;
  colon = !colon;

  // get the current time
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    return;
  }

  int hours   = timeinfo.tm_hour;   // get hours (0-23)
  int minutes = timeinfo.tm_min;    // get minutes (0-59)

  // move hours to the left two digits
  int hourPart = hours * 100;

  // combine hours and minutes into HHMM
  int value = hourPart + minutes;

  // control the colon (for blinking colon)
  uint8_t colonMask;
  if (colon == true) {
    colonMask = 0b01000000;   // turn colon ON
  } else {
    colonMask = 0;            // turn colon OFF
  }

  display.showNumberDecEx(
    value,
    colonMask,   // blink colon
    true         // leading zeros
  );
}

void setup() {
  Serial.begin(115200);

  display.setBrightness(7);
  display.clear();

  initWiFi();
  initTime();
}

void loop() {
  displayTime();
  delay(1000);
}

ESP32 y TM1637 mostrando la hora sincronizada por NTP

Cómo probarlo

  1. Enchufa el ESP32 al PC con el cable USB.
  2. En el Arduino IDE selecciona la placa "ESP32 Dev Module" y el puerto COM correspondiente.
  3. Presiona Subir. Al terminar, abre el Monitor Serial a 115200 baudios.
  4. Deberías ver "Connecting to WiFi.." un par de veces y después el display se encenderá con la hora actual chilena.
  5. Verifica que los dos puntos del display parpadeen cada segundo. es la señal visual de que el loop() está corriendo.

Si el display queda en ---- o en cero, casi siempre es un problema de cableado (revisa CLK vs DIO, es fácil confundirlos) o WiFi mal escrito.

Variantes y mejoras

Convertirlo en reloj despertador con OLED y buzzer

Agregando un OLED SSD1306 (I2C, pines GPIO 21/22), dos botones táctiles y un buzzer piezoeléctrico, el proyecto se transforma en un despertador completo. El TM1637 sigue mostrando la hora en grande, el OLED muestra la fecha completa y la temperatura del cuarto (si sumas un DHT22), y los botones te dejan configurar hasta 3 alarmas horarias que suenan con el buzzer. El código clave: agregar un array struct Alarm alarms[3] y en el displayTime() comparar timeinfo.tm_hour y timeinfo.tm_min contra cada alarma activa.

Segundo display para fecha o temperatura

Si tienes un segundo TM1637 (usan solo 2 pines cada uno, o sea 4 en total), puedes dedicar uno a la hora y otro a mostrar la fecha DDMM alternando con la temperatura interior. El código va en el mismo sketch, solo agregas TM1637Display display2(CLK2, DIO2) y una función displayDate().

Domótica: enviar la hora por MQTT

Si tu casa ya corre Home Assistant o Node RED, agrega PubSubClient.h al sketch y publica la hora local a un topic MQTT cada minuto. Sirve para dispositivos que no tienen NTP propio y para que el sistema sepa cuándo se cortó la conexión (si el topic no se actualiza por 5 minutos, saltó una alarma).

Personalización para Chile

En Chile puedes armar este proyecto completo con piezas en stock en MechatronicStore:

  • Placa ESP32 DevKit con WiFi (SKU X2-10V2). $7.990 CLP
  • Módulo display TM1637 4 dígitos 7 segmentos (SKU D-204). $2.900 CLP
  • Jumpers macho macho 20cm (SKU C-411). $1.990 CLP

Costo total aproximado: $12.880 CLP, sin contar el cable USB (uno viejo de celular Android sirve). Si el tutorial original te pedía un "ESP32 board, any model", el DevKit chileno cumple la misma función que el WROOM que muestran ellos: es la variante genérica más vendida del catálogo y trae los mismos GPIOs que se usan aquí.

Un detalle regional: no cambies pool.ntp.org por un servidor local. pool.ntp.org te asigna automáticamente el servidor más cercano geográficamente, así que desde Chile igual te sirve un nodo sudamericano con latencia baja.

Recursos

Versión chilena con componentes en stock local en MechatronicStore, con string de zona horaria adaptado a Chile Continental e Isla de Pascua.