En esta entrada veremos como utilizar el reloj en tiempo real DS3231 con las placas arduino a través de nuestra librería GFRTC, que permite acceder a la totalidad de las funciones de este circuito integrado, incluyendo las alarmas y el sensor de temperatura interno. Esperamos que además de enseñarte el potencial de este circuito podamos recibir algo de feedback sobre nuestra librería.
El DS3231 es un reloj en tiempo real que puede servir como reemplazo de DS1307 que es muy popular, pero que puede no ser muy apto para conservar el tiempo con precisión.
El DS3231 es ideal para aplicaciones en las que la precisión a largo plazo es muy importante, pudiendo este conservar el tiempo por años gracias a su oscilador interno con compensación de temperatura.
Materiales necesarios para las prácticas.
Para completar los ejercicios propuestos en esta página recomendamos adquirir los siguiente materiales:
Conexión del reloj en tiempo real DS3231 con Arduino
Para conectar un DS3231 con arduino se requieren 4 conexiones y una opcional para la señal de interrupción. En algunos ejemplos a continuación utilizaremos la señal de interrupción, por lo que aparece en la ilustración que se muestra más abajo.
Las conexiones son las siguientes:
- VCC – Alimentación de 5 volts del módulo.
- GND – Tierra o común.
- SCL – Señal de reloj del bus I2C.
- SDA – Señal de datos del bus I2C.
- SQW – Salida de interrupción o señal de reloj.
Librería GFRTC para el DS3231, DS3232 y DS1307
La librería GFRTC fue creada por nuestro equipo de desarrollo con el objetivo de tener una interfaz de software unificada con varios relojes en tiempo real del fabricante Maxim Integrated.
Para poder compilar la librería GFRTC es necesario también tener instalada la librería TimeLib.
Podemos descargar las librerías y ver los repositorios con los siguientes enlaces:
- Repositorio de la librería GFRTC de Geek Factory
- Enlace de descarga de la librería GFRTC
- Repositorio de la librería TimeLib de Geek Factory
- Enlace de descarga de la librería TimeLib
Es importante mencionar que para que los programas que se muestran a continuación funcionen, debemos tener instaladas ambas liberías: GFRTC y TimeLib.
¿Cómo leer la fecha y hora desde el DS3231?
El siguiente programa permite leer la fecha y hora desde el RTC para mostrarla en el monitor serial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /** GeekFactory - "INNOVATING TOGETHER" Distribucion de materiales para el desarrollo e innovacion tecnologica www.geekfactory.mx Basic example for GFRTC, this example shows how to read date/time from RTC and display it on the serial terminal. */ #include <GFRTC.h> void setup() { // prepare serial interface Serial.begin(115200); while (!Serial); // show message on serial monitor Serial.println(F("----------------------------------------------------")); Serial.println(F(" GFRTC LIBRARY TEST PROGRAM ")); Serial.println(F(" https://www.geekfactory.mx ")); Serial.println(F("----------------------------------------------------")); // prepare the RTC class, this also calls Wire.begin() GFRTC.begin(true); // check if we can communicate with RTC if (GFRTC.isPresent()) { Serial.println(F("RTC connected and ready.")); } else { Serial.println(F("Check RTC connections and try again.")); for (;;); } } void loop() { // structure to hold data from RTC struct timelib_tm datetime; // get date and time if (GFRTC.read(datetime)) { // read ok, print data from RTC Serial.print(F("OK, Time = ")); print2digits(datetime.tm_hour); Serial.write(':'); print2digits(datetime.tm_min); Serial.write(':'); print2digits(datetime.tm_sec); Serial.print(F(", Date (D/M/Y) = ")); Serial.print(datetime.tm_mday); Serial.write('/'); Serial.print(datetime.tm_mon); Serial.write('/'); Serial.print(time_tm2y2k(datetime.tm_year)); Serial.println(); } else { // error reading the RTC Serial.println(F("Cannot read RTC.")); } // wait a second before reading again delay(1000); } /** Helper function to print always two digits */ void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); } |
¿Cómo poner a tiempo el DS3231?
Este programa carga la fecha y hora ajustada por el usuario en el momento de compilar.
Es conveniente cargar y ejecutar solamente una vez este programa, ya que cada reset de la placa Arduino causará que se escriba la misma fecha y hora en los registros del RTC.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | /** GeekFactory - "INNOVATING TOGETHER" Distribucion de materiales para el desarrollo e innovacion tecnologica www.geekfactory.mx Basic example for GFRTC, this example shows how to write date/time to RTC using a timelib_tm structure and display it on the serial terminal. The time and date is always set to the same value provided during compilation. */ #include <GFRTC.h> void setup() { // prepare serial interface Serial.begin(115200); while (!Serial); // show message on serial monitor Serial.println(F("----------------------------------------------------")); Serial.println(F(" GFRTC LIBRARY TEST PROGRAM ")); Serial.println(F(" https://www.geekfactory.mx ")); Serial.println(F("----------------------------------------------------")); // prepare the RTC class, this also calls Wire.begin() GFRTC.begin(true); // check if we can communicate with RTC if (GFRTC.isPresent()) { Serial.println(F("RTC connected and ready.")); } else { Serial.println(F("Check RTC connections and try again.")); for (;;); } // structure to hold data from RTC struct timelib_tm datetime; // use the datetime structure to group date time information // SET THE DESIRED TIME / DATE HERE datetime.tm_hour = 12; datetime.tm_min = 0; datetime.tm_sec = 0; datetime.tm_wday = 7; datetime.tm_mday = 20; datetime.tm_mon = 4; datetime.tm_year = timelib_y2k2tm(19); // write data on struct to RTC registers if (GFRTC.write(datetime)) { // write ok, print data sent to RTC Serial.print(F("Set date / time to: ")); print2digits(datetime.tm_hour); Serial.write(':'); print2digits(datetime.tm_min); Serial.write(':'); print2digits(datetime.tm_sec); Serial.print(F(", Date (D/M/Y) = ")); Serial.print(datetime.tm_mday); Serial.write('/'); Serial.print(datetime.tm_mon); Serial.write('/'); Serial.print(timelib_tm2y2k(datetime.tm_year)); Serial.println(); } else { // error reading the RTC Serial.println(F("Cannot write RTC.")); } } void loop() { // do nothing on main loop } /** Helper function to print always two digits */ void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); } |
Leer el sensor de temperatura interno
El DS3231 posee un sensor de temperatura interno que podemos leer a través de la interfaz I2C. Nuestra librería viene preparada para leer este sensor de forma sencilla.
El código a continuación muestra cómo realizar una lectura de temperatura con el DS3231.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /** GeekFactory - "INNOVATING TOGETHER" Distribucion de materiales para el desarrollo e innovacion tecnologica www.geekfactory.mx This example shows how to read the internal temperature sensor on the DS3231. The reading is displayed in a serial monitor window. */ #include <GFRTC.h> void setup() { // prepare serial interface Serial.begin(115200); while (!Serial); // show message on serial monitor Serial.println(F("----------------------------------------------------")); Serial.println(F(" GFRTC LIBRARY TEST PROGRAM ")); Serial.println(F(" https://www.geekfactory.mx ")); Serial.println(F("----------------------------------------------------")); // prepare the GFRTC class, this also calls Wire.begin() GFRTC.begin(true); // check if we can communicate with RTC if (GFRTC.isPresent()) { Serial.println(F("RTC connected and ready.")); } else { Serial.println(F("Check RTC connections and try again.")); for (;;); } } void loop() { // reads temperature to variable int temperature = GFRTC.getTemperature(); // print to serial monitor Serial.print(F("Temperature: ")); Serial.println(temperature); // wait before next reading delay(1000); } |
Trabajar con fecha / hora en formato Unix
La librería GFRTC incorpora métodos para leer y poner a tiempo el reloj en tiempo real utilizando un timestamp Unix.
La capacidad de manejar tiempos en este formato nos permite realizar manipulaciones de intervalos de tiempo con facilidad. También podemos utilizar el protocolo NTP para poner a tiempo el RTC utilizando una conexión a internet.
El siguiente programa muestra como poner a tiempo el DS3231 utilizando un timestamp de Unix. En este caso ajustaremos la fecha / hora para el 01/01/2020 14:10:30 lo que equivale a 1577887830 en tiempo unix.
También podemos obtener un timestamp de Unix utilizando la librería, tal como se muestra en el ejemplo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /** GeekFactory - "INNOVATING TOGETHER" Distribucion de materiales para el desarrollo e innovacion tecnologica www.geekfactory.mx Basic example for GFRTC, this example shows how to read date/time from RTC and display it on the serial terminal. */ #include <GFRTC.h> void setup() { // prepare serial interface Serial.begin(115200); while (!Serial); // show message on serial monitor Serial.println(F("----------------------------------------------------")); Serial.println(F(" GFRTC LIBRARY TEST PROGRAM ")); Serial.println(F(" https://www.geekfactory.mx ")); Serial.println(F("----------------------------------------------------")); // prepare the RTC class, this also calls Wire.begin() GFRTC.begin(true); // check if we can communicate with RTC if (GFRTC.isPresent()) { Serial.println(F("RTC connected and ready.")); } else { Serial.println(F("Check RTC connections and try again.")); for (;;); } // Setting the time using unix timestamp Serial.println(F("Setting time to Wednesday, 01/Jan/20 14:10:30 (1577887830)")); GFRTC.set(1577887830); } void loop() { // read unix time timelib_t ut = GFRTC.get(); // print unix time and then human readable time Serial.print(ut); Serial.print(F(" unix equals ")); readAndDisplayTime(); // wait a second before reading again delay(1000); } /** Prints date and time to serial monitor */ void readAndDisplayTime() { // structure to hold data from RTC struct timelib_tm datetime; // get date and time if (GFRTC.read(datetime)) { // read ok, print data from RTC print2digits(datetime.tm_hour); Serial.write(':'); print2digits(datetime.tm_min); Serial.write(':'); print2digits(datetime.tm_sec); Serial.print(F(", Date (D/M/Y) = ")); Serial.print(datetime.tm_mday); Serial.write('/'); Serial.print(datetime.tm_mon); Serial.write('/'); Serial.print(timelib_tm2y2k(datetime.tm_year)); Serial.println(); } else { // error reading the RTC Serial.println(F("Cannot read RTC.")); } } /** Helper function to print always two digits */ void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); } |
Activando las interrupciones y alarmas
Las alarmas del DS3231 pueden generar interrupciones mediante un pin de salida destinado a este propósito. El siguiente ejemplo muestra como utilizar la funcionalidad de interrupciones y alarmas.
Las alarmas pueden configurarse de varias formas, la alarma 1 puede configurarse de 6 formas distintas:
- E_ALM1_EVERY_SECOND – alarma cada segundo.
- E_ALM1_MATCH_SECONDS – los segundos coinciden (una vez por minuto).
- E_ALM1_MATCH_MINUTES – los minutos y segundos coinciden (una vez por hora).
- E_ALM1_MATCH_HOURS – las horas, los minutos y segundos coinciden (una vez por dia).
- E_ALM1_MATCH_DAY – el día de la semana, las horas, los minutos y segundos coinciden (semanal).
- E_ALM1_MATCH_DATE – el día del mes, las horas, los minutos y segundos coinciden (una vez por mes).
Mientras tanto, la alarma 2 puede configurarse de 5 formas:
- E_ALM2_EVERY_MINUTE – alarma cada minuto.
- E_ALM2_MATCH_MINUTES – los minutos coinciden (una vez por hora).
- E_ALM2_MATCH_HOURS – las horas y los minutos coinciden (una vez por día).
- E_ALM2_MATCH_DAY – el día de la semana, las horas y los minutos coinciden (una vez por semana).
- E_ALM2_MATCH_DATE – el día del mes, las horas y los minutos coinciden (una vez por mes).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /** GeekFactory - "INNOVATING TOGETHER" Distribucion de materiales para el desarrollo e innovacion tecnologica www.geekfactory.mx This example shows how to configure the alarms on the DS3231 and DS3232. The RTC is configured to generate an alarm signal each hour when the minutes register matches with the value configured on the alarm register. */ #include <GFRTC.h> // Flag used to indicate that DS3231 generated an interrupt volatile bool rtcflag = false; void setup() { // prepare serial interface Serial.begin(115200); while (!Serial); // show message on serial monitor Serial.println(F("----------------------------------------------------")); Serial.println(F(" GFRTC LIBRARY TEST PROGRAM ")); Serial.println(F(" https://www.geekfactory.mx ")); Serial.println(F("----------------------------------------------------")); // prepare the GFRTC class, this also calls Wire.begin() GFRTC.begin(true); // check if we can communicate with RTC if (GFRTC.isPresent()) { Serial.println(F("RTC connected and ready.")); } else { Serial.println(F("Check RTC connections and try again.")); for (;;); } // enable interrupt output on INT/SQW pin from DS3231 GFRTC.setIntSqwMode(E_INTERRUPT_OUTPUT); // configure the alarm GFRTC.setAlarm(E_ALM1_MATCH_MINUTES, 0, 15, 0, 1); // enable the interrupts for alarm 1 GFRTC.setAlarmInterrupt(E_ALARM_1, true); // clear the interrupt flag by reading it GFRTC.getAlarmInterruptFlag(E_ALARM_1); // attach interrupt, this calls isrhandler function on every falling edge pinMode(2, INPUT); attachInterrupt(digitalPinToInterrupt(2), isrhandler, FALLING); } void loop() { // check if we received interrupt signal if (rtcflag) { // clear flag and display time rtcflag = false; // check if interrupt is generated by alarm 1 if (GFRTC.getAlarmInterruptFlag(E_ALARM_1)) { Serial.print(F("ALARM1 ")); } // check if interrupt is generated by alarm 2 if (GFRTC.getAlarmInterruptFlag(E_ALARM_2)) { Serial.print(F("ALARM2: ")); } // display time readAndDisplayTime(); } } /** Sets a flag when interrupt signal is received from DS3231 */ void isrhandler() { rtcflag = true; } /** Prints date and time to serial monitor */ void readAndDisplayTime() { // structure to hold data from RTC struct timelib_tm datetime; Serial.print(F("Got interrupt signal, time is = ")); // get date and time if (GFRTC.read(datetime)) { // read ok, print data from RTC print2digits(datetime.tm_hour); Serial.write(':'); print2digits(datetime.tm_min); Serial.write(':'); print2digits(datetime.tm_sec); Serial.print(F(", Date (D/M/Y) = ")); Serial.print(datetime.tm_mday); Serial.write('/'); Serial.print(datetime.tm_mon); Serial.write('/'); Serial.print(timelib_tm2y2k(datetime.tm_year)); Serial.println(); } else { // error reading the RTC Serial.println(F("Cannot read RTC.")); } } /** Helper function to print always two digits */ void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); } |
Activando la salida de señal cuadrada
Además de generar interrupciones por alarmas, podemos utilizar el pin INT/SQR para generar una señal de reloj cuadrada que puede servirnos en otras partes de nuestros circuitos. La frecuencia de esta señal puede configurarse en alguno de los siguientes valores:
- E_SQRWAVE_1_HZ
- E_SQRWAVE_1024_HZ
- E_SQRWAVE_4096_HZ
- E_SQRWAVE_8192_HZ
El código a continuación muestra cómo configurar este pin para generar una frecuencia de 1 Hz y utiliza esa señal para generar una interrupción cada segundo. Dentro de esa interrupción leeremos los registros de fecha y hora, mostrando el resultado en el monitor serial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | /** GeekFactory - "INNOVATING TOGETHER" Distribucion de materiales para el desarrollo e innovacion tecnologica www.geekfactory.mx Example that shows how to configure the DS3231 interrupt / square wave output pin on DS3231 and DS3232 devices. This code configures the INT/SQW pin to output a 1 Hz signal and uses an interrupt to read the date / time from RTC. */ #include <GFRTC.h> // Flag used to indicate that DS3231 generated an interrupt volatile bool rtcflag = false; void setup() { // prepare serial interface Serial.begin(115200); while (!Serial); // show message on serial monitor Serial.println(F("----------------------------------------------------")); Serial.println(F(" GFRTC LIBRARY TEST PROGRAM ")); Serial.println(F(" https://www.geekfactory.mx ")); Serial.println(F("----------------------------------------------------")); // prepare the GFRTC class, this also calls Wire.begin() GFRTC.begin(true); // check if we can communicate with RTC if (GFRTC.isPresent()) { Serial.println(F("RTC connected and ready.")); } else { Serial.println(F("Check RTC connections and try again.")); for (;;); } // enable sqrwave output with 1 Hz frequency if (GFRTC.setIntSqwMode(E_SQRWAVE_1_HZ)) { Serial.println(F("Set square wave output OK")); } // use other values on the gfrtc_intsqw_modes enum to configure different // frequencies or use the pin as interrupt output for alarms, for example: // GFRTC.setIntSqwMode(E_SQRWAVE_1024_HZ); // GFRTC.setIntSqwMode(E_SQRWAVE_4096_HZ); // GFRTC.setIntSqwMode(E_SQRWAVE_8192_HZ); // GFRTC.setIntSqwMode(E_INTERRUPT_OUTPUT); // Note that DS3231M chips can only output 1 Hz frequency, on DS3231 you // can configure the output frequency. // attach interrupt, this calls isrhandler function on every falling edge pinMode(2, INPUT); attachInterrupt(digitalPinToInterrupt(2), isrhandler, FALLING); } void loop() { // check if we received interrupt signal if (rtcflag) { // clear flag and display time rtcflag = false; readAndDisplayTime(); } } /** Sets a flag when interrupt signal is received from DS3231 */ void isrhandler() { rtcflag = true; } /** Prints date and time to serial monitor */ void readAndDisplayTime() { // structure to hold data from RTC struct timelib_tm datetime; Serial.print(F("Got interrupt signal, time is = ")); // get date and time if (GFRTC.read(datetime)) { // read ok, print data from RTC print2digits(datetime.tm_hour); Serial.write(':'); print2digits(datetime.tm_min); Serial.write(':'); print2digits(datetime.tm_sec); Serial.print(F(", Date (D/M/Y) = ")); Serial.print(datetime.tm_mday); Serial.write('/'); Serial.print(datetime.tm_mon); Serial.write('/'); Serial.print(timelib_tm2y2k(datetime.tm_year)); Serial.println(); } else { // error reading the RTC Serial.println(F("Cannot read RTC.")); } } /** Helper function to print always two digits */ void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); } |
Conclusión
Durante este tutorial, hemos aprendido a acceder a la funcionalidad del DS3231 a través de la librería GFRTC.
Mediante los programas vistos en esta página, logramos configurar y también obtener la fecha y hora. También exploramos otras posibilidades que ofrece este circuito integrado como su funcionalidad de generar alarmas y señales de reloj de diversas frecuencias.
Existen algunas funcionalidades avanzadas que no hemos abordado en este tutorial, sin embargo, dentro de la carpeta de ejemplos de la librería podremos encontrar ejemplos para toda la funcionalidad expuesta por la librería.