Timed bootup of the Rasp Pi with a real-time clock

Continuous Operation

Achieving continuous operation requires a bit more work to allow automation of activities otherwise performed manually. For this purpose, you need to deactivate the entry blacklisti2c-bcm2708 in file /etc/modprobe.d/raspi-blacklist.conf. Then, you either edit the /etc/modprobe.d/raspi.conf file and add the line options i2c-bcm2708baudrate=400000 or add the line i2c-dev in the /etc/modules file.

You can clean up the startup scripts with the following commands:

sudo update-rc.d fake-hwclock remove

and

sudo update-rc.d hwclock remove

Then, in the directory /etc/init.d, apply the patch from Listing 5 by adding the following command to the relevant script:

$ patch < hwclock.sh.patch

Listing 5

hwclock.sh.patch

--- hwclock.sh.original    2012-12-22 06:57:47.000000000 +0100
+++ hwclock.sh  2013-11-03 19:27:50.653988109 +0100
@@ -21,10 +21,10 @@
 ### BEGIN INIT INFO
 # Provides:          hwclock
-# Required-Start:    mountdevsubfs
+# Required-Start:    kmod
 # Required-Stop:     $local_fs
 # Default-Start:     S
-# X-Start-Before:    checkroot
+# X-Start-Before:
 # Default-Stop:      0 6
 ### END INIT INFO
@@ -33,6 +33,7 @@
 HWCLOCKACCESS=yes
 HWCLOCKPARS=
 HCTOSYS_DEVICE=rtc0
+I2CBUS=0
# You only want to use the system timezone or you'll get
 # potential inconsistency at startup.
@@ -43,6 +44,7 @@
     [ ! -x /sbin/hwclock ] && return 0
     [ ! -r /etc/default/rcS ] || . /etc/default/rcS
     [ ! -r /etc/default/hwclock ] || . /etc/default/hwclock
+    [ ! -r /etc/default/rtc ] || . /etc/default/rtc
     . /lib/lsb/init-functions
     verbose_log_action_msg() { [ "$VERBOSE" = no ] || log_action_msg "$@"; }
@@ -61,8 +63,8 @@
                printf "0.0 0 0.0\n0\nUTC" > /etc/adjtime
            fi
-           if [ -d /run/udev ] || [ -d /dev/.udev ]; then
-                return 0
+           if [ ! -c /dev/HCTOSYS_DEVICE ]; then
+               echo mcp7941x 0x6f > /sys/bus/i2c/devices/i2c-$I2CBUS/new_device
            fi
            if [ "$HWCLOCKACCESS" != no ]; then

You also should add the new start script from Listing 6. Both start scripts read the number of the I2C bus from the file in Listing 7.

Listing 6

New Start Script

#!/bin/sh
### BEGIN INIT INFO
# Provides:          raspi-rtc-power
# Required-Start:    kmod
# Required-Stop:     halt
# X-Stop-After:      networking
# Default-Start:     S
# Default-Stop:      0
# Short-Description: on/off Raspi-RTC-Power
# Description:       switching on/off power via RTC chip on Rasp Pi extension board
### END INIT INFO
set -e
. /lib/lsb/init-functions
ALARM=0;
I2CBUS=0;
if [ -r /etc/default/rtc ]; then
  . /etc/default/rtc
fi
if [ -r /etc/wakealarm ]; then
  ALARM=$(cat /etc/wakealarm)
fi
case "${1:-}" in
  start)
    log_action_begin_msg "Switching on RasPi-RTC-Power "
    modprobe i2c-dev
    i2cset -f -y $I2CBUS 0x6f 0x07 0x00
    ES=$?
    log_action_end_msg $ES
    ;;
  stop)
    log_action_begin_msg "Switching off RasPi-RTC-Power "
    /sbin/rtc-power $I2CBUS $ALARM
    ES=$?
    log_action_end_msg $ES
    ;;
  *)
    echo "Usage: ${0:-} {start|stop}" >&2
    exit 1
    ;;
esac

Listing 7

Number of I2C Bus

# Default settings for Raspi-RTC-Power. This file is
# sourced by /bin/sh from /etc/init.d/raspi-rtc-power.
# Number of I2C bus hosting the RTC chip: 0 or 1
I2CBUS=<bus#>

Next, incorporate the two updated bootup scripts by typing:

$ sudo update-rc.d hwclock defaults
$ sudo update-rc.d raspi-rtc-power defaults

Some necessary functions are not available in the current driver for the MCP79410, so the chip needs its own program (Listing 8) that defines the wake-up time and sets the control register in case of a system shutdown [5] [6].

Listing 8

