Sincronización horaria con un módulo ESP8266

publicado en: Portada | 14

Sin duda, el sistema más preciso para sincronizar la hora es consultarla con un servidor NTP (Network Time Protocol) o con su versión simplificada SNTP.

El cliente y el servidor que utilizan NTP se comunican por UDP (User Datagram Protocol) normalmente en el puerto 123 enviándose paquetes de 48 bytes que incluyen 40 bytes de cabeceras de información de versión, estado, modo… y 8 con la información de fecha y hora.

Para poder hacer la consulta es importante saber como mínimo que los bits 0 a 2 de la cabecera deben indicar el valor 3 decimal para establecer el modo cliente y que el resto pueden ser cero para realizar una consulta al servidor NTP.

Los primeros 4 bytes de los 8 que contienen la fecha y la hora expresan como un número entero los segundos desde el 1 de enero de 1900 (mientras que la forma más frecuente de expresar los segundos fuera de NTP es tomando como referencia el 1 de enero de 1970) Los otros 4 bytes representan la parte fraccionaria de los segundos, lo que simplifica la conversión con una precisión muy alta (entorno a un cuarto de nanosegundo) En la RFC 958 se explica cómo se puede calcular de manera precisa el tiempo en función del tiempo de recepción de los paquetes.

Si bien internamente el control del tiempo es crítico, en la mayoría de las aplicaciones que usan microcontroladores, una precisión de segundos para el «tiempo real» (el presentado al usuario, no para el de control) suele ser más que suficiente. Esta precisión puede conseguirse de manera sencilla consultando un servidor NTP, estimando el tiempo de la transmisión y despreciando la parte fraccionaria. Dependiendo de la precisión con la que se estime el tiempo de respuesta y en menor medida la parte decimal despreciada (grande o pequeña dependiendo casi del azar) no es difícil conseguir errores por debajo de 10 segundos (además es difícil medir con mucha precisión este error) Esta precisión suele ser suficiente para la mayoría de aplicaciones con microcontroladores que no dependan críticamente del tiempo real para su funcionamiento.

La extraordinaria difusión del módulo WiFi ESP8266 ha hecho que existan algunos firmwares que incorporan directamente la sincronización horaria con NTP entre sus órdenes AT como el de Markus Backes, que se puede descargar libremente de GitHub. Usando un firmware como este basta con invocar la orden AT+CIPNTP={GMT} siendo {GMT} el desfase horario con respecto a Greenwich que el caso de España (aunque está bastante cerca de GMT+00) es GMT+01 en verano y GMT+02 en invierno; es decir AT+CIPNTP=2 sincronizaría la hora real de España en invierno.

Para hacer una consulta NTP "a mano" se pueden usar los comandos AT explicados en la entrada sobre la conexión WiFi con el módulo ESP8266 y el tipo de conexión UTP siguiendo el método descrito en la entrada de la librería para manejar el módulo WiFi ESP8266 con Arduino. A grandes rasgos la consulta NTP requeriría la siguiente secuencia de comandos AT:

AT+RST (reiniciar el módulo para asegurarse que no hereda configuración no deseada)
AT+CWMODE=1 (establecer el modo de estación es decir no de punto de acceso)
AT+CWJAP="SleepManager","****" (conectar al SSID SleepManager con la clave ****)
AT+CIPMUX=0 (establecer el modo monoconexión)
AT+CIPSTART="UDP","192.168.1.22",123 (conectar por UDP con el servidor en 192.168.1.22, donde se supone que hay un servidor NTP, usando el puerto 123)
AT+CIPSEND=48 (preparar el envío de 48 bytes)
300000000000000000000000000000000000000000000000 (petición mínima)

Posteriormente se envían 48 bytes, el primero de los cuales debe ser 3 (cliente) y el resto cero (consulta) y se atiende (al menos) al valor recibido en los bytes 41,42,43 y 44 (la parte entera de la fecha/hora expresada en segundos)

