Ajustar la hora de un RTC (reloj en tiempo real) con Arduino

publicado en: Portada | 14

El RTC DS3231 es uno de los más populares por su precisión, compensada con la temperatura, y los módulos que lo incluyen son de los más utilizados con Arduino por su precio y su sencillez de uso, entre otras cosas, por comunicarse por medio del bus I2C.

El año pasado publiqué una librería para gestionar el DS3231 por I2C desde Arduino, más para describir su funcionamiento que para explotarla, que tenía una gestión básica de la fecha y la hora. Hace unos meses, y a petición popular, modifiqué la gestión de la fecha y la hora del DS3231 separándola de la gestión genérica de Arduino de la fecha y la hora.

Cuando el reloj en tiempo real DS3231 se utiliza para ubicar en el tiempo los datos monitorizados, por ejemplo, almacenándolos en un servicio en la nube para Internet de las cosas, la operación de ajuste de la hora (sincronización) se suele hacer de manera más o menos automática con ayuda del servidor. Para ajustar de forma manual RTC DS3231 se pueden utilizar también recursos locales, estableciendo la fecha y la hora como si fuera un reloj convencional, por ejemplo con pulsadores, un encoder

Módulo DS3231 para Arduino con reloj en tiempo real (RTC) y temperatura

En este artículo propongo algunos recursos para poner en hora un reloj con Arduino, en concreto, cómo modificar la fecha y la hora actuales, que se pueden estar mostrando en una pantalla, incrementando o decrementando un elemento de la fecha o la hora previamente seleccionado: segundo, minuto, hora, día… El resultado es una versión ampliada de la librería para gestionar la fecha y la hora con Arduino con un método para aumentar y otro para disminuir un valor de la fecha y la hora, que puede utilizarse para gestionar el tiempo con cualquier reloj e incluso con ninguno, solamente con Arduino.

Antes de explicar cómo hacerlo, es interesante recordar que hace falta un pequeño algoritmo para adelantar o retrasar un elemento de la fecha y la hora, no basta con sumar o restar una unidad y acumular o detraer del siguiente nivel porque existen unos límites muy diferentes entre ellos y porque los límites de unos pueden depender de los valores de otros: el límite de los días depende del valor del mes y del año.

El algoritmo para incrementar la fecha y la hora consistiría en sumar una unidad a cierto elemento solamente si no ha alcanzado el límite superior, por ejemplo, añadir una hora a la actual si no son las 23 (el último valor posible de la hora). Si se ha alcanzado el límite superior hay que pasar al inferior (la hora cero, en el ejemplo anterior) e incrementar una unidad al siguiente nivel (el día, en el ejemplo) siempre no haya alcanzado su límite superior, en cuyo caso se procedería de la misma forma hasta recorrer todos los niveles desde el que se desea cambiar.

El método para retrasar el valor del objeto tiempo sería el contrario, pero se plantea además el inconveniente de que el límite superior del día (del número del día del mes) depende del valor del mes y del año (incluyendo el siglo) por lo que habría que aplazar ese cambio hasta conocer los valores definitivos de los niveles superiores.

Por último, hay que recordar que en el momento de establecer la hora se cambian todos los valores, por ejemplo los segundos aunque se cambien los minutos, así que habrá que confirmar el cambio (pulsar un hipotético botón «grabar», por ejemplo) en el instante adecuado, de forma sincronizada.

En el siguiente código se pueden ver las nuevas funciones para incrementar o decrementar los elementos del objeto tiempo (fecha y hora). Para utilizarlas, se puede pasar como argumento un objeto tiempo y el número de elemento que se adelanta o se retrasa (que puede hacerse usando las constantes INDICE_SEGUNDO, INDICE_MINUTO, INDICE_HORA…) o simplemente el número de elemento, de forma que utilizará el valor de la fecha que almacena el objeto. Más adelante, también se incluyen algunos ejemplos de cómo usar estos nuevos métodos.

