3

I've just obtained a DS3231 real time clock (RTC), and added it to my RPi 3b. It seems to work as far as keeping time, but I wanted more.

I'd like to be able to set alarms to trigger the INT/SQW pin on the chip (while retaining the basic timekeeping function I currently have), but I've been unable to find any way to do that with the toolset in RPi. I've installed python-smbus and i2c-tools as part of the RTC configuration. Have I overlooked something? - how should I go about reading & writing the DS3231's internal registers to set up the alarm function on my RPi 3b?

goldilocks
  • 60,325
  • 17
  • 117
  • 234

1 Answers1

1

There are two fundamental choices for working with the DS3231 RTC. One is to write your own code for controlling the DS3231, the other is to use the i2c-rtc overlay included in RPi OS. This answer deals with the latter option.

If the alarm function is your only requirement (beyond basic timekeeping), the i2c-rtc overlay will meet your needs easily. Let's cover the specifics:

Q: How to set an alarm that triggers the #INT/SQW pin on the RTC (DS3231)?

As you've already configured the DS3231, and it's keeping time, you're very close to having a functional alarm. See this recipe if you need some help setting up your RTC on any RPi except the RPi5.

Once your RTC is configured for basic timekeeping, you need only take two additional steps:

1. Add the wakeup-source parameter to the dtoverlay for i2c-rtc in the /boot/config.txt file:

FROM: dtoverlay=i2c-rtc,ds3231

TO: dtoverlay=i2c-rtc,ds3231,wakeup-source

Once you reboot your system, the i2c-rtc overlay is ready to support the alarm function.

2. Write the alarm time to sysfs:

You will write to the file /sys/class/rtc/rtc<X>/wakealarm, where <X> generally corresponds to the ordinal rtc you're using (where rtc0 is the first one used).

Navigating sysfs can be a little "hairy" until you get used to it. Most important is not to let all of the symlinks confuse you. I've included a brief guide to navigating sysfs at the "bottom" of this answer, but we'll forego those details for now to get to the answer.

Assuming your RTC is at rtc0, here's a simple script (or command to be run from the CLI) to set the alarm. For this example, let's assume you want to trigger an alarm 30 minutes from now:

#!/usr/bin/bash
date '+%s' -d '+ 30 minutes' | sudo tee /sys/class/rtc/rtc0/wakealarm

That's it - the alarm is set, and it will "trigger" 30 minutes after the above command is run. IOW, the "trigger" occurs when the RTC clock detects that the current time is equal to the time set in the wakealarm file:

IF current time = time set in /sys/class/rtc/rtc0/wakealarm ==> TRIGGER

What happens when the alarm is "triggered"? From the DS3231 data sheet, we know that when the #INT/SQW pin has a pullup resistor connected, the pin will transition from LOGIC HI to LOGIC LOW when the alarm is triggered. How you use this state change is up to you, but you can find examples here, and here, and in other places.

Here's a schematic showing one possible implementation using a RTC to control power to an RPi in a battery-powered, off-grid application:

Schematic showing RTC used for RPi Power Control

3. OPTIONAL: Clear/reset wakealarm

Depending on your application & requirements, it may be necessary to release the assertion on #INT (pin 3 of the DS3231), and allow it to return to its "HIGH" (pulled-up) state. If so (or if you're just tidy), this is easily accomplished by writing a '0' to wakealarm:

$ echo "0" | sudo tee /sys/class/rtc/rtc0/wakealarm 
...OR...
# printf '%s' "0" > '/sys/class/rtc/rtc0/wakealarm'
...etc,etc...

Other alternatives

In the procedure outlined above, the overlay does all of the heavy lifting. The only code you need to create is an interrupt handler that determines what action to take when the alarm is triggered (and perhaps clear/reset the alarm).


A Brief Guide to Navigating sysfs

sysfs is an ephemeral file system. It resides in RAM - not on your SD card/HDD. It is created by the Linux kernel at boot time to support access to kernel data structures from userland.

To learn which folder in sysfs contains the wakealarm file, we might do this:

$ ls -l /sys/class/rtc

lrwxrwxrwx 1 root root 0 Jul 1 21:24 rtc0 -> ../../devices/platform/soc/3f804000.i2c/i2c-1/1-0068/rtc/rtc0

Note the long symlink to ... /rtc0, and note that there is no rtc1, rtc2, etc. Consequently, wakealarm must be in /sys/class/rtc/rtc0. Therefore, ignore the symlink, and do this:

$ cd /sys/class/rtc/rtc0
$ ls -l

drwxr-xr-x 3 root root 0 Jul 1 21:24 alarmtimer.0.auto -r--r--r-- 1 root root 4096 Jul 1 22:39 date -r--r--r-- 1 root root 4096 Jul 1 22:39 dev lrwxrwxrwx 1 root root 0 Jul 1 22:39 device -> ../../../1-0068 -r--r--r-- 1 root root 4096 Jul 1 21:24 hctosys -rw-r--r-- 1 root root 4096 Jul 1 22:39 max_user_freq -r--r--r-- 1 root root 4096 Jul 1 22:39 name drwxr-xr-x 2 root root 0 Jul 1 22:39 power -r--r--r-- 1 root root 4096 Jul 1 22:39 since_epoch lrwxrwxrwx 1 root root 0 Jul 1 21:24 subsystem -> ../../../../../../../../class/rtc -r--r--r-- 1 root root 4096 Jul 1 22:39 time -rw-r--r-- 1 root root 4096 Jul 1 21:24 uevent -rw-r--r-- 1 root root 4096 Jul 1 22:39 wakealarm

And there it is! ... the file named wakealarm! Note it requires root privileges for write.

Alternatively: You may use find to locate wakealarm:

$ sudo find /sys -name "wakealarm" -type f
/sys/devices/platform/soc/3f804000.i2c/i2c-1/1-0068/rtc/rtc0/wakealarm

Once again, do not let the symlinks confuse you! This is the same file that you see in /sys/class/rtc/rtc0! You can see that using the realpath command:

$ realpath /sys/class/rtc/rtc0
/sys/devices/platform/soc/3f804000.i2c/i2c-1/1-0068/rtc/rtc0

And so you may write directly to the file in realpath, or you may write to the symlinked file at /sys/class/rtc/rtc0/wakealarm:

$ date '+%s' -d '+ 30 minutes' | sudo tee /sys/class/rtc/rtc0/wakealarm

...OR...

$ date '+%s' -d '+ 30 minutes' | sudo tee /sys/devices/platform/soc/3f804000.i2c/i2c-1/1-0068/rtc/rtc0/wakealarm

Both statements accomplish the same thing!

Seamus
  • 23,558
  • 5
  • 42
  • 83