The PiWatcher is a very small board that helps you fully shut down or reboot a Raspberry Pi, in case of an incident or simply for power saving purposes. You can even program your PiWatcher to shut down and automatically reboot your Pi a few minutes or hours later. In essence, the PiWatcher is a programmable watchdog circuit for the Raspberry Pi, usable with the humble Pi Zero up to the power-hungry Pi 3.

The Pi Watcher

The board must be fitted on the first 6 pins of the Raspberry GPIO header and plugged into a micro USB power supply suitable for the Raspberry Pi in use. The board will then switch on or off the power provided to the Raspberry Pi based on instructions it receives through I2C. The board also features a miniature tact switch to allow manual control.

A solution for Raspberry Pi power management

The PiWatcher is designed to solve 2 problems with power management on the Raspberry Pi.

First, if you shut down a Raspberry Pi through software, for example by issuing a shutdown or halt command, your Raspberry Pi will cease to operate, but it does not completely shut down. Instead, it enters a quiescent state where it continues to eat 30 to 50mA, sometimes more depending on connected peripherals. This is wasteful and can become an issue in battery powered systems.

The PiWatcher can be programmed to fully cut power to the Raspberry Pi after a certain delay of inactivity. For example, you can tell the PiWatcher to cut power 10 seconds after a shutdown command has been issued, giving the Raspberry Pi enough time to cleanly power off.

The second issue with power management has to do with the ability to recover from situations where a Raspberry Pi becomes stuck in an unrecoverable state. While in theory, this should not happen, a long-running Raspberry Pi can get 'stuck' or 'freeze', due to software or hardware failures or even environmental factors such as heat. In some cases the least unsatisfactory approach to deal with this issue is to simply switch off and on again the Raspberry Pi, enabling the application to continue and/or send out an alert.

The PiWatcher can be programmed to automatically power cycle a Raspberry Pi if it does not receive a 'heartbeat' from the device after a user selectable delay. This can allow some critical applications to continue to operate without human intervention.

Furthermore, the PiWatcher can be programmed to "sleep" for a certain delay after a shutdown, ranging from a few seconds to about 36 hours, before rebooting your Raspberry Pi.

The table below summarizes the power consumption of a PiWatcher connected to a Raspberry Pi (measurements were done with a Pi Zero W).

Situation Current consumption
PiWatcher is running normally 7mA in addition to the power consumption of the Raspberry Pi (typically >130mA).
PiWatcher is "sleeping" until reboot 3mA
PiWatcher has fully shut down >0.5mA

It has an LED and a button too!

The PiWatcher has an LED. When the Raspberry Pi is powered on, the LED is continuously on. When the Raspberry Pi is "sleeping" for a certain delay, the LED blinks slowly. When the Raspberry Pi is powered off, the LED remains off.

The PiWatcher also has a useful micro tact-switch:

  • When your Raspberry Pi is running:
    • a long press (5 seconds) on the button will fully power off the Raspberry Pi.
    • a short press (half a second) on the button will send a software signal that can be exploited for user-defined purposes.
  • When your Raspberry Pi is shut down, a short press on the button will restart the Raspberry Pi.

Using the PiWatcher

Setup

To start using the PiWatcher, follow these steps.

Plug the PiWatcher in the first 6 pins of the Raspberry-Pi GPIO header. For greater mechanical stability it is recommended to secure the PiWatcher to the Pi with a standard 11mm standoff (2.5mm).

Next, plug the USB power supply into the PiWatcher. In turn, this will boot the Raspberry Pi.

To use the PiWatcher on the Raspberry Pi, you will need to enable I2C on the Raspberry Pi. Use sudo raspi-config for this purpose (as described in this Adafruit tutorial for example).

Download the piwatcher command line tool:

wget -N http://omzlo.com/downloads/piwatcher

You'll need to make sure the file is executable:

chmod a+x piwatcher

Note: you can also compile the file yourself from source.

The downloaded binary can then be used to query and configure the PiWatcher.

Typing ./piwatcher status should return:

OK  0x00

If you press the micro-switch on the PiWatcher and then re-run ./piwatcher status, you should see the following output:

OK  0x80    button_pressed

You can clear this button_press event, resetting the status of the PiWatcher by running ./piwatcher reset. Re-running ./piwatcher status will show again status 0x00.

Watchdog

We will now program the PiWatcher to shut off the Raspberry-Pi after 30 seconds if it does not receive any message from the Raspberry Pi, with the command ./piwatcher watch 30.

After this command has been issued, the PiWatcher will start a 30 second counter. If the counter reaches 0, the power to the Raspberry Pi will be cut off. However, this counter will be reset to 30 seconds each time the PiWatcher receives a 'read request' from the Raspberry Pi. A simple way to issue a 'read request' is to issue the command ./piwatcher status.

