Reloj UTC y local con Arduino

Reloj sin display

Ha llovido mucho desde que monté el primer reloj digital, basado en puertas lógicas, que utilizaba la frecuencia de la red eléctrica como patrón de tiempo. Quizá por esta razón, uno de los primeros proyectos que he realizado con Arduino es un reloj digital para el cuarto de la radio utilizando un módulo RTC basado en el chip DS 1307 de Maxim Integrated.

Como ejemplo para una charla sobre “Arduino en el cuarto de la radio” que he compartido en el club URD-RCE de Donostia, he montado un reloj y calendario digital que muestra la fecha y las horas, local y UTC. Se trata de un módulo basado en el chip DS 3231SN, fabricado también por Maxim Integrated. El módulo incluye además una memoria ATHYC532.

El DS3231 es más preciso que su antecesor, el DS1307, porque el patrón de tiempo lo genera un cristal TCXO compensado en temperatura. Una batería de 3,3 – 4V mantiene el reloj en marcha cuando se desconecta la alimentación externa. La comunicación con el Arduino se realiza a través del puerto I2C. El módulo, incorpora además una batería recargable tipo LIR2032. Dispone de dos alarmas programables.

Este tipo de módulos se utilizan habitualmente en proyectos que mantienen sistemas que requieren un encendido y apagado programado pudiendo registrar los eventos en la memoria EEPROM ATHYC532.

Para programar el calendario y los relojes, he utilizado la librería RTClib.h, No es la más completa de entre las que he podido escoger, pero es la que menos problemas me ha creado para un proyecto tan sencillo. El proyecto utiliza pocos componentes, cuyo costo es inferior a los 12€:

Componente Tipo Costo
Placa Arduino NANO 3,70
Módulo RTC DS3231SN 2,40
Display + Adaptador I2C 1602 3,29
Tira de 40 pines hembra    
Conectores 4 pines M/H Molex  
Circuito impreso perforado 60 x 40mm  

Comprando todos los componentes en eBay, suman 9,39€ más unos pocos céntimos por la tira de pines, los conectores Molex y el circuito impreso perforado que se puede encontrar en el mercado nacional por 1€ las 10 placas. La caja y alimentación quedan a criterio del montador.

El sketch

El software de un proyecto con Arduino se conoce como “sketch” y la extensión es *.ino. El código fuente se puede descargar desde este repositorio de GitHub

  • clicar en “Clone or download” y
  • en la nueva ventana “Download ZIP”.
  • extraer los ficheros en una carpeta.
  • Doble clic sobre el archivo clock_charla.ino,
  • el IDE nos indicará que clock_charla.ino debe incluirse en una carpeta nueva, aceptar.
  • Si tenemos instalado en el PC el IDE de Arduino, doble clic sobre el archivo clock_charla.ino que lanzará el editor del IDE y cargará el archivo en el editor de código.

De esta forme habremos instalado el fichero principal que tiene la extensión. A continuación, debemos incluir en el IDE los dos ficheros auxiliares del sketch, para ello, debemos clicar en la pestaña “Programa” y en el menú emergente, clicar en “Añadir fichero“. Saldrá una nueva ventana de ayuda para buscar ficheros:

  • Ir a la carpeta donde hemos guardado los ficheros descomprimidos,
  • clicar sobre “clock_auxiliar.cpp”.
  • Volver a la misma carpeta,
  • clicar sobre “clock_auxiliar.h”.

Se habrán añadido al editor de código los dos ficheros que contienen las funciones auxiliares (*.cpp) y la cabecera (*.h) . A pesar de que los tres fichero no contienen más de 200 líneas en total y se podrían haber editado en un solo fichero, para aportar claridad y como ejercicio, se ha dividido en sketch en tres:

  • clock_charla.ino, fichero principal,
  • clock_auxiliar.cpp, funciones de usuario,
  • clock_auxiliar.h, cabecera.

Para que el sketch funcione, debemos tener cargadas dos librerías externas. En Internet se pueden descargar tutoriales para instalar librerías.

LiquidCrystal_I2C.h, para el puerto de comunicación I2C, que se puede descargar del repositorio de LiquidCrystal

RTClib.h, para el DS3231 que se puede descargar del repositorio de Adafruit