Con estos nuevos métodos ya no tiene sentido mantener el código de los antiguos adelantar_hora() y retrasar_hora(), que se pueden sustituir (manteniendo la compatibilidad con la versión anterior) por wrappers a adelantar_tiempo() y a retrasar_tiempo(), pasando como argumento INDICE_HORA

Para usar un bucle que recorra los elementos de la fecha y la hora, se define una matriz con los límites superiores e inferiores de cada elemento y un par de métodos que se invocarán al definir la fecha y la hora. El primer índice corresponde con el número de elemento (siguiendo la nomenclatura de la versión anterior) y el segundo hace referencia al límite inferior (índice cero) y al superior (índice uno).

Para configurar los límites de los elementos de la fecha y la hora habrá que añadir una llamada a los métodos correspondientes en las funciones que sirven para establecer la hora. Como todos son wrappers de establecer_hora() o de establecer_fecha_siglo() en sus diferentes formatos, solo hará falta cambiar estos dos métodos como en las líneas resaltadas del siguiente código.

También será necesario añadir a la cabecera los nuevos métodos. En las líneas resaltadas del siguiente código se pueden ver los cambios a la cabecera de la versión anterior.

Desde este enlace puedes descargar la librería para gestionar la fecha y la hora con Arduino completa para no tener que modificar la versión anterior.

El uso de las nuevas funciones es muy sencillo: para incrementar un elemento se llama al método adelantar_tiempo() y se incluye como argumento el índice del elemento que se desea adelantar. Por ejemplo, para adelantar la hora almacenada por una variable reloj que apuntara a un objeto tiempo declarado como tiempo reloj;, se escribiría reloj.adelantar_tiempo(INDICE_HORA);. De manera similar, si lo que se quiere es retrasar los minutos de la misma variable reloj de tipo tiempo, se escribiría reloj.retrasar_tiempo(INDICE_MINUTO);.

Para poder hacer pruebas solamente con el software, sin tener que montar el hardware que ajusta el valor de los elementos de la fecha y la hora e incluso sin tener que usar un RTC DS3231, he utilizado esta pequeña función que asigna un valor inicial a la fecha y a la hora leyéndolo desde el puerto serie con el formato {ssmmhhDDMMAASS} en el que ss representa los segundos, mm los minutos, hh las horas, DD los días del mes, MM el número de mes, AA los dos últimos dígitos del año y SS los dos primeros a los que, por simplificar, se les llama siglo aunque sea una unidad menos que el valor real del siglo.

En la siguiente captura de pantalla se muestra como ejemplo la entrada {10152005061620} que produce la fecha 5 de junio de 2016 y la hora 20:15:10, conforme al formato que explicaba antes.

Formato de ajuste de la fecha y la hora con la librería Arduino tiempo

La función de prueba primero adelanta todos los elementos de la fecha y la hora y va mostrando los valores que toman, y luego los retrasa y nuevamente va mostrando los resultados de las operaciones como puede verse en la captura de pantalla de la imagen de abajo.

Fecha y hora ajustada con la librería Arduino tiempo

