My motivation for adding a RTC boils down to this: I wanted to use my RPi in an off-grid application powered by a small solar cell and a battery. After working up a power budget, I realized there was no way to accomplish this without a RTC. The RPi uses too much power - even in low-power mode - to make it a practical component in a battery-powered system.
You might be wondering: "What does a RTC have to do with the RPi's power consumption?" That's a good question - and there's a good answer: To minimize the RPi's energy consumption (power consumption over time) requires that it be powered off whenever it's not needed. If you've owned a RPi for more than 5 minutes, you're aware that there is no power switch; you may also be aware that the RPi has no in-built RTC (RPi 5 being the sole exception). The only way to remove power from the RPi is to disconnect the USB cable from the RPi's connector - fairly inconvenient for an off-grid application. We can insert a switch (relay) in the power line to remove power from the RPI, but once the switch removes power... how do we restore power so that useful work may be resumed? This is the RTC function: to restore power to the RPi after some time has passed.
So the RTC alone does not, by itself, enable off-grid operation. The "system" must maintain cognizance of its energy burden on its energy sources (e.g. solar cell & battery), and power the system so as to sustain the sources. By keeping accurate time the RTC can maintain a schedule for powering the RPi that ensures it draws zero power when it is not needed, and full power when it is. Other sensors in the system may be employed to monitor (for example) battery voltage, thereby enabling the system to override the RTC schedule when necessary. Note that this recipe deals only with the selection and integration of an appropriate RTC, leaving the design and configuration of these other sensors for another time. Finally, a working, tested example, including hardware schematic, is provided for illustration.
After a brief market survey, I selected a DS3231 real time clock (RTC) module found on eBay. At this point I really knew next-to-nothing, but the DS3231 seemed to be in wide use, it seemed to have greater flexibility and accuracy than many of the alternatives. And a DS3231 module was available on eBay for about $3. It actually worked - at least partly. I subsequently learned that my eBay RTC was made with a counterfeit DS3231 - a word to the wise. My next RTC module was purchased from Adafruit, and that is the one used in this recipe.
This balance of this recipe is divided in two parts: RTC Integration, and RTC Power Control. The first part covers general usage of the RTC including RTC configuration for general timekeeping, and configuration of the I2C interface between the RPi and the RTC module. The second part includes a working example for using the RTC for energy conservation - as in an off-grid application where power consumption must be managed.
This is fairly detailed - I wrote this as I did it. I've included some steps that are peripheral, and some that may seem trivial. If you know what you're doing, feel free to skim through.
-
Power down your RPi before making the necessary wiring connections to the RTC.
-
I am (now) using the Adafruit DS3231 Precision RTC Breakout. Four (4) connections are required initially:
- Connect Vcc and GND on the Breakout to +5v power and Ground on the RPi, respectively.
- Connect SDA and SCL on the RTC Breakout to GPIO2 & GPIO3 (a.k.a. physical pins 3 & 5; a.k.a.
i2c1
), respectively.
-
Apply/connect power to boot the RPi, and then run
sudo raspi-config
from the terminal. In theraspi-config
interface, select3 Interface Options
, then selectP5 I2C
and turn it on. This enables the I2C support in the Linux kernel. Alternatively, and more easily than usingraspi-config
, edit/boot/config.txt
, and remove the comment from the following line:dtparam=i2c_arm=on
. -
Reboot the RPi from the terminal (
sudo reboot
). -
Use
apt
toupdate
andupgrade
the RPi in preparation for installing the new software:
$ sudo apt-get update
...
$ sudo apt-get full-upgrade
...
$
-
Install the
i2c-tools
package:$ sudo apt-get install i2c-tools ... $
-
The
i2c-tools
package is not well documented! There is no system manual fori2c-tools
. Its component software modules do haveman pages
, but these components are not listed or defined! This is a rare, but quite useful document that explains some of the things that can be done with thei2c-tools
collection.Command Description i2cdetect Detect I2C chips connected to the bus i2cdump Examine and read I2C registers on a connected device i2cget Read from I2C/SMBus chip registers on a connected device i2cset Set I2C registers on a connected device with new data or values i2ctransfer Send user defined I2C messages in one transfer to a connected device -
Run
i2cdetect
to verify the wiring connections in Step 2 were done correctly; i.e.0x68
will appear for the following RTC chips: DS1307, PCF8523 or DS3231 (and perhaps others).$ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
-
Before we can set the time on the DS3231 RTC, we must configure the device tree overlay. Edit
/boot/config.txt
by adding the following line, and thenreboot
:dtoverlay=i2c-rtc,ds3231
$ sudo reboot
There are several other parameters that may be specified; see the README documentation for details. We shall see below that a key parameter for using the RTC to control power to the RPi is
wakeup-source
; these details will show one way to use the RTC to apply power to the RPi, causing it to boot from an OFF state. For now, we'll continue the steps required to configure the RTC for general timekeeping. -
Verify the driver is now in control of the RTC:
0x68
is replaced byUU
:$ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
-
NOTE - This has happened to me on more than one occasion: After setting everything up as detailed above, you may get an ERROR:
$ sudo i2cdetect -y 1 Error: Could not open file `/dev/i2c-1' or `/dev/i2c/1': No such file or directory
The solution that has worked each time is to run
sudo raspi-config
, as outlined in Step 3 above. I do not know why, or what causes this, nor have I seen it mentioned anywhere else. -
Disable the
fake-hwclock
service running undersystemd
: (N.B. This is no longer required for more current systems)$ sudo systemctl mask fake-hwclock.service
-
Edit & comment out the following 3 lines in
/lib/udev/hwclock-set
: (N.B. This is no longer required for more current systems)#if [ -e /run/systemd/system ] ; then #exit 0 #fi
-
With access to a reliable & accurate clock, check the time on your RPi from the terminal:
$ date +"%Y-%m-%d %T.%6N"; sudo hwclock -r
The two times should be very "close".
-
If your RPi has network connectivity, the
hwclock
time may have already been adjusted by the time you check it. If not, you may manually update the hwclock as follows:$ sudo hwclock -w
-
Check the status of the RTC:
$ timedatectl
# you may see something similar to the following:
Local time: Fri 2023-08-25 22:21:59 UTC
Universal time: Fri 2023-08-25 22:21:59 UTC
RTC time: Fri 2023-08-25 22:22:00
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
-
That concludes the basic configuration of the realtime clock. It will work silently for the most part. If you wish to verify this, disconnect the RPi from the network, or disable the RPi's timekeeping daemon.
-
OPTIONAL: Move your RTC from I2C channel 1 to I2C channel 0 - here's how. The following section on RTC usage for Power Control assumes that I2C0 is being used, but if you prefer/need to use I2C1 you may alter the next step instead.
As mentioned previously (in Step 9 above), one of the key parameters of the i2c-rtc
overlay when the RTC is being used as a wake-up source is the wakeup-source
parameter. Adding the wakeup-source
parameter has the effect of adding a file to the sysfs
: /sys/class/rtc/rtc0/wakealarm
. Writing to this file allows one to specify the time at which the RTC's ALARM
(at pin SQW/#INT
) is triggered, and this pin may be used to drive an external hardware switch which will apply power to the RPi.
This procedure is best illustrated by an example. Let's first take a look at a hardware schematic shown below for power control; then we'll discuss the configuration and software steps. The RPi's initial state is powered OFF; the schematic illustrates circuitry that will apply power, causing the RPi to boot, when the DS3231 RTC is "triggered" at the time specified in software. In other words, this circuitry acts as an "alarm clock" to "wake" the RPi.
The salient feature of this schematic is the SQW/#INT
pin on the DS3231 RTC. We use software to trigger the "WAKE UP ALARM" on this pin by writing a time to the file /sys/class/rtc/rtc0/wakealarm
. This ALARM/INTERRUPT is actually a HIGH-to-LOW state change on the SQW/#INT
pin. This state change causes power to be connected to the RPi via the K1 relay. The one-shot (U2) and transistor (Q1) are simply used for conditioning the SQW/#INT
alarm/interrupt signal. Note also that the relay at K1 is a "latching relay", meaning that once its state has been set (contacts OPEN or CLOSED), it will remain in this state even if power is removed from the circuit.
We now cover the software and configuration settings used in this example. Again, please note that we use the I2C0 channel (instead of I2C1) for this example. I2C0 was chosen for this example as an expedient to accomodate another experiment; any I2C channel should work. See this recipe for details on configuring I2C0. The following should be added to /boot/config.txt
:
# 3231 RTC; SDA on GPIO 0 @ pin 27; SCL on GPIO 1 @ pin 28; pullups: 1.8K to 3V3
dtparam=i2c_vc=on
dtoverlay=i2c-rtc,ds3231,i2c0,wakeup-source
# gpio-poweroff used to automate removal of power using GPIO 26/see schematic
dtoverlay=gpio-poweroff
The example here is a simple schedule for powering the RPi: The RPi will alternate between 10 minutes of "POWER ON" time, and 10 minutes of "POWER OFF" time; i.e. a 20 minute cycle. This is arranged with 2 entries in the root crontab
, and a small bash
script:
In the root crontab
:
# shutdown & power off schedule
*/20 * * * * /home/pi/setalarm.sh >> /home/pi/setalarm.log 2>&1
# clear the current alarm @reboot
@reboot sleep 10; /bin/echo "0" | tee /sys/class/rtc/rtc0/wakealarm; /bin/echo "boot time: $(/bin/date '+%D %X' -d '- 10 seconds') set wakealarm to 0" >> home/pi/setalarm.log 2>&1
Create the setalarm.sh
script:
#!/usr/bin/env bash
# clear the current alarm
echo "0" | tee /sys/class/rtc/rtc0/wakealarm
echo "THE TIME IS: $(date '+%D %X')"
# calculate the next wakeup and then halt:
/bin/date '+%s' -d '+ 10 minutes' | tee /sys/class/rtc/rtc0/wakealarm
/bin/sleep 5
/sbin/halt
After making these changes, issue a reboot
to update the changes to /boot/config.txt
, and your RPi should begin power-cycling itself at 10 minute intervals.
- Raspberry Pi Tutorial Series: I2C - A very worthwhile read; stuff that's hard to find!
- The Rust community’s crate registry brings us their rpi_embedded i2c module.
- I2C Serial Communication Bus in Raspberry pi - includes some examples using
i2ctools
. - Creating Multiple I2C Ports on a Raspberry PI - an article from Medium - beware the paywall.
- How to Work with I2C Communication in Raspberry Pi - example using i2c as Rpi-to-Arduino interface.
- Is there a package or library for controlling the DS3231 on the Raspberry Pi?
- RPi Python Programming 25 – Synchronous serial communication in Raspberry Pi using I2C protocol
- Using the second (I2C 0) port on a Raspberry Pi 3B - a YouTube "how-to" for the RPi 3B.
- The
raspi-gpio
tool GitHub repo - not necessarily useful for I2C, but for GPIO; incl Pi4 support? - Enabling and checking I2C on the Raspberry Pi using the command line for your own scripts; Useful!