En el cuerpo del scketch, antes de la función void setup (); están las funciones que utilizan clases rtc y lcd:

  • void showDate ();                           //Muestra en el display la fecha
  • void showLocalTime ();                 //Muestra en el display la hora local
  • void showUtcTime ();                    //Muestra en el display la hora UTC
  • void setTimeClock ();                     //Ajusta la fecha y/o la hora

La función de ajuste del reloj se invoca desde setup () y está inhabilitada por las dos barras de comentarios. Para poner en hora el reloj desde el editor de código del IDE, hay que “descomentar” (borrar las barra al inicio) e introducir los datos de ajuste en las variables:

void setTimeClock () {
int initYear = 2019;
int initMonth = 10;
int initDay = 20;
int initHour = 21;
int initMinute = 27;
rtc.adjust(DateTime(initYear, initMonth, initDay, initHour, initMinute, 0));
}

Con este ejemplo estaríamos ajustando el tiempo a las 21:27:00 horas UTC del 20 de octubre de 2019. A continuación, compilar y cargar en la memoria del microprocesador (“subir“) el código. Marcar inmediatamente la llamada a la función con dos barras (//) al inicio y guardar el archivo. La hora que se debe introducir es la correspondiente a UTC. Los datos se ajustan en tiempo de compilación en, aproximadamente, 10 o 12 segundos después de haber pulsado la instrucción para compilar, por lo que es necesario tener en cuenta este detalle si se desea una precisión de unos pocos segundos.

La función setup() se ejecuta una sola vez, cada vez que se arranca el programa (Se enciende el reloj). La función loop () es un bucle que se repite indefinidamente mientras se ejecuta el programa (se mantiene encendido el reloj).

Cada bucle, tiene la duración de un segundo para que refrescar el segundero del reloj local. Para controlar la duración del bucle, he evitado utilizar la función delay (), aplicando una condición de tiempo para el retardo basada en los millis ().

El flujo del programa en cada bucle es muy simple: initialDAY es una variable que almacena el día del mes del tiempo inicial. Esta variable se carga por primera en la función setup (). El bucle loop (), una vez cada segundo ejecuta las siguientes funciones:

DateTime nowTime = rtc.now(); //Se crea un objeto DateTime con el tiempo actual

if (nowTime != initialDAY) { //Si el día actual es diferente a initialDAY

initialDAY = nowTime.day(); //initialDAY toma el valor del día actual

String s_date = processDate (nowTime); //la función processDate () recibe los datos del tiempo actual y almacena en un objeto de la clase String la fecha en la variable s_date

showDate (s_date)}; //La función showDate () recibe la cadena de texto con la fecha actual y la muestra en el display. De esta forma, solo escribe la fecha en el display al inicio y cuando cambia el día del mes.

String s_hour_local = processLocalTime (nowTime); //La función processLocalTime () recibe los datos del tiempo actual y los almacena en un objeto de la clase String la hora local s_hour_local.

showLocalTime (s_hour_local); //La función showLocalTime (), recibe la cadena texto con la hora local y la muestra en el display.

String s_hour_utc = processUtcTime (nowTime); // La función processUtcTime (), recibe los datos del tiempo actual y los almacena en un objeto de la clase String la hora UTC s_hour_utc.

showUtcTime (s_hour_utc)}; //La función showUtcTime (), recibe la cadena texto con la hora UTC y la muestra en el display.

Este proceso se repite cada segundo mientras el Arduino tenga encendida la alimentación.

Las funciones de usuario auxiliares se encuentran en el fichero clock_auxiliar.cpp:

  • int calcUtcOffset (DateTime t)               //Calcula ajuste horario
  • void clockSetup (DateTime t)                //Inicia el reloj
  • String nameOfTheDayOfTheWeek (int d)  //Devuelve día de la semana
  • String nameOfMonth (int m)                 //Devuelve el nombre del mes
  • String processDate (DateTime t)           //Devuelve la fecha formateada 
  • String processLocalTime (DateTime t)   //Devuelve la hora local forma
  • String processUtcTime (DateTime t)  //Devuelve la hora UTC formatea

Comentarios

El horario internacional es el Tiempo Universal Coordinado (UTC). Es el que utilizamos los radioaficionados para registrar nuestros comunicados. Los horarios locales son el resultado de modificar el tiempo UTC al huso horario y a los convenios de cambio de horario (invierno y verano) que se producen por razones de ahorro energético.

