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:

  1. VCC – Alimentación de 5 volts del módulo.
  2. GND – Tierra o común.
  3. SCL – Señal de reloj del bus I2C.
  4. SDA – Señal de datos del bus I2C.
  5. SQW – Salida de interrupción o señal de reloj.

Conexion arduino con RTC DS3231

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:

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.

/**
   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.

/**
   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.

/**
   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.

/**
   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).
/**
   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.

/**
   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.

shares