Esta es una guía de inicio que muestra cómo conectar el módulo de reloj de tiempo real DS3231 con la placa Raspberry Pi Pico programada con MicroPython: aprenda a configurar y obtener la hora, configurar alarmas y obtener lecturas de temperatura.

El módulo RTC DS3231 es un excelente módulo para un cronometraje preciso, también le permite configurar alarmas, emitir ondas cuadradas con diferentes frecuencias y obtener lecturas de temperatura.
Introducción a los módulos de reloj de tiempo real (RTC)
Los módulos RTC, como el DS3231 y el DS1307, cuentan con su propio reloj en miniatura para controlar la hora. Suelen incluir un soporte para conectar una batería y así seguir funcionando incluso si la Raspberry Pi Pico se reinicia o se queda sin energía.

El DS3231 y el DS1307 son dos de las opciones más populares para usar con microcontroladores. Ambos son compatibles con Raspberry Pi Pico y se comunican mediante el protocolo I2C. El DS3231 es más preciso porque proporciona resultados con compensación de temperatura. Además, también se pueden configurar alarmas externas con el DS3231, lo cual puede resultar muy útil.
Presentamos el módulo RTC DS3231
La siguiente imagen muestra el módulo RTC DS3231. Utiliza un oscilador de cristal con compensación de temperatura (TCXO) de 32 kHz para registrar el tiempo con precisión (es resistente a los cambios de temperatura). Por ello, también permite obtener datos de temperatura.

Además de realizar un seguimiento preciso de la fecha y la hora, también tiene memoria incorporada para almacenar hasta dos alarmas y puede emitir ondas cuadradas a diferentes frecuencias: 1 Hz, 4 kHz, 8 kHz y 32 kHz.
La comunicación con el módulo RTC se realiza mediante el protocolo de comunicación I2C. Normalmente, se encuentra en la dirección 0x68.
Este módulo también incluye una EEPROM 24C32 de 32 bytes que permite almacenar cualquier dato no volátil. Puede comunicarse con esta memoria EEPROM mediante I2C, direccionando la dirección correcta (0x57).
Portapilas DS3231
El DS3231 viene con un portapilas para conectar una pila y mantener una hora precisa. En caso de un corte de energía, puede seguir registrando la hora con precisión y mantener todas las alarmas
Debe
usar una batería LIR2032, que es recargable. No use una CR2032 (no recargable).


Si desea utilizar una batería CR2032, que no es recargable, debe desconectar el circuito de carga de la batería desoldando y quitando la resistencia (etiquetada como R4 en mi módulo) al lado del diodo.

Alarmas del DS3231
El DS3231 puede almacenar hasta dos alarmas:
alarma 1y
alarma 2.Estas alarmas se pueden configurar para que se activen en función de una hora o fecha específica. Cuando se activa una alarma, el pin SQW del módulo emite una señal BAJA
Puedes detectar esta señal con la Raspberry Pi Pico y
activar interrupciones. Esta función es extremadamente útil para tareas periódicas, automatización basada en tiempo y alertas puntuales (ya que puedes borrar una alarma después de que se active).
Dirección I2C del módulo RTC DS3231
Por defecto, la dirección del RTC DS3231 es
0x68y la de la EEPROM conectada al módulo es 0x57. Puede ejecutar un
script de escáner I2C para verificar las direcciones.
Distribución de pines del módulo RTC DS3231
La siguiente tabla describe rápidamente la distribución de pines del módulo RTC DS3231.
| 32 mil | Salida del oscilador de 32 kHz: se puede usar como referencia de reloj |
| SQW | Salida de onda cuadrada/interrupción |
| SCL | Pin SCL para I2C |
| Adventista del Séptimo Día | Pin SDA para I2C |
| VCC | Proporciona alimentación al módulo (3.3 V o 5 V) |
| Tierra | Tierra |
Conexión del módulo RTC DS3231 a la Raspberry Pi Pico

