Interfaz del módulo RTC DS3231 de precisión con Arduino
Cuando se trata de proyectos de electrónica, el seguimiento preciso del tiempo puede ser crucial. Ya sea que estés diseñando un reloj digital, registrando datos de sensores con marcas de tiempo o automatizando tareas en momentos específicos del día, es esencial contar con un mecanismo confiable para el rastreo del tiempo. Aquí es donde el módulo de reloj en tiempo real (RTC) DS3231 se convierte en una herramienta invaluable.
El DS3231 es reconocido por su precisión y fiabilidad. Este módulo se presenta como la opción ideal en aquellos proyectos que no pueden permitirse perder la noción del tiempo. En este artículo, exploraremos en profundidad cómo funciona el módulo DS3231, cómo conectarlo a un Arduino, y cómo establecer y leer la fecha, hora y temperatura. También examinaremos el uso de su EEPROM incorporada para almacenar datos adicionales.
Visión general del hardware
Chip RTC DS3231
El corazón del módulo DS3231 es un chip RTC de bajo costo y alta precisión, desarrollado por Maxim Integrated. Este chip está diseñado para comunicarse mediante el protocolo I2C, lo que lo hace compatible con múltiples microcontroladores.
El DS3231 tiene la capacidad de llevar un registro de segundos, minutos, horas, días, fechas, meses y años. Su diseño inteligente le permite reconocer automáticamente los meses que tienen menos de 31 días y ajustarse en consecuencia. Además, maneja correctamente los años bisiestos, aunque solo hasta el año 2100.
Este chip permite configurar la visualización de la hora en formato de 12 horas (con indicadores AM/PM) o en formato de 24 horas. También incluye dos alarmas programables que pueden activarse a horas específicas durante el día.
Entre sus características más destacadas, el chip posee un pin especial llamado INT / SQW, que puede generar una señal de interrupción cuando suena una alarma o emitir una señal de onda cuadrada a frecuencias de 1Hz, 4kHz, 8kHz o 32kHz. Además, cuenta con un pin de 32K que proporciona una señal de temporización extremadamente estable y precisa.
Oscilador de cristal compensado por temperatura (TCXO)
A diferencia de otros módulos RTC, como el DS1307, que dependen de un oscilador de cristal externo de 32 kHz, el DS3231 incorpora un oscilador de cristal compensado por temperatura (TCXO) que es altamente resistente a las variaciones de temperatura. Este oscilador garantiza que el reloj mantenga su precisión a lo largo del tiempo.
El TCXO se compone de tres partes clave que trabajan en conjunto: un sensor de temperatura, un oscilador de cristal de 32 kHz y lógica de control. Si la temperatura provoca un cambio en la frecuencia del cristal, el chip ajusta automáticamente el reloj añadiendo o eliminando ticks para mantenerlo en sincronía.
Gracias a esta tecnología, el DS3231 puede mantener una precisión de aproximadamente ±2 minutos por año, lo que lo convierte en uno de los módulos RTC más fiables disponibles actualmente.
Diferencias entre DS3231 y DS1307
La principal diferencia entre los módulos DS3231 y DS1307 radica en su precisión en la medición del tiempo. El DS1307 depende de un cristal externo de 32 kHz, cuya frecuencia puede variar con la temperatura, resultando en un desvío de hasta cinco minutos por mes. En contraste, el DS3231, con su oscilador TCXO, solo presenta un desvío de aproximadamente ±2 minutos por año.
Esto no indica que el DS1307 sea una opción negativa; sigue siendo un RTC confiable para proyectos básicos y a menudo es más económico. Sin embargo, si tu proyecto requiere una medición del tiempo más precisa, el DS3231 es la opción más adecuada.
Respaldo de batería
El chip DS3231 incluye una función de respaldo por batería que garantiza que el reloj siga funcionando con precisión incluso cuando se desconecta la alimentación principal. En la parte posterior del módulo, se encuentra un soporte diseñado para una batería de litio de 3V.
El chip lleva un circuito de detección de energía inteligente que supervisa constantemente la corriente principal. Si detecta que se ha perdido la energía principal, cambia automáticamente al respaldo de batería, manteniendo así la precisión del reloj.
Advertencia: Algunos módulos DS3231 vienen con un resistor de 200Ω soldado junto a un diodo 1N4148, formando un circuito de carga simple para baterías recargables LIR2032. Si tu módulo tiene una batería no recargable CR2032, es esencial quitar el resistor, ya que intentar cargar una batería no recargable puede ser peligroso, causando daños o explosiones.
EEPROM 24C32 incorporada
Además del chip DS3231, el módulo incluye un chip EEPROM 24C32. La EEPROM (Memoria Electrónicamente Borrable y Programable) es útil para almacenar datos, como registros o configuraciones, que deben conservarse incluso cuando se apaga la energía.
La 24C32 EEPROM puede almacenar 32 kilobits de datos y está diseñada para soportar hasta 1,000,000 de ciclos de escritura, lo que permite guardar información repetidamente antes de que se desgaste.
Este EEPROM también utiliza el protocolo de comunicación I2C, al igual que el chip DS3231, y ambos comparten el mismo bus I2C. Si utilizas otros dispositivos en el mismo bus, puede que necesites cambiar la dirección de la EEPROM para evitar conflictos. Para este propósito, el módulo cuenta con tres saltos de soldadura en la parte posterior etiquetados como A0, A1 y A2.
Cambiando la configuración de estos jumpers, puedes modificar la dirección de la EEPROM. De acuerdo con la hoja de datos de la 24C32, los tres bits de dirección están ubicados al final del registro de dirección I2C de 7 bits, justo antes del bit de lectura/escritura. Esto permite crear hasta ocho combinaciones diferentes de direcciones (2^3 = 8).
Interfaz I2C
El módulo DS3231 emplea el protocolo I2C para comunicarse con microcontroladores como Arduino o Raspberry Pi. Cuando conectas este módulo a tu proyecto, utiliza dos direcciones I2C diferentes:
- El chip RTC DS3231 tiene una dirección I2C fija de 0x68.
- El chip EEPROM tiene una dirección I2C predeterminada de 0x57, que puedes cambiar entre 0x50 y 0x57 usando los jumpers de soldadura.
Las señales I2C, SDA y SCL, junto con las conexiones de energía y tierra, están dispuestas a ambos lados de la placa. Esto facilita la conexión del módulo o incluso la cadena de otros dispositivos I2C.
Especificaciones técnicas
A continuación se presentan las especificaciones del módulo DS3231 y de la EEPROM 24C32:
| Característica | Descripción |
|---|---|
| Rango de voltaje de alimentación | 3.3V a 5V |
| Precisión | ±2 minutos por año |
| Dirección I2C del DS3231 | 0x68 |
| Dirección I2C de la EEPROM | 0x57 (cambiable) |
| Capacidad de la EEPROM | 32 kilobits |
Pinout del módulo DS3231 RTC
El módulo DS3231 RTC cuenta con un total de 6 pines. La asignación de pines es la siguiente:
- 32K: proporciona una señal de onda cuadrada de 32.768 kHz.
- INT/SQW: pin de salida de interrupción/onda cuadrada que se puede configurar.
- SCL: pin de reloj serial para la comunicación I2C.
- SDA: pin de datos serial para la comunicación I2C.
- VCC: pin de alimentación que puede conectarse a 3.3V o 5V.
- GND: pin de tierra.
Conexión del módulo DS3231 RTC a un Arduino
La conexión del módulo DS3231 a un Arduino es un proceso sencillo que requiere solo unos pocos pasos. Primero, conecta el pin VCC del módulo a la salida de 5V del Arduino y el pin GND a tierra.
Luego, conecta los pines utilizados para la comunicación I2C. Es importante tener en cuenta que diferentes placas de Arduino pueden tener diferentes pines I2C. En las placas Arduino con diseño R3, los pines SDA y SCL se encuentran cerca del pin AREF, aunque internamente son los mismos que los pines A4 (SDA) y A5 (SCL).
A continuación, se muestra una tabla rápida de las conexiones de pines:
| Pines del módulo DS3231 | Conexiones de Arduino |
|---|---|
| VCC | 5V |
| GND | GND |
| SDA | A4 |
| SCL | A5 |
Instalación de la biblioteca
Para utilizar el módulo DS3231 en nuestros proyectos, emplearemos una biblioteca especial llamada uRTCLib. Esta biblioteca simplifica la lectura de datos de tiempo desde el RTC.
A pesar de su simplicidad, uRTCLib es muy potente. A diferencia de muchas otras bibliotecas RTC, soporta alarmas de tiempo de día y permite controlar la salida de onda cuadrada.
Para instalar la biblioteca, sigue estos pasos:
- Abre tu programa Arduino IDE y haz clic en el icono de Administrador de bibliotecas en la barra lateral izquierda.
- Escribe “urtclib” en el cuadro de búsqueda para filtrar los resultados.
- Busca la biblioteca uRTCLib de Naguissa.
- Haz clic en el botón Instalar para añadirlo a tu Arduino IDE.
Más adelante en este tutorial, también mostraremos cómo leer y escribir en el chip EEPROM 24C32 incorporado. Si estás interesado en probar eso, necesitarás instalar otra biblioteca llamada uEEPROMLib.
Para instalar esta biblioteca, busca “uEEPROMLib” de la misma manera en el Administrador de bibliotecas y haz clic en Instalar.
Código Arduino – Lectura de fecha, hora y temperatura
En este ejemplo, utilizaremos un simple sketch de Arduino para establecer y leer la fecha, la hora y la temperatura del módulo RTC DS3231.
#include "Arduino.h"
#include "uRTCLib.h"
// uRTCLib rtc;
uRTCLib rtc(0x68);
char daysOfTheWeek[7][12] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
void setup() {
Serial.begin(9600);
URTCLIB_WIRE.begin();
// Comentar la línea siguiente una vez que hayas establecido la fecha y la hora.
// Esta línea establece el RTC con una fecha y hora explícitas
// por ejemplo, para establecer el 14 de abril de 2025 a las 12:56, llamarías:
rtc.set(0, 56, 12, 2, 14, 4, 25);
// rtc.set(segundo, minuto, hora, día de la semana, día del mes, mes, año)
// establecer día de la semana (1=domingo, 7=sábado)
}
void loop() {
rtc.refresh();
Serial.print("Fecha y hora actuales: ");
Serial.print(rtc.year());
Serial.print('/');
Serial.print(rtc.month());
Serial.print('/');
Serial.print(rtc.day());
Serial.print(" (");
Serial.print(daysOfTheWeek[rtc.dayOfWeek() - 1]);
Serial.print(") ");
Serial.print(rtc.hour());
Serial.print(':');
Serial.print(rtc.minute());
Serial.print(':');
Serial.println(rtc.second());
Serial.print("Temperatura: ");
Serial.print(rtc.temp() / 100);
Serial.println("°C");
Serial.println();
delay(1000);
}Una vez que subas el código a tu Arduino, abre el monitor serial y establece la tasa de baudios a 9600 bps. Verás la fecha y hora actuales en un formato ordenado, seguido de la lectura de temperatura.
Explicación del código:
Comenzamos incluyendo dos bibliotecas importantes: Arduino.h y uRTCLib.h, que permiten que el Arduino se comunique con el chip DS3231.
#include "Arduino.h"
#include "uRTCLib.h"Luego, creamos un objeto RTC utilizando la biblioteca uRTCLib y definimos un arreglo llamado daysOfTheWeek, que contiene los nombres de los días de la semana.
// uRTCLib rtc;
uRTCLib rtc(0x68);
char daysOfTheWeek[7][12] = { "domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado" };Dentro de la función setup(), abrimos el Monitor Serial para poder imprimir las lecturas de fecha, hora y temperatura en la pantalla. Luego, usamos la función URTCLIB_WIRE.begin() para iniciar la comunicación I2C.
Posteriormente, establecemos la fecha y la hora manualmente utilizando la función rtc.set(). Solo necesitas ejecutar esta línea una vez para establecer la fecha y hora correctas. Después de eso, deberías comentarla para evitar que se restablezca cada vez que subas el código.
Por ejemplo, la línea rtc.set(0, 56, 12, 2, 14, 4, 25); establece la hora a las 12:56 PM del lunes 14 de abril de 2025. Los valores representan: segundos, minutos, horas, día de la semana (1 = domingo), día del mes, mes y los últimos dos dígitos del año.
rtc.set(0, 56, 12, 2, 14, 4, 25);En la función loop(), llamamos a la función rtc.refresh() para actualizar los valores de fecha y hora desde el chip.
A continuación, imprimimos el año, mes y día actuales en el Monitor Serial. Utilizamos el valor de dayOfWeek() como índice para imprimir el nombre del día desde nuestro arreglo daysOfTheWeek.
Serial.print("Fecha y hora actuales: ");
Serial.print(rtc.year());
Serial.print('/');
Serial.print(rtc.month());
Serial.print('/');
Serial.print(rtc.day());
Serial.print(" (");
Serial.print(daysOfTheWeek[rtc.dayOfWeek() - 1]);
Serial.print(") ");Después, imprimimos la hora, los minutos y los segundos para mostrar la hora actual.
Serial.print(rtc.hour());
Serial.print(':');
Serial.print(rtc.minute());
Serial.print(':');
Serial.println(rtc.second());A continuación, leemos el sensor de temperatura incorporado utilizando la función rtc.temp() e imprimimos la temperatura en grados Celsius.
Serial.print("Temperatura: ");
Serial.print(rtc.temp() / 100);
Serial.println("°C");Al final del ciclo, agregamos un retraso de 1 segundo para que los datos se actualicen cada segundo.
Código Arduino – Lectura y escritura en la EEPROM 24C32
Como se mencionó anteriormente, el módulo DS3231 RTC cuenta con un chip EEPROM 24C32 que proporciona 32 kilobits de memoria. Esto es suficiente para almacenar pequeños segmentos de datos, como configuraciones, contraseñas, registros de sensores o cualquier otra cosa que desees mantener.
En este ejemplo, escribiremos diferentes tipos de datos en la EEPROM y luego los leeremos para asegurarnos de que todo funcionó correctamente. Intentaremos escribir un entero, un flotante, un solo carácter y una cadena de texto. Posteriormente, podrás expandir esta técnica para almacenar datos más útiles conforme lo requiera tu proyecto.
#include "Arduino.h"
#include "Wire.h"
#include "uEEPROMLib.h"
// uEEPROMLib eeprom;
uEEPROMLib eeprom(0x57);
void setup() {
Serial.begin(9600);
Wire.begin();
int inttmp = 32123;
float floattmp = 3.1416;
char chartmp = 'A';
char c_string[] = "lastminuteengineers.com"; // 23 caracteres
int string_length = strlen(c_string);
Serial.println("Escribiendo en la memoria...");
// Escribe un entero
if (!eeprom.eeprom_write(0, inttmp)) {
Serial.println("No se pudo almacenar el entero.");
} else {
Serial.println("El entero fue almacenado correctamente.");
}
// Escribe un flotante
if (!eeprom.eeprom_write(4, floattmp)) {
Serial.println("No se pudo almacenar el flotante.");
} else {
Serial.println("El flotante fue almacenado correctamente.");
}
// Escribe un carácter en la dirección
if (!eeprom.eeprom_write(8, chartmp)) {
Serial.println("No se pudo almacenar el carácter.");
} else {
Serial.println("El carácter fue almacenado correctamente.");
}
// Escribe una cadena larga de caracteres desde la posición 33, que no está alineada a las páginas de 32 bytes de la EEPROM
if (!eeprom.eeprom_write(33, (byte *) c_string, strlen(c_string))) {
Serial.println("No se pudo almacenar la cadena.");
} else {
Serial.println("La cadena fue almacenada correctamente.");
}
Serial.println("");
Serial.println("Leyendo la memoria...");
Serial.print("entero: ");
eeprom.eeprom_read(0, &inttmp);
Serial.println(inttmp);
Serial.print("flotante: ");
eeprom.eeprom_read(4, &floattmp);
Serial.println((float) floattmp);
Serial.print("carácter: ");
eeprom.eeprom_read(8, &chartmp);
Serial.println(chartmp);
Serial.print("cadena: ");
eeprom.eeprom_read(33, (byte *) c_string, string_length);
Serial.println(c_string);
Serial.println();
}
void loop() {
}Una vez que subas el código a tu Arduino, abre el monitor serial y establece la tasa de baudios a 9600 bps. La salida en el Monitor Serial mostrará los valores que escribimos, demostrando que la EEPROM almacena datos de manera segura.
Explicación del código EEPROM:
Comenzamos incluyendo las bibliotecas Arduino.h, Wire.h y uEEPROMLib.h. Estas bibliotecas permiten que el Arduino se comunique con la EEPROM a través de I2C.
#include "Arduino.h"
#include "Wire.h"
#include "uEEPROMLib.h"A continuación, creamos un objeto EEPROM utilizando la dirección I2C predeterminada 0x57.
Dentro de la función setup(), inicializamos el Monitor Serial para poder visualizar nuestros resultados y comenzamos la conexión I2C con Wire.begin().
Definimos cuatro variables: una para un entero, una para un flotante, una para un carácter y una cadena de estilo C (que es solo un arreglo de caracteres). También obtenemos la longitud de la cadena para saber cuántos caracteres estamos almacenando.
int inttmp = 32123;
float floattmp = 3.1416;
char chartmp = 'A';
char c_string[] = "lastminuteengineers.com"; //23 caracteres
int string_length = strlen(c_string);Para escribir datos en la EEPROM, utilizamos la función eeprom_write(). Proporcionamos una dirección de memoria y el valor que deseamos almacenar. Cada vez que escribimos, verificamos si fue exitoso e imprimimos un mensaje de confirmación.
Almacenamos el entero en la dirección 0, el flotante en la dirección 4, el carácter en la dirección 8 y la cadena comenzando en la dirección 33. Comenzar la cadena en una dirección más alta evita que se superponga con otros datos almacenados en ubicaciones de memoria inferiores.
// Escribe un entero
if (!eeprom.eeprom_write(0, inttmp)) {
Serial.println("No se pudo almacenar el entero.");
} else {
Serial.println("El entero fue almacenado correctamente.");
}
// Escribe un flotante
if (!eeprom.eeprom_write(4, floattmp)) {
Serial.println("No se pudo almacenar el flotante.");
} else {
Serial.println("El flotante fue almacenado correctamente.");
}
// Escribe un carácter en la dirección
if (!eeprom.eeprom_write(8, chartmp)) {
Serial.println("No se pudo almacenar el carácter.");
} else {
Serial.println("El carácter fue almacenado correctamente.");
}
// Escribe una cadena larga de caracteres desde la posición 33, que no está alineada a las páginas de 32 bytes de la EEPROM
if (!eeprom.eeprom_write(33, (byte *)c_string, strlen(c_string))) {
Serial.println("No se pudo almacenar la cadena.");
} else {
Serial.println("La cadena fue almacenada correctamente.");
}Después de escribir, pasamos a leer los datos de nuevo utilizando la función eeprom_read(). Imprimimos los valores uno por uno para verificar que todo se haya guardado correctamente.
Serial.print("entero: ");
eeprom.eeprom_read(0, &inttmp);
Serial.println(inttmp);
Serial.print("flotante: ");
eeprom.eeprom_read(4, &floattmp);
Serial.println((float)floattmp);
Serial.print("carácter: ");
eeprom.eeprom_read(8, &chartmp);
Serial.println(chartmp);
Serial.print("cadena: ");
eeprom.eeprom_read(33, (byte *)c_string, string_length);
Serial.println(c_string);Nota: Al escribir en la EEPROM, es importante entender cuánto espacio ocupan los diferentes tipos de datos:
Por ejemplo, si almacenas un entero en la dirección 0, no debes almacenar nada más en la dirección 1, ya que los enteros utilizan dos bytes. En su lugar, almacenarías el siguiente valor en la dirección 2 o superior. Así, para almacenar múltiples enteros, utilizarías direcciones de memoria como 0, 2, 4, 6, etc.
Esto ayuda a prevenir la superposición de datos o la corrupción de los mismos.
Deja una respuesta

Estos temas te pueden interesar