Volver al inicio

Servidor NTP en ESP8266: Hora Precisa Sin Internet | Guía

Este artículo describe la implementación de un servidor NTP autónomo basado en ESP8266 con precisión de microsegundos. Se analizan en detalle los algoritmos de compensación de retrasos de red y el mantenimiento de la precisión horaria entre sincronizaciones. La solución es relevante para condiciones de bloqueo de servidores NTP externos.

Servidor NTP Autónomo: Cómo ESP8266 Resuelve Problemas de Bloqueo de Hora
Advertisement 728x90

Servidor NTP autónomo en ESP8266: Garantizando tiempo preciso sin dependencias externas

Cuando las conexiones a internet son poco fiables y los servidores NTP externos se bloquean, una fuente local de tiempo preciso se vuelve esencial. Te guiaremos paso a paso para construir un servidor NTP autónomo en el ESP8266 con precisión de microsegundos que sigue marcando el tiempo incluso sin acceso constante a internet.

Por qué las soluciones estándar fallan

Las infraestructuras modernas a menudo no pueden acceder a servidores NTP globales debido a restricciones de red y políticas de listas blancas. Cuando se cortan las fuentes de tiempo externas, los relojes del sistema se desincronizan, lo que representa un gran problema para:

  • Transacciones financieras
  • Registro de seguridad
  • Sistemas de computación distribuidos
  • Dispositivos IoT con necesidades estrictas de temporización

¿La solución? Construir un servidor NTP local que mantenga el tiempo preciso de forma autónoma entre sincronizaciones. El ESP8266 es ideal: bajo consumo de energía, WiFi integrado y potencia suficiente para manejar marcas de tiempo.

Google AdInline article slot

Arquitectura del sistema de tiempo

El principal desafío es superar las limitaciones de la biblioteca estándar de tiempo de Arduino, que solo rastrea segundos enteros. NTP (RFC 5905) exige precisión de microsegundos. Aquí entra nuestra clase personalizada JbTime, que soluciona dos problemas fundamentales:

  • Falta de soporte para segundos fraccionarios en el tiempo del sistema
  • Acumulación de deriva entre sincronizaciones

Cómo funciona:

  • Cuando llega el tiempo de un servidor externo, captura la marca de tiempo de microsegundos del procesador
  • Cada solicitud calcula la diferencia desde el momento de la sincronización hasta ahora
  • Los factores de corrección incluyen el retraso de red con esta fórmula: offset = ((t2 - t1) + (t3 - t4)) / 2
#ifndef jb_time
#define jb_time

#include <stdint.h>

#define JB_TIME_MAX_AGE  600

class JbTime {
private:
  uint64_t _last;
  uint64_t _sec;
  uint32_t _usec;
  uint32_t _mark;

public:
  bool ok;
  bool fresh;

  JbTime(){
    _sec = 0;
    _usec = 0;
    _mark = 0;
    _last = 0;
    ok = false;
    fresh = false;
  }

  inline bool old() {
    if(_sec == 0) return true;
    if((_sec - _last) > JB_TIME_MAX_AGE) return true;
    return false;
  }

  inline void settime(uint64_t sec, uint32_t usec, uint32_t mark = micros()){
    _mark = mark;
    _sec = sec;
    _sec += usec / 1000000;
    _usec = usec % 1000000;
    ok = true;
    _last = sec;
  }

  inline void gettime(uint64_t *o_sec, uint32_t *o_usec){
    if(ok){
      uint32_t now = micros();
      uint32_t delta = now - _mark;
      _mark = now;

      uint64_t total = (uint64_t)_usec + delta;
      _sec += total / 1000000;
      _usec = total % 1000000;

      *o_sec = _sec;
      *o_usec = _usec;
    } else {
      *o_sec = 0;
      *o_usec = 0;
    }
  }
};
#endif

Manejo de solicitudes NTP

El protocolo NTP funciona en el puerto UDP 123 y envía el tiempo como un valor de 64 bits (32 bits de segundos, 32 bits de fracciones). ¿La parte más complicada? Compensar el retraso de red. Aquí está el algoritmo:

Google AdInline article slot
  • El cliente envía la solicitud con la marca de tiempo T1 (su tiempo local)
  • El servidor la recibe en T2, envía la respuesta en T3
  • El cliente recibe la respuesta en T4
  • Desfase: offset = ((T2 - T1) + (T3 - T4)) / 2

La clase JbNTPClient maneja esto teniendo en cuenta las peculiaridades del ESP8266:

  • Paquetes formateados según RFC 5905
  • Ajuste automático de época (NTP comienza en 1900, Unix en 1970)
  • Filtra respuestas erróneas
bool JbNTPClient::requestTime(const char* server, JbTime * mytime) {
  // ... initialization
  
  // Calculation T1
  uint64_t t1_sec = 0;
  uint32_t t1_usec = 0;
  systime->gettime(&t1_sec, &t1_usec);
  t1_sec += NTP_UNIX_EPOCH_DIFF;
  
  // Sending request
  udp.beginPacket(timeServerIP, ntpPort);
  udp.write((uint8_t*)&packet, sizeof(NTPPacket));
  udp.endPacket();

  // Ozhidanie otveta with taymautom 2000 ms
  while (udp.parsePacket() == 0) {
    if (millis() > timeout) return false;
    delay(10);
  }

  // Raschet T4 and setevoy zaderzhki
  double t4 = ntpToDouble(t4_sec, t4_frac);
  _networkDelay = (t4 - t1) - (t3 - t2);
  double offset = ((t2 - t1) + (t3 - t4)) / 2;

  // Installation skorrektirovannogo vremeni
  mytime->settime(sec, usec, t4_mark);
  return true;
}

Lado del servidor: Sirviendo tiempo en la red local

Una vez sincronizado con servidores externos, el dispositivo se convierte en una fuente NTP completa. Maneja las solicitudes de clientes mediante el método serve():

  • Analiza el paquete UDP entrante
  • Verifica el modo (espera mode=3 — solicitud de cliente)
  • Construye la respuesta con el tiempo actual de JbTime
  • Incluye el tiempo de procesamiento para máxima precisión

Función clave: La marca de tiempo se captura justo al enviar la respuesta (T3) para minimizar el error. A diferencia de las opciones prefabricadas, extrae el tiempo directamente de un contador de alta resolución, evitando funciones del sistema burdas.

Google AdInline article slot

Características principales

  • Autonomía: Mantiene precisión de 20-30 microsegundos entre sincronizaciones, incluso desconectado durante horas
  • Eficiencia de recursos: El ESP8266 consume menos de 70 mA en activo, ideal para baterías
  • Compatibilidad: Cumple con RFC 5905, funciona con cualquier cliente NTP de inmediato
  • Flexibilidad: Ajusta la edad máxima de los datos (JB_TIME_MAX_AGE) según tus necesidades
  • Tolerancia a fallos: Cambia automáticamente a respaldos si el principal falla

— Editorial Team

Advertisement 728x90

Leer después