El siguiente es código el de ejemplo que carga una fecha desde el puerto serie (se puede utilizar desde la consola de Arduino como en las capturas de arriba y la modifica con los nuevos métodos en las líneas resaltadas. Para hacerlo más legible he usado una función llamada mostrar_fecha_hora(), que presenta la fecha y que he utilizado para mostrarla, primero tal como se ha cargado (línea 36) y a cada adelanto (línea 41) y retraso (línea 47) después.

El código de la función, programado ad hoc, está incluido al final del programa de prueba, no se carga desde otro documento. La única característica reseñable es que utiliza un número de elemento especial (negativo) para no mostrar el elemento que se transforma y así poder utilizarla tanto con la fecha y hora recién cargada como con la fecha y hora modificada. Por lo demás solo se encarga de presentar en la consola serie con Serial.print() o Serial.println() los distintos resultados.

La función con la que se carga la fecha desde el puerto serie, que sí puede utilizarse para otras operaciones aunque está muy vinculada a su formato, la he separado en un documento que se carga desde el programa de ejemplo igual que en la versión anterior de la librería de gestión de fecha y hora con Arduino. El código de esta función auxiliar es diferente al de la original, que servía para poner en hora el DS3231, ya que en este caso sirve para establecer los valores del objeto tiempo de la nueva librería.

Si quieres usarlo para hacer pruebas, puedes descargar el código completo del ejemplo, la función para leer por el puerto serie la fecha y la hora y la nueva versión de la librería para gestionarla. También puedes descargar la librería para gestionar la fecha y la hora con Arduino por separado.

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. Chorrocntos

    Excelente trabajo Víctor,

    He probado el codigo y por algún motivo no devuelve nada por el puerto de serie, si uso ejemplos anteriores devuelve fecha y hora sin problemas.

    Un saludo

    • Víctor Ventura

      Hola, Chorrocntos.

      Siento que no te funcione. Así, sin más datos, no se me ocurre cómo ayudarte.

      Supongo que te habrás fijado que este código de prueba devuelve la fecha solamente cuando se introduce un valor por el puerto serie, no está mostrando la fecha cada rato (no hay RTC). Por otra parte, el formato de la fecha que espera que se introduzca es {ssmmhhDDMMAASS} es decir, necesita todos los dígitos del año (no solo los últimos) aunque los dos últimos se escriben primero.

      Gracias por participar en polaridad.es

  2. Chorrocntos

    Ya funciona, realice la siguiente modificación en el void setup:

    void setup()
    {
    Serial.begin(9600);
    }

    Espero que sirva si alguien más experimenta problemas con la comunicación del puerto.

    Un saludo Víctor

  3. Jorge Vallejos

    Hola Víctor, si quiero ajustar la fecha y la hora por medio de pulsadores cómo podría hacerlo? Puedes darme una idea?

    • Víctor Ventura

      Hola, Jorge.

      Puedes usar tres pulsadores, uno para ir cambiando de elemento que se comportara de forma circular (del último pase otra vez al primero) y otros dos para aumentar y disminuir.

      En el código de ejemplo se explica cómo adelantar y retrasar el valor de un elemento con las funciones adelantar_tiempo() y retrasar_tiempo() (solo hay que pasarles como argumento el número de elemento que se modifica)

      Con respecto a cómo reaccionar a la pulsación, puedes usar interrupciones (si tienes disponibles, dependerá del resto del código) o a cada ciclo del programa revisar si el pin al que se conecta está a nivel alto o bajo y elegir entonces el elemento o cambiar su valor (dependiendo de qué esté pulsado en ese momento)

      Si tienes un display LED de 7 segmentos para marcar la hora (no dices nada de tu montaje ni de tu código, tengo que imaginar) puedes apagar los elementos que NO se estén modificando (muy sencillo) o hacer parpadear el SÍ que se está modificando (más código para encender y apagar cada cierto intervalo)

      Espero que eso te ayude. Suerte con tu proyecto.

  4. Jorge Vallejos

    Hola Víctor, gracias por responder. Voy a intentar entonces hacerlo de esa forma, para la visualización por el momento voy a utilizar un LCD 16×2. Un cordial saludo

  5. Marco Zañudo

    Bro, será posible ajustar la fecha y hora mediante bluetooth?

    Madarle la fecha y la hora actual que tiene mi celular, y que arduino lo reciba por el Serial2.

    • Víctor Ventura

      Hola, Marco.

      Sí, sería posible, pero tendrás que preparar un programa que funcione en Android (o en el sistema operativo que tenga tu teléfono), que seguramente dispondrá de Bluetooth y otro en Arduino para controlar un módulo Bluetooth que tendrás que añadir.

      Un sistema genérico para comunicar Android (por ser el más utilizado) con Arduino parece que puede ser interesante para los lectores del blog. Tomo nota para un próximo artículo.

      Gracias por participar en polaridad.es

  6. Miguel

    Hola Victor,
    llevo unos cuantos días intentando adaptar el ejemplo del código completo que pones, pero que en vez de te tome la lectura que le metes por el puerto serie que la coja del RTC que tengo montado(DS3231) en una placa de pruebas, conectado al arduino por I2C. Tu ejemplo me funciona perfectamente y lo veo tanto en el monitor serial como en un display de 20X4 que tengo montado. Pero como te digo el problema viene cuando intento que me lea la fecha y hora del RTC para despues modificarla con tres pulsadores como explicas más arriba.
    Podrías explicarme como se hace esto?
    Si utilizo el ejemplo de solo lectura del RTC me funciona perfectamente
    y veo la fecha y hora.
    No se si me he explicado bien.
    Gracias por enseñarnos a los que estamos empezando sobretodo

    • Víctor Ventura

      Hola, Miguel.

      Intento explicarlo otra vez, pero lo hago un poco a ciegas porque no sé qué es exactamente lo que te da problemas o no te ha quedado claro.

      En primer lugar necesitas establecer el modo de cambio de la hora. Partiendo de que tienes pocas interrupciones libres (que sería la mejor forma de detectar una pulsación) puedes usar una variable booleana que indique este modo y que se establezca con la detección, en el bucle principal, de una pulsación. Como es lógico, además tendrás que resolver por hardware o software el tema de los rebotes de la pulsación en este y los otros casos.

      Una vez en el modo de cambio de hora debes poder elegir el dato que se cambia (horas, minutos…) y marcarlo, por ejemplo parpadeando o apagando los otros valores. Puedes utilizar el mismo método, detectar una (otra) pulsación mientras está activo el modo de cambio de hora.

      Cuando se ha elegido el parámetro que se cambia (horas, minutos…), otra vez con una pulsación, se aumenta o disminuye. Supongo que aquí viene la parte importante, para aumentar el valor del parámetro se utiliza el método adelantar_tiempo pasando como parámetro el índice del elemento que se cambia. Igualmente, con retrasar_tiempo se puede disminuir el valor.

      Para terminar, seguramente utilizando el mismo pulsador del modo, se graba en el DS3231 el cambio de la hora, que hasta ahora estaba en memoria. Si utilizas la librería de gestión de fecha y hora para el módulo RTC DS3231 y Arduino puedes usar el método grabar_registro_DS3231 para hacerlo.

      Como no conozco muy bien tu perfil (qué experiencia tienes en programación o electrónica) puede que te esté explicando esto a un nivel muy diferente al que necesitas (mucho más alto o mucho más bajo), espero que lo entiendas; por eso es más práctico consultar en el blog cosas concretas. En cualquier caso, espero que esto te ayude un poco.

      Gracias por participar en polaridad.es y suerte con tu proyecto.

  7. Miguel

    Hola Victor.
    mi experiencia en programación es muy muy básica pues estoy empezando ahora mismo.
    No podrías poner un ejemplo, leyendo la fecha_hora del RTC y cambiar la hora por ejemplo?
    Lo de los pulsadores lo tengo claro.
    Gracias

    • Víctor Ventura

      Hola, Miguel.

      El código de ejemplo del artículo incluye precisamente el cambio de hora (adelanta y retrasa cada elemento una unidad) y leer la hora desde el DS3231 se explica en otro artículo (el del enlace) y usando la librería propuesta es tan sencillo como invocar el método cargar_fecha_hora.

      Se me ocurre que estás confundiendo la gestión de la fecha y la hora con el DS3231, de la que no trata este post, con las operaciones sobre datos de fecha y hora, que es el objetivo de este artículo ¿Puede ser? Te recomiendo que consultes el otro texto a ver si estoy en lo cierto y en tal caso te sirve de ayuda.

      Saludos.

  8. cristian diaz

    hola victo.

    necesito realizar un proyecto en donde pueda programar hora y fecha mediante botones, y a su vez programar una hora de encendido y apagado para prender un foco, todo esto mediante botones, tambien lo quiero visualizar a travez de la red con un modulo esp8266-12e, espero me puedas ayudar y gracias por tu valiosa ayuda muy buen post.

Deja un comentario

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