Hay librerías para Arduino que incluyen métodos para calcular el tiempo local. Sin embargo, en este caso, lo único que necesitamos es conocer el tiempo de ajuste entre la hora local y UTC en nuestro huso horario y período de tiempo (horario de verano e invierno) tanto en la Península como las Islas Canarias.

Un RTC es un contador de segundos que transcurren a partir de una fecha determinada. Los métodos de las librerías, realizan los cálculos para conocer la fecha y la hora que corresponden al tiempo que registra el RTC.

El sketch que controla el reloj, utiliza la medida del tiempo UTC sin modificar por la aplicación del tiempo local. Para conocer la hora local, es necesario aplicar la desviación del horario local y el UTC que depende del huso horario y del período de aplicación (horario de invierno y horario de verano). En España peninsular es de 2 horas en verano y 1 hora en invierno. Como en el resto de la UE, el cambio de horario estacional se realiza el último domingo del mes de marzo y el último domingo del mes de octubre cuya fecha es diferente cada año. En España, se realiza a las 2 horas (hora local). En el resto de los países de la UE pueden tener un horario de cambio diferente

La forma más cómoda de aplicar la diferencia horaria entre la hora local y la hora UTC es conocer si el tiempo actual corresponde al horario de verano o al de invierno cada vez que se debe mostrar en el display, es decir: cada segundo. Esta tarea la realiza la función:

bool isTimeSummer (DataTime dt);

La función recibe el tiempo actual, revisa todas las posibilidades de que sea verano y devuelve verdadero (HIGH o 1) si cumple alguna de las condiciones o falso (LOW o 0) si no cumple ninguna.

Las condiciones para que sea verano son alguna de las siguientes:

Si el mes del tiempo actual es mayor que 3 (marzo) y menor que 10 (octubre)

Si el mes del tiempo actual es igual a 3 (marzo), el día del mes es mayor que el último día del mes antes de la semana del último domingo (día 24), ((el día de la semana es domingo (0) y la hora local mayor de la hora de cambio (2)) o ( 24 – el día actual + el día de la semana (1=Lunes…) < 0))*

Si el mes del tiempo actual es igual a 10 (octubre), el día del mes es mayor que el último día del mes antes de la semana del último domingo (24), ((el día de la semana es domingo (0) y la hora local menor de la hora de cambio (2)) o ( 24 – el día actual + el día de la semana (1=Lunes…) >= 0))*

* Los meses de marzo y octubre tienen 31 días, luego el primero de los último siete días en que puede caer un domingo es el 25 y el último día antes de que comiencen esos siete día es el 24. La constante LAST_DAY_OF_THE_LAST_WEEK = 24; toma este valor

Si a LAST_DAY_OF_THE_WEEK se le resta el día actual del mes y se le suma el día de la semana en que cae el día actual, resultará un valor mayor o igual a 0 si es anterior al domingo o, menor de 0 si es posterior, de forma que se puede establecer si al tiempo UTC actual se le debe aplicar el horario de invierno o verano para hallas la hora UTC

Última semana del mes

Ejemplo para el año 2020:

Marzo, último domingo = día 29

  • día 26 (jueves:4) = 24 – 26 + 4 = 2 (invierno)
  • día 27 (viernes:5) = 24 – 27 + 5 = 2 (invierno)
  • día 28 (sábado:6) = 24 – 28 + 6 = 2 (invierno)
  • día 30 (lunes:1) = 24 – 30 + 1 = -5 (verano)
  • día 31(martes:2) = 24 – 31 + 2 = -5 (verano)

Octubre, último domingo = 25

  • día 26(Lunes:1) =24 – 26 + 1 = -1 (invierno)
  • día 27(Martes:2) =24 – 27 + 2 = -1 (invierno)
  • día 28(Miércoles:3) = 24 – 28 + 3 = -1 (invierno)
  • día 29(Jueves:4) = 24 – 29 + 4 = -1 (invierno)
  • día 30(Viernes:5) = 24 – 30 + 5 = -1 (invierno)
  • día 31(Sábado:6) = 24 – 31 + 6 = -1 (invierno)

Partiendo de esta fórmula es sencillo establecer, mediante las funciones de la librería, el horario local y UTC en período de horario de verano e invierno.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s