In this example, a simple way to keep the Raspberry-Pi form shutting down would be to run the following loop:

while true
do 
   ./piwatcher status
   sleep 25
done

The sleep 25 assures that the PiWatcher is called approximately every 25 seconds, taking a 5-second safety margin from our 30-second deadline. If this loop gets interrupted for any reason, the Raspberry-Pi will reboot after at most 30 seconds.

This feature can be exploited to completely shut off a RaspberryPi after a shutdown. Assuming the Pi takes at most 15 seconds to shut down, we can issue the following commands:

./piwatcher watch 15
shutdown now

Automated rebooting

By default, the PiWatcher will shut off the Raspberry Pi until the user presses the button or until USB power is cycled.

We can program the PiWatcher to "wake" the Raspberry Pi after a certain delay (expressed in seconds).

To reboot the Raspberry-Pi 10 minutes after shutdown, we would simply type:

./piwatcher wake 600

A clock under the hood

The PiWatcher is based on a Microchip attiny45 8-bit microcontroller, which runs with an 8Mhz internal oscillator.

An internal clock ticks about 25.04 times per second (8.000.000/(8192*39)). Watchdog and reboot time values are converted to internal clock ticks by multiplying their value by 25. Because of variations in temperature and voltage, the accuracy of these timings may be off by +/-10%.

You can view the actual value of the clock by issuing the command ./piwatcher ticks.

Running piwatcher automatically

In most cases, you don't want to type commands with piwatcher each time your Raspberry Pi boots up. Instead, you will want to run PiWatcher automatically each time the Raspberry Pi boots up, with the correct configuration. Fortunately, this is rather easy thanks to systemd.

As an example, we will consider the case where you want the Raspberry-Pi to fully shut down automatically after 30 seconds of inactivity, either because you've halted it or because it got 'stuck'.

For simplicity, we will assume you are running a recent version of Raspbian Linux and that you have installed the piwatcher binary in your home directory /home/pi/.

It takes 3 steps to implement our scenario.

STEP 1. Create a monitoring script

Use your favorite editor to create the file /home/pi/piwatcher.sh with the following content.

#!/bin/bash

/home/pi/piwatcher watch 30

while true;
do
    /home/pi/piwatcher status >> /dev/null
    sleep 15
done

This will essentially set a 30-second watchdog, and start an infinite loop that sends a message to the piwatcher every 15 seconds to assure that the watchdog is not triggered.

We need to make this script executable as follows.

chmod a+x /home/pi/piwatcher.sh

STEP 2: Create a new systemd service

Now we will create a systemd service for the previously created script. As we need 'root permission' to create the script, we will use sudo:

sudo vim /etc/systemd/system/piwatcher.service

You can replace vim with nano or your favorite editor in the line above.

The content of the file should look as follows.

[Unit]
Description=PiWatcher Service
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=pi
ExecStart=/home/pi/piwatcher.sh

[Install]
WantedBy=multi-user.target

STEP 3: Activate and start the service

Finally, we will activate the service.

sudo systemctl enable piwatcher

This will typically output the following message: "Created symlink /etc/systemd/system/multi-user.target.wants/piwatcher.service → /etc/systemd/system/piwatcher.service."

We will also start the service, as follows.

sudo systemctl start piwatcher

The activation (and start) only needs to be performed once. The piwatcher will now be launched automatically each time the machine boots up.

I2C specification

The PiWatcher uses an extremely simple communication protocol based on I2C. The board essentially features an 8-byte register array that can you can read to or write from, at I2C address 0x62.

This register array is structured as follows:

Register Description
0 Status byte
1 Watchdog timeout in seconds, 0=disabled.
2-3 Wakeup delay in seconds divided by 2 (5 means 10 seconds). LSB first. 0=disabled.
4-7 Internal clock. LSB first.

The status byte has the following structure:

Position Name Description
Bit 7 BUTTON_PRESSED A 1 indicates that the PiWatcher button has been pressed. Writing a 1 will clear the bit.
Bit 6 TIMER_BOOT A 1 means that the PiWatcher rebooted the Pi after a wake-up period. Writing a 1 will clear the bit. This is unreliable however since the PiWatcher may have reset.
Bit 5 BUTTON_BOOT A 1 means that the PiWatcher rebooted the Pi because the user pressed the button. Writing a 1 will clear the bit. This is unreliable however since the PiWatcher may have reset.
Bit 4-0 unused

The command ./piwatcher dump will print the raw content of all registers.

The content of the registers can be manipulated with i2c-tools as well (see instructions here for example). For example, you can get the status byte with the command i2cget -y 1 0x62 0.

Resources