In addition I want:
- A power light to indicate when the standby power is on and when the main power is on.
- Push button that can turn the who power supply on or off (safely)
- A 12V supply and speed control for the heatsink fan
- An interface to connect temperature sensors so we can monitor the heatsink temperature.
Hardware
As discussed previously, the 5V power for the Raspberry PI is provided by an embedded switch mode supply. This will also act as a stanby power source when the supply is off. The main power supply (i.e. the torroidal transformers) are switched using a different circuit under the control of the Raspberry PI (see previous blog on this).The power switch circuit is based around a P channel MOSFET (Q2) so that initially when power is applied the MOSFET gate is at the supply rail and no power is passed to the Raspberry PI. When the switch is pressed it activates Q3 which pulls the gate low, powers up the Raspberry Pi and actives Q4 which keeps the MOSFET gate low (latches the power on).
When the PI is ready to shutdown it activates Q5 (via the RC network) which turns off Q4 which turns off Q2. The problem is that deactivating the power turns off Q5 and can cause the power to just glitch and turn back on. The RC network keeps the MOSFET off long enough that it won't come back.
The Raspberry PI monitors the voltage at the power switch and will initiate a shutdown if the level goes low (see be
There is a low-side switching transistor that is driven by a PWM signal from the PI to control the fan speed (Q1). The fan is powered from a small boost converter that is running on a daughter board soldered on using a DIP header to the main board. The 12V boost converter is one of these Pololu jobs off ebay. I had to add a diode to the boost converter input as it was causing problems with turn-off and keeping the power on. Also I found out the hard way that a 1N4148 diode was not beefy enough to supply the boost converter.
There is a header and pull-up to connect a string of DS18B20 temperature sensors. These are a very neat 1-wire bus temperature sensor capable of high precision. There is a Linux driver for the Raspberry Pi that makes super-simple to interface. The plan is to add one of these near the pass element on each heatsink and to use this feedback to control the fan speed.
The board is a very and designed to fit onto the raspberry pi GIO header. I took a simple approach in the design and flood-filled the bottom with ground and the top with 5V. The GPIO header is soldered onto the bottom of the board.
Getting the PI to Signal when it is Done
My first approach was to write a little python program that would set a GPIO pin high and to run this when the operating system is about to go into halt state. I modified the /etc/init.d/halt script and added a call to my program just before it calls the 'halt' program but found this didn't work. For whatever reason by the time we get here we can't control the GPIOs from python anymore.
I did some searching and eventually found that there is a way to control the default state of the GPIO pins. There is a set of configuration files in /boot that enables/disables hardware systems. These are read by the kernal at boot and will usually result in additional drivers being loaded but actually you can change the state of hardware using this config.
Some of these files are .dtb files or Device Tree Blob files. There is a compiled (dtc) used to generate these using a dts (text) file. The language is a bit c-like and uses curly brackets to define data as structures.
Initially I thought I just wanted to set the default state of the power pin when the Pi shutsdown but after some digging I found there is a mechanism here to configure a GPIO pin to be set when the PI shuts down - this is basically intended for signalling shutdown to other hardware.
The complicated part is that the device tree is segmented by raspberry pi hardware type and then there is hardware which is available on multiple devices. Some hardware is optional and then you can add custom configurations for hardware. To handle all this they created an overlay system where you can create an overlay dtb file that gets loaded on top of the device tree to implement a specific change.
There is an overlay called gpio-poweroff that does exactly what I want. To enable this you modify /boot/config.and add a line like this (see below). The option after the comma determines which GPIO pin will be asserted at power-off
dtoverlay=gpio-poweroff,gpio=25
Detecting a Power Button Press
Now we want to detect a power button press from python and when it occurs we want to trigger a shutdown. Here is a simple python program that (using the GPIO libraries interrupts feature) triggers a function when the pin state changes from high to low. The pin state change is caused by the button press.
#!/usr/bin/env python
from subprocess import call
import RPi.GPIO as gpio
import os
import time
# Define a function to keep script running
def loop():
while True:
time.sleep(5)
# Define a function to run when an interrupt is called
def shutdown(pin):
os.system("sudo shutdown -h now")
gpio.setmode(gpio.BCM)
gpio.setup(4, gpio.IN)
gpio.add_event_detect(4, gpio.FALLING, callback=shutdown, bouncetime=200)
loop() # Run the loop function to keep script running
Now we make this file executable to the world.
We want this program to start when the operating system boots so we need to create a daemon for it.
I created a file in /etc/init.d called powerbutton-watcher and it contains this (taken from a template). The script resides in the /home/pi/LabPSU/LabPowerSupplyCtrl directory.
#!/bin/bash
### BEGIN INIT INFO
# Provides: powerbutton-watcher
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Watches the state of the power button and initiates shutdown
# Description: Watches the state of the power button and initiates shutdown
### END INIT INFO
# Change the next 3 lines to suit where you install your script and what you want to call it
DIR=/home/pi/LabPSU/LabPowerSupplyCtrl
DAEMON=$DIR/WatchPowerButton.py
DAEMON_NAME=powerbutton-watcher
# Add any command line options for your daemon here
DAEMON_OPTS=""
# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
DAEMON_USER=root
# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid
. /lib/lsb/init-functions
do_start () {
log_daemon_msg "Starting system $DAEMON_NAME daemon"
start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
log_end_msg $?
}
do_stop () {
log_daemon_msg "Stopping system $DAEMON_NAME daemon"
start-stop-daemon --stop --pidfile $PIDFILE --retry 10
log_end_msg $?
}
case "$1" in
start|stop)
do_${1}
;;
#!/usr/bin/env python
from subprocess import call
import RPi.GPIO as gpio
import os
import time
# Define a function to keep script running
def loop():
while True:
time.sleep(5)
# Define a function to run when an interrupt is called
def shutdown(pin):
os.system("sudo shutdown -h now")
gpio.setmode(gpio.BCM)
gpio.setup(4, gpio.IN)
gpio.add_event_detect(4, gpio.FALLING, callback=shutdown, bouncetime=200)
loop() # Run the loop function to keep script running
We want this program to start when the operating system boots so we need to create a daemon for it.
I created a file in /etc/init.d called powerbutton-watcher and it contains this (taken from a template). The script resides in the /home/pi/LabPSU/LabPowerSupplyCtrl directory.
#!/bin/bash
### BEGIN INIT INFO
# Provides: powerbutton-watcher
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Watches the state of the power button and initiates shutdown
# Description: Watches the state of the power button and initiates shutdown
### END INIT INFO
# Change the next 3 lines to suit where you install your script and what you want to call it
DIR=/home/pi/LabPSU/LabPowerSupplyCtrl
DAEMON=$DIR/WatchPowerButton.py
DAEMON_NAME=powerbutton-watcher
# Add any command line options for your daemon here
DAEMON_OPTS=""
# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
DAEMON_USER=root
# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid
. /lib/lsb/init-functions
do_start () {
log_daemon_msg "Starting system $DAEMON_NAME daemon"
start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
log_end_msg $?
}
do_stop () {
log_daemon_msg "Stopping system $DAEMON_NAME daemon"
start-stop-daemon --stop --pidfile $PIDFILE --retry 10
log_end_msg $?
}
case "$1" in
start|stop)
do_${1}
;;
restart|reload|force-reload)
do_stop
do_start
;;
status)
status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
;;
*)
echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
exit 1
;;
esac
exit 0
Finally to get this to start with the OS I used the following command
sudo update-rc.d powerbutton-watcher defaults