Aquí hay una lista de las piezas necesarias para este tutorial:
- Placa Raspberry Pi Pico
- Módulo RTC DS3231
- CONDUJO
- Resistencia de 220 ohmios
- Cables puente
- Placa de pruebas
Conecte el DS3231 a la Raspberry Pi Pico. Puede usar la siguiente tabla como referencia. Usamos los pines I2C predeterminados.
| Módulo RTC DS3231 | Frambuesa Pico |
| SQW | GPIO 3(o cualquier otro pin digital) |
| SCL | GPIO 5 |
| Adventista del Séptimo Día | GPIO 4 |
| VCC | 3 contra 3 |
| Tierra | Tierra |
Trabajando con el RTC
El uso de un módulo RTC en sus proyectos siempre requiere dos pasos importantes.
- Configuración de la hora actual : puede hacerlo manualmente insertando la hora actual (o una hora diferente deseada) en el código; la hora local del sistema; u obtener la hora de un servidor NTP.
- Retención de la hora: para garantizar que el RTC mantenga la hora correcta, incluso si se corta la alimentación, debe estar conectado a una batería. Los módulos RTC incluyen un soporte para batería, generalmente para una pila de botón.
Módulo RTC MicroPython DS3231
Existen diferentes bibliotecas con distintas funciones que facilitan la comunicación con el módulo RTC DS3231. Usaremos una versión ligeramente modificada del módulo
urtc.pydesarrollado por Adafruit.
Siga los siguientes pasos para instalar el módulo requerido.
Descargue y cargue el urtc.py
- Haga clic aquí para descargar el código urtc.py ;
- Copie el código a un archivo en Thonny IDE;
- Vaya a Archivo > Guardar como… y seleccione Raspberry Pi Pico;
- Guarde el archivo con el nombre urtc.py (no cambie el nombre).
# Forked and adapted from https://github.com/adafruit/Adafruit-uRTC/tree/master
import collections
import time
DateTimeTuple = collections.namedtuple("DateTimeTuple", ["year", "month",
"day", "weekday", "hour", "minute", "second", "millisecond"])
def datetime_tuple(year=None, month=None, day=None, weekday=None, hour=None,
minute=None, second=None, millisecond=None):
return DateTimeTuple(year, month, day, weekday, hour, minute,
second, millisecond)
def _bcd2bin(value):
return (value or 0) - 6 * ((value or 0) >> 4)
def _bin2bcd(value):
return (value or 0) + 6 * ((value or 0) // 10)
def tuple2seconds(datetime):
return time.mktime((datetime.year, datetime.month, datetime.day,
datetime.hour, datetime.minute, datetime.second, datetime.weekday, 0))
def seconds2tuple(seconds):
(year, month, day, hour, minute,
second, weekday, _yday) = time.localtime(seconds)
return DateTimeTuple(year, month, day, weekday, hour, minute, second, 0)
class _BaseRTC:
_SWAP_DAY_WEEKDAY = False
def __init__(self, i2c, address=0x68):
self.i2c = i2c
self.address = addressdef _register(self, register, buffer=None):
if buffer is None:
return self.i2c.readfrom_mem(self.address, register, 1)[0]
self.i2c.writeto_mem(self.address, register, buffer)
def _flag(self, register, mask, value=None):
data = self._register(register)
if value is None:
return bool(data & mask)
if value:
data |= mask
else:
data &= ~mask
self._register(register, bytearray((data,)))
def datetime(self, datetime=None):
if datetime is None:
buffer = self.i2c.readfrom_mem(self.address,
self._DATETIME_REGISTER, 7)
if self._SWAP_DAY_WEEKDAY:
day = buffer[3]
weekday = buffer[4]
else:
day = buffer[4]
weekday = buffer[3]
return datetime_tuple(
year=_bcd2bin(buffer[6]) + 2000,
month=_bcd2bin(buffer[5]),
day=_bcd2bin(day),
weekday=_bcd2bin(weekday),
hour=_bcd2bin(buffer[2]),
minute=_bcd2bin(buffer[1]),
second=_bcd2bin(buffer[0]),
)
datetime = datetime_tuple(*datetime)
buffer = bytearray(7)
buffer[0] = _bin2bcd(datetime.second)
buffer[1] = _bin2bcd(datetime.minute)
buffer[2] = _bin2bcd(datetime.hour)
if self._SWAP_DAY_WEEKDAY:
buffer[4] = _bin2bcd(datetime.weekday)
buffer[3] = _bin2bcd(datetime.day)
else:
buffer[3] = _bin2bcd(datetime.weekday)
buffer[4] = _bin2bcd(datetime.day)
buffer[5] = _bin2bcd(datetime.month)
buffer[6] = _bin2bcd(datetime.year - 2000)
self._register(self._DATETIME_REGISTER, buffer)
class DS1307(_BaseRTC):
_NVRAM_REGISTER = 0x08
_DATETIME_REGISTER = 0x00
_SQUARE_WAVE_REGISTER = 0x07
def stop(self, value=None):
return self._flag(0x00, 0b10000000, value)
def memory(self, address, buffer=None):
if buffer is not None and address + len(buffer) > 56:
raise ValueError("address out of range")
return self._register(self._NVRAM_REGISTER + address, buffer)
class DS3231(_BaseRTC):
_CONTROL_REGISTER = 0x0e
_STATUS_REGISTER = 0x0f
_DATETIME_REGISTER = 0x00
_TEMPERATURE_MSB_REGISTER = 0x11
_TEMPERATURE_LSB_REGISTER = 0x12
_ALARM_REGISTERS = (0x08, 0x0b)
_SQUARE_WAVE_REGISTER = 0x0e
def lost_power(self):
return self._flag(self._STATUS_REGISTER, 0b10000000)
def alarm(self, value=None, alarm=0):
return self._flag(self._STATUS_REGISTER,
0b00000011 & (1 << alarm), value)
def interrupt(self, alarm=0):
return self._flag(self._CONTROL_REGISTER,
0b00000100 | (1 << alarm), 1)
def no_interrupt(self):
return self._flag(self._CONTROL_REGISTER, 0b00000011, 0)
def stop(self, value=None):
return self._flag(self._CONTROL_REGISTER, 0b10000000, value)
def datetime(self, datetime=None):
if datetime is not None:
status = self._register(self._STATUS_REGISTER) & 0b01111111
self._register(self._STATUS_REGISTER, bytearray((status,)))
return super().datetime(datetime)
def alarm_time(self, datetime=None, alarm=0):
if datetime is None:
buffer = self.i2c.readfrom_mem(self.address,
self._ALARM_REGISTERS[alarm], 3)
day = None
weekday = None
second = None
if buffer[2] & 0b10000000:
pass
elif buffer[2] & 0b01000000:
day = _bcd2bin(buffer[2] & 0x3f)
else:
weekday = _bcd2bin(buffer[2] & 0x3f)
minute = (_bcd2bin(buffer[0] & 0x7f)
if not buffer[0] & 0x80 else None)
hour = (_bcd2bin(buffer[1] & 0x7f)
if not buffer[1] & 0x80 else None)
if alarm == 0:
# handle seconds
buffer = self.i2c.readfrom_mem(
self.address, self._ALARM_REGISTERS[alarm] - 1, 1)
second = (_bcd2bin(buffer[0] & 0x7f)
if not buffer[0] & 0x80 else None)
return datetime_tuple(
day=day,
weekday=weekday,
hour=hour,
minute=minute,
second=second,
)
datetime = datetime_tuple(*datetime)
buffer = bytearray(3)
buffer[0] = (_bin2bcd(datetime.minute)
if datetime.minute is not None else 0x80)
buffer[1] = (_bin2bcd(datetime.hour)
if datetime.hour is not None else 0x80)
if datetime.day is not None:
if datetime.weekday is not None:
raise ValueError("can't specify both day and weekday")
buffer[2] = _bin2bcd(datetime.day)
elif datetime.weekday is not None:
buffer[2] = _bin2bcd(datetime.weekday) | 0b01000000
else:
buffer[2] = 0x80
self._register(self._ALARM_REGISTERS[alarm], buffer)
if alarm == 0:
# handle seconds
buffer = bytearray([_bin2bcd(datetime.second)
if datetime.second is not None else 0x80])
self._register(self._ALARM_REGISTERS[alarm] - 1, buffer)
def get_temperature(self):
"""
Reads the temperature from the DS3231's temperature registers.
Returns the temperature as a float in Celsius.
"""
msb = self._register(self._TEMPERATURE_MSB_REGISTER) # 0x11
lsb = self._register(self._TEMPERATURE_LSB_REGISTER) # 0x12
if msb is None or lsb is None:
print("Error: Register read returned None")
return None
temp = msb + ((lsb >> 6) * 0.25)
if msb & 0x80:
temp -= 256
return tempclass PCF8523(_BaseRTC):
_CONTROL1_REGISTER = 0x00
_CONTROL2_REGISTER = 0x01
_CONTROL3_REGISTER = 0x02
_DATETIME_REGISTER = 0x03
_ALARM_REGISTER = 0x0a
_SQUARE_WAVE_REGISTER = 0x0f
_SWAP_DAY_WEEKDAY = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.init()
def init(self):
# Enable battery switchover and low-battery detection.
self._flag(self._CONTROL3_REGISTER, 0b11100000, False)
def reset(self):
self._flag(self._CONTROL1_REGISTER, 0x58, True)
self.init()
def lost_power(self, value=None):
return self._flag(self._CONTROL3_REGISTER, 0b00010000, value)
def stop(self, value=None):
return self._flag(self._CONTROL1_REGISTER, 0b00010000, value)
def battery_low(self):
return self._flag(self._CONTROL3_REGISTER, 0b00000100)
def alarm(self, value=None):
return self._flag(self._CONTROL2_REGISTER, 0b00001000, value)
def datetime(self, datetime=None):
if datetime is not None:
self.lost_power(False) # clear the battery switchover flag
return super().datetime(datetime)
def alarm_time(self, datetime=None):
if datetime is None:
buffer = self.i2c.readfrom_mem(self.address,
self._ALARM_REGISTER, 4)
return datetime_tuple(
weekday=_bcd2bin(buffer[3] &
0x7f) if not buffer[3] & 0x80 else None,
day=_bcd2bin(buffer[2] &
0x7f) if not buffer[2] & 0x80 else None,
hour=_bcd2bin(buffer[1] &
0x7f) if not buffer[1] & 0x80 else None,
minute=_bcd2bin(buffer[0] &
0x7f) if not buffer[0] & 0x80 else None,
)
datetime = datetime_tuple(*datetime)
buffer = bytearray(4)
buffer[0] = (_bin2bcd(datetime.minute)
if datetime.minute is not None else 0x80)
buffer[1] = (_bin2bcd(datetime.hour)
if datetime.hour is not None else 0x80)
buffer[2] = (_bin2bcd(datetime.day)
if datetime.day is not None else 0x80)
buffer[3] = (_bin2bcd(datetime.weekday) | 0b01000000
if datetime.weekday is not None else 0x80)
self._register(self._ALARM_REGISTER, buffer) Con el módulo cargado en la placa, ahora puede usar las funcionalidades de la biblioteca en su código para interactuar con el módulo RTC DS3231
DS3231 RTC con Raspberry Pi Pico: Configurar y obtener la hora

El siguiente código sincroniza la hora RTC con la hora del sistema y obtiene la fecha, hora y temperatura actuales cada segundo.
# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-ds3231-rtc-micropython/
import time
import urtc
from machine import I2C, Pin
days_of_week = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
# Initialize RTC (connected to I2C)
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
rtc = urtc.DS3231(i2c)
# Set the current time using a specified time tuple
# Time tuple: (year, month, day, day of week, hour, minute, seconds, milliseconds)
#initial_time = (2024, 1, 30, 1, 12, 30, 0, 0)
# Or get the local time from the system
initial_time_tuple = time.localtime() # tuple (microPython)
initial_time_seconds = time.mktime(initial_time_tuple) # local time in seconds
# Convert to tuple compatible with the library
initial_time = urtc.seconds2tuple(initial_time_seconds)
# Sync the RTC
rtc.datetime(initial_time)
while True:
current_datetime = rtc.datetime()
temperature = rtc.get_temperature()
# Display time details
print('Current date and time:')
print('Year:', current_datetime.year)
print('Month:', current_datetime.month)
print('Day:', current_datetime.day)
print('Hour:', current_datetime.hour)
print('Minute:', current_datetime.minute)
print('Second:', current_datetime.second)
print('Day of the Week:', days_of_week[current_datetime.weekday])
print(f"Current temperature: {temperature}°C")
# Format the date and time
formatted_datetime = (
f"{days_of_week[current_datetime.weekday]}, "
f"{current_datetime.year:04d}-{current_datetime.month:02d}-{current_datetime.day:02d} "
f"{current_datetime.hour:02d}:{current_datetime.minute:02d}:{current_datetime.second:02d} "
)
print(f"Current date and time: {formatted_datetime}")
print(" n");
time.sleep(1) Cómo funciona el código
Echemos un vistazo rápido a las partes relevantes de este código.
Importar bibliotecas
Primero, necesitas importar el
urtc
módulo que has cargado previamente y que contiene las funciones para interactuar con el RTC. También necesitas importar el
Pin
y
I2C
clases para establecer una comunicación I2C con el módulo
import time
import urtc
from machine import I2C, PinInicializar el módulo RTC
Luego, inicialice la comunicación I2C y cree un objeto llamado
rtc
para referirse a nuestro módulo RTC DS3231.
# Initialize RTC (connected to I2C)
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
rtc = urtc.DS3231(i2c)Sincronizar la hora
Para sincronizar la hora del RTC, debe usar el método
datetime()
en
objeto rtc y pasar como argumento una tupla de tiempo con el siguiente formato:
(year, month, day, day of week, hour, minute, seconds, milliseconds)esta tupla es diferente de la utilizada por el módulo Micropyhton time
Podemos establecer la hora manualmente en una fecha y hora definidas de la siguiente manera:
#initial_time = (2024, 1, 30, 1, 12, 30, 0, 0)O bien, podemos sincronizar el RTC con la hora local del sistema. Sincronizaremos la hora con la hora local del sistema.
Primero, obtenemos la tupla de tiempo de la hora local usando
time.localtime()
.
initial_time_tuple = time.localtime() # tuple (microPython)La tupla devuelta es diferente de la utilizada por el módulo urtc.
Entonces, primero lo convertimos a segundos usando
time.mktime()
.
initial_time_seconds = time.mktime(initial_time_tuple) # local time in secondsY finalmente, lo convertimos en una tupla que sea compatible con la biblioteca usando la función seconds2tuple() de la biblioteca urtc que acepta como argumento el número de segundos desde la época de la hora local
initial_time = urtc.seconds2tuple(initial_time_seconds)Por fin podemos pasar nuestra variable initial_time
que contiene la hora local en una tupla compatible con el módulo
urtc
a la función
datetime()
de la siguiente manera.
# Convert to tuple compatible with the library
initial_time = urtc.seconds2tuple(initial_time_seconds)
# Sync the RTC
rtc.datetime(initial_time)Después de configurar la hora por primera vez, solo es necesario volver a configurarla si el reloj de tiempo real pierde energía (debe tener una batería conectada para evitarlo). Puede usar la función lost_power() en el objeto rtc para comprobar si ha perdido energía. Esta función devuelve Verdadero o Falso según corresponda.
Obtener la hora
Después de estas líneas de código, el módulo RTC se sincroniza con tu hora local y puedes simplemente llamarlo rtc.datetime()para obtener la hora actual del RTC. Esto devuelve un objeto con todos los elementos de hora
Para obtener e imprimir cada elemento de tiempo, podemos hacer lo siguiente:
current_datetime = rtc.datetime()
print('Current date and time:')
print('Year:', current_datetime.year)
print('Month:', current_datetime.month)
print('Day:', current_datetime.day)
print('Hour:', current_datetime.hour)
print('Minute:', current_datetime.minute)
print('Second:', current_datetime.second)
print('Day of the Week:', days_of_week[current_datetime.weekday])También podemos obtener la temperatura actual del módulo llamando al metodo get
_temperature()
en el objeto rtc
temperature = rtc.get_temperature()Los datos de temperatura vienen en grados Celsius
print(f"Current temperature: {temperature}°C")Imprimimos la hora y la temperatura actual cada segundo.
time.sleep(1)Ejecutar el código
Ejecute este código anterior para sincronizar la hora del reloj en tiempo real con la hora local y obtener la hora y la temperatura actuales

A partir de ahora, si el RTC tiene una batería conectada, mantendrá la hora sincronizada con tu hora local. Así que ya no necesitas sincronizarlo y puedes simplemente llamar rtc.datetime() para obtener la hora actual.

DS3231 con Raspberry Pi Pico: Configuración de alarmas
El módulo RTC DS3231 permite configurar hasta dos alarmas: alarma 1 y alarma 2. Al llegar a la hora de la alarma, el módulo cambia el estado del pin SQW de ALTO a BAJO (es activo bajo). Podemos comprobar el cambio en ese pin y realizar una acción específica cuando se dispara la alarma.

Notas importantes sobre las alarmas:
- El RTC le permite guardar hasta dos alarmas;
- sólo puedes tener una alarma activa a la vez;
- después de que se activa una alarma, debes borrar su bandera para evitar que se active y bloquee la Pi Pico;
- Debes desactivar una alarma antes de activar la otra.
DS3231 – Configuración de las alarmas
El siguiente ejemplo muestra cómo configurar dos alarmas. Se enciende un LED cuando se activa la alarma. Para probar este ejemplo, añada un LED con una resistencia de 220 ohmios al circuito. Conecte el LED a GPIO 2.
# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-ds3231-rtc-micropython/
import time
import urtc
from machine import Pin, I2C
# Pin setup for SQW pin and LED
CLOCK_INTERRUPT_PIN = 3 # Adjust to your specific GPIO pin for SQW
LED_PIN = 2 # Adjust to your specific GPIO pin for the LED
# Initialize RTC (connected to I2C)
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
rtc = urtc.DS3231(i2c)
# Setup GPIO for SQW and LED
sqw_pin = Pin(CLOCK_INTERRUPT_PIN, Pin.IN, Pin.PULL_UP)
led_pin = Pin(LED_PIN, Pin.OUT)
led_pin.off()
# Alarm times (year, month, day, weekday, hour, minute, second, millisecond)
alarm1_time = urtc.datetime_tuple(2025, 01, 04, None, 12, 32, 00, 0) # Alarm 1 uses full datetime
alarm2_time = urtc.datetime_tuple(2025, 01, 04, None, 12, 35, 00, 0) # Alarm 2 uses day, hour, minute, weekday
# Print the current time from the RTC
def print_current_time():
now = rtc.datetime()
formatted_time = f"{now.year}-{now.month:02}-{now.day:02}{now.hour:02}:{now.minute:02}:{now.second:02}"
print(f"Current time: {formatted_time}")
#Callback for handling alarm interrupt.
def on_alarm(pin):
print("Interrupt detected.")
if rtc.alarm(alarm=0): # Check if Alarm 1 triggered
print("Alarm 1 triggered.")
rtc.alarm(False, alarm=0) # Clear Alarm 1 flag
led_pin.value(not led_pin.value()) # Toggle LED for Alarm 1
if rtc.alarm(alarm=1): # Check if Alarm 2 triggered
print("Alarm 2 triggered.")
rtc.alarm(False, alarm=1) # Clear Alarm 2 flag
led_pin.value(not led_pin.value()) # Toggle LED for Alarm 2
# Setup alarms on the DS3231
def setup_alarms():
# Clear any existing alarms
rtc.alarm(False, 0)
rtc.alarm(False, 1)
rtc.no_interrupt()
# Set the desired alarm times
rtc.alarm_time(alarm1_time, 0) # Alarm 1
rtc.alarm_time(alarm2_time, 1) # Alarm 2
# Enable interrupts for the alarms
rtc.interrupt(0)
rtc.interrupt(1)
print("Alarms set successfully.")
# Attach the interrupt callback
sqw_pin.irq(trigger=Pin.IRQ_FALLING, handler=on_alarm)
try:
# Sync time to compile time if RTC lost power
if rtc.lost_power():
initial_time_tuple = time.localtime() # tuple (MicroPython)
initial_time_seconds = time.mktime(initial_time_tuple) # local time in seconds
initial_time = urtc.seconds2tuple(initial_time_seconds) # Convert to tuple compatible with the library
rtc.datetime(initial_time) # Sync the RTC
print("RTC initialized. Setting alarms...")
setup_alarms()
while True:
print_current_time()
time.sleep(5)
except KeyboardInterrupt:
print("Exiting program.") ¿Cómo funciona el código?
Echemos un vistazo a las partes relevantes del código para configurar las alarmas.
Definición de los pines
Primero, debe definir el GPIO que está conectado al pin SQW. Este es el pin que cambiará de estado cuando se activen las alarmas. Elegimos conectarlo a GPIO 3, pero puede elegir cualquier otro pin siempre que las conexiones del circuito coincidan
CLOCK_INTERRUPT_PIN = 3 # Adjust to your specific GPIO pin for SQWConfiguramos el pin SQW como una entrada con una resistencia pull-up interna (porque es un pin activo bajo).
sqw_pin = Pin(CLOCK_INTERRUPT_PIN, Pin.IN, Pin.PULL_UP)Horas de alarma
Luego, creamos dos variables de tipo
datetime_tuple
Para guardar los tiempos de alarma en variables de la siguiente manera:
# Alarm times (year, month, day, weekday, hour, minute, second, millisecond)
alarm1_time = urtc.datetime_tuple(2025, 01, 04, None, 12, 32, 00, 0) # Alarm 1 uses full datetime
alarm2_time = urtc.datetime_tuple(2025, 01, 04, None, 12, 35, 00, 0) # Alarm 2 uses day, hour, minute, weekdayAjuste las alarmas a las horas deseadas.
Configuración del pin SQW como interrupción
Luego, configuramos el pin SQW como una interrupción llamando al metodo
irq()
sqw_pin.irq(trigger=Pin.IRQ_FALLING, handler=on_alarm)- manejador: esta es una función que se llamará cuando se detecte una interrupción, en este caso la función on_alarm() .
- disparador: esto define el modo de disparo. Hay 3 condiciones diferentes. En nuestro caso, usaremos IRQ_FALLING para activar la interrupción cada vez que el pin pase de ALTO a BAJO.
Función on_alarm
Esta función
on_alarm
verifica qué alarma se activó y la desactiva para borrar su indicador. Se llamará cuando se dispare una alarma.
def on_alarm(pin):if rtc.alarm(alarm=0): # Check if Alarm 1 triggered
print("Alarm 1 triggered.")
rtc.alarm(False, alarm=0) # Clear Alarm 1 flag
led_pin.value(not led_pin.value()) # Toggle LED for Alarm 1rtc.alarm(False, alarm=0) # Clear Alarm 1 flagLuego, alternamos el estado del LED.
led_pin.value(not led_pin.value()) # Toggle LED for Alarm 1Actuamos de forma similar para la alarma2.
if rtc.alarm(alarm=1): # Check if Alarm 2 triggered
print("Alarm 2 triggered.")
rtc.alarm(False, alarm=1) # Clear Alarm 2 flag
led_pin.value(not led_pin.value()) # Toggle LED for Alarm 2Configuración de las alarmas
Creamos una función llamada setup_alarms()para configurar las alarmas.
Para configurar las alarmas, primero debemos borrar cualquier alarma existente de la siguiente manera
def setup_alarms():
# Clear any existing alarms
rtc.alarm(False, 0)
rtc.alarm(False, 1)
rtc.no_interrupt()Luego, podemos configurar las horas de alarma llamando el método
alarm_time() en
el objeto
rtc
. Pasa como argumentos la hora de la alarma (una variable de tipo
datetime_tuple
) y el número de alarma (
0
=alarm1;
1
=alarm2).
# Set the desired alarm times
rtc.alarm_time(alarm1_time, 0) # Alarm 1
rtc.alarm_time(alarm2_time, 1) # Alarm 2rtc.interrupt(0)
rtc.interrupt(1)try:
# Sync time to compile time if RTC lost power
if rtc.lost_power():
initial_time_tuple = time.localtime() # tuple (MicroPython)
initial_time_seconds = time.mktime(initial_time_tuple) # local time in seconds
initial_time = urtc.seconds2tuple(initial_time_seconds) # Convert to tuple compatible with the library
rtc.datetime(initial_time) # Sync the RTC
print("RTC initialized. Setting alarms...")
setup_alarms()Luego, imprimiremos constantemente la hora actual cada cinco segundos.
while True:
print_current_time()
time.sleep(5)Prueba de las alarmas
Configura las alarmas a una hora cercana para que puedas verlas en acción. Luego, ejecuta el código

La hora actual se imprimirá en la carcasa cada cinco segundos. Cuando se active la alarma, aparecerá un mensaje en la carcasa y el LED se encenderá.

DS3231 – Configuración de alarmas periódicas
Para configurar alarmas periódicas, podemos ajustar la hora de la alarma cada vez que se active. Por ejemplo, imagine que desea activar una alarma cada 10 minutos. Para ello:
- leer la hora actual cuando se activa la alarma;
- configurar una nueva alarma agregando 600 segundos (10 minutos) a la hora actual;
- después de 10 minutos se activará la alarma;
- Repita los pasos anteriores.
Eso es exactamente lo que implementaremos en el siguiente ejemplo.
# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-ds3231-rtc-micropython/
from machine import Pin, I2C
import time
import urtc
# Pin setup for SQW pin and LED
CLOCK_INTERRUPT_PIN = 3 # Adjust to your specific GPIO pin for SQW
LED_PIN = 2 # Adjust to your specific GPIO pin for the LED
# Initialize RTC (connected to I2C)
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
rtc = urtc.DS3231(i2c)
# Setup GPIO for SQW and LED
sqw_pin = Pin(CLOCK_INTERRUPT_PIN, Pin.IN, Pin.PULL_UP)
led_pin = Pin(LED_PIN, Pin.OUT)
led_pin.off()
# Add minutes to a datetime tuple and return a new datetime tuple
def add_minutes_to_time(dt, minutes):
timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, 0, 0))
new_timestamp = timestamp + (minutes * 60)
new_time = time.localtime(new_timestamp)
return urtc.datetime_tuple(new_time[0], new_time[1], new_time[2], None, new_time[3], new_time[4], new_time[5], 0)
# Print the current time from the RTC
def print_current_time():
now = rtc.datetime()
formatted_time = f"{now.year}-{now.month:02}-{now.day:02} {now.hour:02}:{now.minute:02}:{now.second:02}"
print(f"Current time: {formatted_time}")
# Callback for handling alarm interrupt
def on_alarm(pin):
print("Alarm triggered! Toggling the LED.")
led_pin.value(not led_pin.value()) # Toggle the LED
# Clear the alarm flag and schedule the next alarm
if rtc.alarm(alarm=0): # Check if Alarm 0 triggered
print("Clearing Alarm 0 flag.")
rtc.alarm(False, alarm=0) # Clear alarm flag
# Schedule the alarm to repeat 10 minutes from now
now = rtc.datetime()
next_alarm_time = add_minutes_to_time(now, 10) # Add 10 minutes
rtc.alarm_time(next_alarm_time, alarm=0) # Set new alarm
rtc.interrupt(0) # Ensure Alarm 0 interrupt is enabled
print(f"Next alarm set for: {next_alarm_time}")
def setup_alarm():
# Clear any existing alarms
rtc.alarm(False, 0)
rtc.no_interrupt()
# Get the current time and set the first alarm 1 minute from now
now = rtc.datetime()
first_alarm_time = add_minutes_to_time(now, 1) # Set first alarm for 1 minute from now
rtc.alarm_time(first_alarm_time, alarm=0) # Alarm 0
# Enable the interrupt for Alarm 0
rtc.interrupt(0)
print(f"Alarm set for: {first_alarm_time}")
# Attach the interrupt callback
sqw_pin.irq(trigger=Pin.IRQ_FALLING, handler=on_alarm)
try:
# Sync time to compile time if RTC lost power
if rtc.lost_power():
initial_time_tuple = time.localtime() # tuple (MicroPython)
initial_time_seconds = time.mktime(initial_time_tuple) # local time in seconds
initial_time = urtc.seconds2tuple(initial_time_seconds) # Convert to tuple compatible with the library
rtc.datetime(initial_time) # Sync the RTC
print("RTC initialized. Setting up the periodic alarm...")
setup_alarm()
while True:
print_current_time()
time.sleep(5)
except KeyboardInterrupt:
print("Exiting program.")En este ejemplo, creamos una función llamada
add_minutes_to_time()
que añadirá minutos a la hora actual.
# Add minutes to a datetime tuple and return a new datetime tuple
def add_minutes_to_time(dt, minutes):
timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, 0, 0))
new_timestamp = timestamp + (minutes * 60)
new_time = time.localtime(new_timestamp)
return urtc.datetime_tuple(new_time[0], new_time[1], new_time[2], None, new_time[3], new_time[4], new_time[5], 0)Esta función acepta como argumentos una tupla datetime
(como la hora actual) y los minutos que desea agregar a la hora actual para configurar una nueva alarma en el futuro. Esta función devuelve una nueva tupla datetime
con la nueva hora de alarma.
Cuando se activa una alarma, la función on_alarma()
se ejecutará. Primero, alternará el estado del LED integrado.
# Callback for handling alarm interrupt
def on_alarm(pin):
print("Alarm triggered! Toggling the LED.")
led_pin.value(not led_pin.value()) # Toggle the LEDif rtc.alarm(alarm=0): # Check if Alarm 0 triggered
print("Clearing Alarm 0 flag.")
rtc.alarm(False, alarm=0) # Clear alarm flag# Schedule the alarm to repeat 10 minutes from now
now = rtc.datetime()
next_alarm_time = add_minutes_to_time(now, 10) # Add 10 minutes
rtc.alarm_time(next_alarm_time, alarm=0) # Set new alarm
rtc.interrupt(0) # Ensure Alarm 0 interrupt is enabled
print(f"Next alarm set for: {next_alarm_time}")Puede cambiar esto fácilmente ajustándolo al tiempo deseado en la siguiente línea.
next_alarm_time = add_minutes_to_time(now, 10) # Add 10 minutesPrueba de las alarmas periódicas
Configura las alarmas a una hora cercana para que puedas verlas en acción. Luego, ejecuta el código
La primera alarma se activará después de un minuto. Las siguientes alarmas se activarán cada 10 minutos. Cuando se activa una alarma, también se activa el estado del LED.