Program for MCP79410 Chip

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <time.h>
#define MCP7941X        0x6f
#define CTRLREG         0x07
#define ALARM0REG       0x0a
#define ALARMREGSETLEN  6
#define ALARMMC         0x70
#define RTCOUT1         0x80
#define ALARM0EN        0x10
#define MINTIMEGAP      120
char int2bcd(char val) {
  return (val%10+16*(val/10));
}
int main(int argc, char *argv[])
{
  int file;
  int i2cbus; // I2C bus number
  char i2cdevicename[11];
  char cbuffer[2]; // I2C Control Buffer
  char buffer[ALARMREGSETLEN+1]; // I2C Alarm0 Buffer
  char *end;
  time_t atime; // Alarm time in seconds since epoch
  time_t now = time(NULL);
  struct tm *alarm; // Alarm in broken down time
  /* get command line parameters */
  if (argc != 3) {
    printf("Error: 2 Parameters expected.\n");
    exit(1);
  }
  i2cbus = strtol(argv[1], &end, 0);
  if (*end || i2cbus < 0 || i2cbus > 1) {
    printf("Error: I2C Bus Number 0 or 1 expected.\n");
    exit(2);
  }
  atime = strtol(argv[2], &end, 0);
  if (*end || atime < 0) {
    printf("Error: Alarm time zero or greater expected.\n");
    exit(3);
  }
  /* set RTC control and ALARM0 accordingly */
  if (atime == 0) { // in case alarm time is zero then ...
    int i;
    cbuffer[1] = RTCOUT1; // ... simply switch off ...
    for (i=1; i<=ALARMREGSETLEN; i++) buffer[i]=0; // ... and clear alarm time
  }
  else { // in case of a non-zero alarm time ....
    cbuffer[1] = ALARM0EN; // ... enable the alarm ...
    if (atime - now < MINTIMEGAP) atime = now + MINTIMEGAP; // ... make alarm causal ...
    alarm = gmtime(&atime);
    if (alarm->tm_sec > 59) alarm->tm_sec = 59; // ... skip any leap seconds if any ...
    buffer[1] = int2bcd(alarm->tm_sec); // and write seconds
    buffer[2] = int2bcd(alarm->tm_min); // minutes
    buffer[3] = int2bcd(alarm->tm_hour); // hour
    buffer[4] = int2bcd(alarm->tm_wday+1); // day of week
    buffer[4] |= ALARMMC; // add alarm match condition and write
    buffer[5] = int2bcd(alarm->tm_mday); // day of month
    buffer[6] = int2bcd(alarm->tm_mon+1); // month
  }
  /* open i2c device */
  snprintf(i2cdevicename, 11, "/dev/i2c-%d", i2cbus);
  file = open(i2cdevicename, O_WRONLY);
  if (file < 0) {
    printf("Error: Can't open /dev/i2c-%d.\n", i2cbus);
    exit(4);
  }
  if (ioctl(file, I2C_SLAVE_FORCE, MCP7941X) < 0) {
    printf("Error: Can't attach I2C Client 0x%x.\n", MCP7941X);
    exit(5);
  }
  /* write alarm0 to RTC */
  buffer[0] = ALARM0REG; // set ALARM0 register address
  if (write(file, buffer, ALARMREGSETLEN+1) != ALARMREGSETLEN+1) {
    printf("Error: Can't write alarm to I2C Client 0x%x.\n", MCP7941X);
    exit(6);
  }
  /* activate alarm and deactivate power rail */
  cbuffer[0] = CTRLREG; // set RTC control register address
  if (write(file, cbuffer, 2) != 2) {
    printf("Error: Can't write configuration to I2C Client 0x%x.\n", MCP7941X);
    exit(7);
  }
  exit(0);
}

If you set the wake-up time to 0, the chip will not wake up the Rasp Pi. Instead, a wake-up only occurs with a manual start-up or if the power is turned back on. However, if the wake-up time is set for at least 120 seconds in the future, this will be the chosen wake-up time. Otherwise it will be exactly 120 seconds from the current time.

You should save Listing 8 in the rtc-power.c file and immediately compile it on the Rasp Pi itself with the gcc rtc-power.c -o rtc-power command. Before you move the compiled rtc-power file to the /sbin/ directory, you should set the owner and the group to the appropriate values with sudo chown root:root rtc-power.

Wake-Up Call

When a system shutdown occurs – initiated, for example, most easily via sudo halt – the script from Listing 6 expects the wake-up time in the file /etc/wakealarm to be the number of seconds that have elapsed since January 1, 1970, at 00:00:00 UTC [7].

Listing 9 shows how you can define an absolute wake-up time for Christmas Eve or one that is relative to the current time through the command line (here 7 hours, 11 minutes, 13 seconds in the future).

Listing 9

Define Wake-Up Time

$ sudo sh -c 'date -d "2014-12-24 17:59:30" +%s > /etc/wakealarm'
$ sudo sh -c 'echo $((`date +%s` + (7 * 60 + 11) * 60 + 13)) > /etc/wakealarm'

You can deactivate the alarm by deleting this file or by setting the wake-up time to 0.

Buy this article as PDF

Express-Checkout as PDF

Pages: 8

Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content