Como alternativa a usar un servidor NTP para sincronizar la fecha y la hora real y considerando que es aceptable cierta renuncia a precisión por el tipo de aplicaciones de que se trata, es posible tomar como referencia el tiempo que se indica en las cabeceras HTTP, suponiendo disponible acceso a un servidor web. Si el proyecto incluye el uso de dicho servidor es aún más sencillo realizar una consulta a una página web que devuelva el valor de tiempo a sincronizar en formato de segundos desde 1970, que es precisamente el dato que devuelve la función PHP time() y el que suelen manejar la mayoría de librerías de gestión horaria como la librería Time de Arduino.

Víctor Ventura

Desarrollando aplicaciones para la web conocí el potencial de internet de las cosas, encontré la excusa perfecta para satisfacer la inquietud de aprender electrónica que había tenido desde siempre. Ahora puedo darme el gusto de programar las cosas que yo mismo diseño y fabrico.

Más entradas - Página web

Sígueme:
TwitterLinkedIn

Seguir Víctor Ventura:

Programador multimedia y web + IoT. Mejor con software libre.

Desarrollando aplicaciones para la web conocí el potencial de internet de las cosas, encontré la excusa perfecta para satisfacer la inquietud de aprender electrónica que había tenido desde siempre. Ahora puedo darme el gusto de programar las cosas que yo mismo diseño y fabrico.