Terminando
En este tutorial, aprendiste a usar el módulo de reloj en tiempo real DS3231 con la Raspberry Pi Pico programada con MicroPython para obtener la fecha y hora actuales y configurar alarmas. Este módulo puede ser muy útil en proyectos de registro de datos, automatización, tareas periódicas y muchas otras aplicaciones.
Preguntas frecuentes
¿Qué es un módulo RTC y por qué lo necesito?
Es un reloj de tiempo real: un módulo con su propio reloj en miniatura para llevar la hora. Módulos como el DS3231 y el DS1307 incluyen soporte para una batería, así que siguen funcionando aunque la Raspberry Pi Pico se reinicie o se apague.
¿Qué ventajas tiene el DS3231 sobre otros RTC?
Es un módulo de cronometraje preciso que además permite configurar alarmas, emitir ondas cuadradas de distintas frecuencias y obtener lecturas de temperatura. Hace bastante más que solo dar la hora.
¿Se puede leer la temperatura con el DS3231?
Sí. Entre sus funciones está entregar lecturas de temperatura, algo que usa internamente para compensar la deriva del cristal y que también queda disponible para el proyecto.
¿Cómo se conecta el DS3231 a la Raspberry Pi Pico?
Por el bus I2C, igual que la mayoría de los módulos de este tipo, y se programa desde MicroPython. La guía cubre configurar la hora, obtenerla, programar alarmas y leer la temperatura.
Aprende esto en un curso
Si prefieres verlo en vivo y con alguien que resuelva tus dudas, estos cursos presenciales de MCI Educación cubren el tema de este tutorial:
- uPython y Electrónica Creativa con Raspberry Pi Pico — nivel inicial, 15 horas presenciales en Santiago
- Domina la Raspberry Pi: Sistemas y Conectividad — nivel intermedio, 18 horas presenciales en Santiago