14 Respuestas

  1. Jose Luis Rodriguez

    Buenas tardes Victor,

    Han pasado días desde que publicaste este artículo, pero me ha resultado muy util, siempre hay alguien que va por delante y en este caso nos haces el favor de compartir conocimiento, gracias.

    He probado «copiandote», lanzando los comandos directos al servidor ntp, y me funciona, la secuencia, como indicas:

    //*******************************************************
    AT+RST
    AT+CWMODE=1
    AT+CWJAP=»Vodafonexxxx»,»xxxxxxx»
    AT+CIPMUX=0
    AT+CIPSTART=»UDP»,»128.138.140.44″,123 // Connect a specific NTP Server
    AT+CIPSTART=»UDP»,»pool.ntp.org»,123 // Connect NTP pool, More robust and reliable.

    AT+CIPSEND=48
    300000000000000000000000000000000000000000000000

    // Recibo el paquete de datos de respuesta del servidor ntp

    AT+CIPCLOSE
    //*******************************************************

    Realmente querría abrir 2 conexiones, una TCP para subir datos, la otra NTP para sincronizar el reloj (DS3231). He probado con AT+CIPMUX=1 para poder tener 2 conexiones. La secuencia de comandos es la que te indico abajo:

    //*******************************************************
    // Si conecto con CIPMUX=1, Conecta, pero no me devuelve datos.
    AT+RST
    AT+CWMODE=1
    AT+CWJAP=»Vodafonexxxx»,»xxxxxxxxx»
    AT+CIPMUX=1
    AT+CIPSTART=1,»UDP»,»192.168.1.22″,123
    AT+CIPSEND=1, 48, «128.138.140.44»,123

    AT+CIPSEND=1,48
    300000000000000000000000000000000000000000000000

    // Problema: NO Recibo el paquete de datos de respuesta del servidor ntp

    AT+CIPCLOSE=1
    //*******************************************************

    Y aparentemente conecta con el servidor, pero no me devuelve el paquete de datos con la hora actualizada.

    Sin hacerte perder mucho tiempo, sabes el motivo os qué estoy haciendo mal? Gracias mil!

    • Víctor Ventura

      Hola, José Luis.

      Así, a ojo, no se me ocurre qué puede ser. Ahora mismo no puedo probarlo pero, en cuanto tenga un rato le echo un ojo a ver si te puedo ayudar.

      Gracias por participar en polaridad.es

    • Víctor Ventura

      Hola, José Luis. Por poco me olvido de esto 🙂

      He estado probando y no se me ocurre cuál puede ser el error, me llegan los datos que espero (es decir, no encuentro un error) 🙁

      Como al copiar el código sin formato salen cosas raras en el comentario, te pego el código que yo he usado, por si algo de lo que has escrito es el problema.

      Saludos

  2. Jose Luis Rodriguez

    Victor, gracias por tu tiempo.

    Tienes razón: lo he probado de nuevo con 3 servidores …
    AT+CIPSTART=1,»UDP»,»213.251.52.234″,123
    AT+CIPSTART=1,»UDP»,»pool.ntp.org»,123
    AT+CIPSTART=1,»UDP»,»192.168.1.22″,123

    Y los 2 tipos de conexión:
    AT+CIPMUX=1
    AT+CIPMUX=0

    Me ha fallado los primeros intentos con ambos tipos de conexión, y tras resetearlo un par de veces me ha funcionado correctamente, con los 3 servidores y los 2 tipos de conexión.

    Pensaba que podía ser por la señal, pero no está mal, así que no veo el motivo, probaré un poco más.
    +CWLAP:(3,»VodafoneXXXX»,-57,»88:13:35:222:35:b9″,1,8,0)

    Gracias de nuevo,

    JLR

  3. JhonyVam

    Excelente aportación!

    Al realizar los pasos si me respondió, solo que tardo mas de 1 minuto en enviar la respuesta.

  4. Alexander

    Hola he tratado de realizar esa misma conexión, he seguido tu codigo pero los datos que me devuelve no lo puedo interpretar…Me puedes ayudar?
    +IPD,1,48:4[02]0é[00][00][06]c[00][00]žÁ>[16]JÝ£‹÷~S[1C][02]00000000Ý£ó}6¿EÝ£ó}<n‚

    • Víctor Ventura

      Hola, Alexander.

      Como estás recibiendo 48 bytes, es probable que te esté funcionando

      Para calcular la fecha y la hora, como mínimo, tienes que formar un número entero largo con los bytes 41 a 44 (siendo 1 el primer byte recibido) y ese valor es el número de segundos transcurrido desde el año 1900. Si quieres más precisión puedes usar el resto de los datos.

      ¡Suerte!

  5. Francisco

    Hola, llevo algún tiempo jugando con la ESP8266, y el sketch (con el IDE Arduino) que resumo me funcionaba bien antes de estas navidades (por cierto, feliz año…):

    #include «sntp.h»
    #include
    sntp_setservername(0, «es.pool.ntp.org»);
    sntp_init();
    if (sntp_get_current_timestamp() != 0) { setTime(sntp_get_current_timestamp() – 28800); }
    sntp_stop();

    ¿Es posible que los servidores horarios hayan cambiado sus «protocolos» por algún motivo y «sntp.h» ya no sirva?.
    Gracias y saludos.

    Gracias.

  6. Francisco

    Pues era evidente que los servidores horarios no iban a cambiar sus protocolos facilmente, el error ha sido mío, pues al intentar «jugar» con los comandos nativos de la ESP8266:

    #ifdef ESP8266
    extern «C» {
    #include «user_interface.h»
    #include «sntp.h»
    #endif
    }

    al conectar ESP8266 con mi router dándole una ip fija, no «generaba» DNS, una vez que trabajé con las librerías habituales:

    #include
    #include
    #include

    configuré DNS y la ESP8266 se conecta sin problemas por UDP y TCP.

    WiFi.config({192, 168, 1, 100}, {192, 168, 1, 1}, {255, 255, 255, 0}, {192, 168, 1, 1}); //WiFi.config(ip, gateway, subnet, DNS);

    gracias y saludos.

  7. ulises

    Hola Victor
    Recivo los 48 bits pero al meter en un long int el bit 41,42,43,44, me da cualquier numero. Seria de muchisima utilidad cualquier alluda.
    Desde ya muchas gracias por todo! Saludos.

Deja un comentario

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