paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

HOW-TO Automatic Power Supply with UPS V4

Mon Jan 11, 2016 8:49 am

Some recent discoveries and developments led to me build yet another automatic power supply for the Pi with a UPS function. A supply like this is crucial for (headless) embedded applications and servers that need to run unattended 24x7.

My latest design for such a supply (my Version 3) was extensively documented here:
viewtopic.php?f=37&t=102015
Separately, I found a simple method to control the power for a Pi with just one button, described here:
viewtopic.php?f=91&t=129357

During a discussion with Forum moderator PhilE about Device Tree Overlays, he hinted that I could use the gpio-poweroff overlay with my automatic PS. So, I spend a couple of days trying to combine the collected wisdom of the two posts and attempt a new design. Turns out, this design (Version 4) is even less complex than the last version.
First of all, some small print... I have built this as a fully functioning prototype running on one of my Model B Rev 2 Pi's, with the very latest kernel and updates (10-jan-16), but time and other constraints will not permit me to build a real circuit on a (proto) PCB for another 3 months. I believe however that this design can be easily replicated, but there are no guarantees.
Here is the circuit:
Automatic PS V4.png
Automatic PS V4.png (62.21 KiB) Viewed 9010 times
If you have looked at the above two posts, you will recognize the simple trickle charging unit around the rechargeable cells that will provide power when the mains is gone. You will also recognize the modified Boost-Buck DC-DC converter (you can also use the LM2577/LM2596 combo) that we also use as a power switch. Lastly you will recognize the Watch-Dog timer circuit. Some optional frills have been removed from version 3 of the PS, but they can be added if you want. BTW, version 3 has been used with my file server 24x7 ever since, and works great.

This is how this one works.
I have limited this design to a 5V DC Wall-Wart, but the design can be easily changed for a 12V DC Wall-Wart. I use the same trick to get the Pi to power-up, by using a charging capacitor (R3 and C2) to turn on the LM2596-ADJ chip and supply power to the Pi to let it boot. I use the same gpio-poweroff overlay that needs to be added to the /boot/config.txt file:

Code: Select all

dtoverlay=gpio-poweroff,gpiopin=22
This overlay gets activated very early in the boot process, and will pull the Enable pin from the LM2596 low, and keep the power on. When the Pi is told to shutdown through a

Code: Select all

sudo poweroff
the Enable pin will be pulled high, cutting the power from the Pi.
There is no button to turn it back on, we use a little robot to do that for us. This is the Watch-Dog circuit from V3. When released, it barks every 55-60 seconds to wake-up the Pi. The 555 timer outputs a positive pulse that needs to be inverted by the MOSFET, and this will short the voltage across C2, restarting the power-up cycle.

I kept the timing of the Watch-Dog the same, a pulse every 55-60 seconds, because you'll need some time to pull the main power and remove the battery supply if you want to reliably shutdown the whole system, ie. make everything powerless. Otherwise, the Watch-Dog will continue to start-up the Pi. I have also added a button to execute a shutdown to be able to stop the whole thing manually. A status LED will give a visual clue of what is going on.

You may notice that I used different values for R3 and C2. I had some troubles to get a voltage that was high enough to pull the junction where the Enable pin of the LM2596 connects. The higher you select R3, to use a lower value C2, the weaker the pull. The voltage across C2 will not rise enough against all the negative pulling, so be aware of this snag.

Here is a simplified but fully functioning Python script to run the supply:

Code: Select all

#!usr/bin/env python2.7
#-------------------------------------------------------------------------------
# Name:         Automatic UPS V4
#
# Purpose:      This program gets installed by cron at boot.
#               It runs with the V4 hardware of the automatic PS with UPS.
#
#               It disables the Watch-dog timer and senses the availability
#               of the main supply to keep track of the UPS window.
#               Another input tracks a button that the user can press to start
#               a Poweroff. This allows the user to shutdown the power
#               completely before the Watch_Dog restarts the system again.
#
#               The Pi will be made powerless through a gpio pin that is
#               controlled by a DTS overlay. /boot/config.txt needs to have :
#               dtoverlay=gpio-poweroff,gpiopin=22
#
# Author:      Paul Versteeg
#
# Created:     10-jan-2016
# Copyright:   (c) Paul Versteeg 2016
# Licence:     Free except for any commercial use, just reference the copyright.
#-------------------------------------------------------------------------------

import RPi.GPIO as GPIO
import subprocess
from time import sleep


__author__ = 'Paul Versteeg'
VERSION = "4.0"

DEBUG = True            # True during testing and with shell control
NO_SHUTDOWN = False     # if True: do not execute the Shutdown when testing

OFF = False
ON = True

if DEBUG :
    UPS_MODE = 10       # While testing use 10 seconds
else:
    UPS_MODE = 10*60    # Run 10 minutes on batteries in normal operation
                        # Beware, it may take 5-10 x longer to re-charge due
                        # to the 80-100mA trickle charge

# Setting up the GPIO interface
GPIO.setmode(GPIO.BCM) # use GPIO numbering
if DEBUG: GPIO.setwarnings(True)
else: GPIO.setwarnings(False)
#
# The program uses the following GPIO pins but this can be changed to any you
# like. You need to add 1K8 pull-ups if you change the next two pins
Shutdown_P = 2      # Input, a low means the user want to shutdown the Pi
                    # This port has a 1K8 pull-up
Sys_Stat_P = 3      # Output, shows system status LED, port has 1K8 pull-up

PWR_Sense_P = 17    # Input, a low means the main power went down
Watch_Dog_P = 23    # Output, a low disables the Watch_Dog timer
POWEROFF = 22       # Reserved by the overlay defined in /boot/config.txt
                    # not used in this program



def init():
    '''
    Initializes the hardware and set-up the system status LED

    '''
    global sys_stat

    if DEBUG : print "Init()"

    # GPIO port definitions:
    # Turn off the Watch_Dog timer first of all
    GPIO.setup(Watch_Dog_P, GPIO.OUT)
    GPIO.output(Watch_Dog_P, GPIO.LOW)
    # setup an event to detect a falling edge on the main power sense input
    GPIO.setup(PWR_Sense_P, GPIO.IN)
    # init the port that measures the button press for a shutdown
    GPIO.setup(Shutdown_P, GPIO.IN)
    #
    GPIO.add_event_detect(PWR_Sense_P, GPIO.FALLING, callback=power_action, bouncetime=5000)
    #
    # system status LED setup
    GPIO.setup(Sys_Stat_P, GPIO.OUT)
    # Use Pulse Width Modulation to control the flashing of the LED
    # begin with a frequency of 0.5 Hz
    sys_stat = GPIO.PWM(Sys_Stat_P, 0.5)
    # start the PWM, but with a duty cycle of 0; the LED is on with no flash
    sys_stat.start(0) # further controlled by system_status()
    #
    # if we lost the main power during the boot process, we need to
    # check this now and take action.
    if (GPIO.input(PWR_Sense_P) == 0) :
        if DEBUG : print "Init: no main power detected"
        # Main power was lost during boot process
        power_action(PWR_Sense_P)
        # maybe power comes back during the UPS period, otherwise we'll
        # shutdown the Pi and wait for clean power again
    #
    # start the System Status hart-beat
    system_status(ON, 20, 0.5) # Normal Operation, 20% ON, 80% OFF @ 0.5 Hz



def system_status(mode, duty_c=20, freq=0.5):
    '''
    Function to drive the status LED.

    parameters : mode [ON|OFF],
                 duty_cycle [0..100] 0=on, 100=off,
                 blinking frequency in Hz
    '''
    global sys_stat

    if mode:
        sys_stat.ChangeDutyCycle(duty_c)
        sys_stat.ChangeFrequency(freq)
    else:
        sys_stat.ChangeDutyCycle(100) # LED is off



def power_action(PWR_Sense_P):
    '''
    This function controls the UPS mode and will shutdown the Pi.

    If a loss of main power is detected, the battery acts as a UPS.
    We need to figure out if it only was a glitch, if not, the Pi is shutdown.

    When the shutdown sequence has started, it cannot be stopped.
    If the power comes back on in this period, the Pi will still be left
    powerless. In that case, the PWR_Watchdog function will restart the Pi.

    After a normal shutdown, and the main power returns, the Pi will get
    power and will be restarted automatically.
    '''

    if DEBUG : print "power_action"

    # flash at 3 Hz so the user can see we have a loss of main power
    system_status(ON, 50, 3)

    sleep(1) # Let the voltage drop completely
    # we can play UPS for a while before we perform the shutdown.
    # if there was a power glitch, we'll let the Pi continue
    # if the mains is really out and we lost power, we need to shutdown the Pi.

    ups_timer = 1
    while (ups_timer < UPS_MODE) and (GPIO.input(PWR_Sense_P) == 0): # play UPS
        if DEBUG : print "Loss of main power detected, starting UPS period : {0} sec.".format(ups_timer)
        sleep(1) # check every second
        ups_timer += 1

    if ups_timer < UPS_MODE :
        # The power came back within the UPS period!
        sleep(1) # let the system settle a bit and check it again
        if GPIO.input(PWR_Sense_P) == 1 :
            # power is back, return to normal
            if DEBUG : print "Main power came back, go back to normal"
            system_status(ON, 20, 0.5) # system LED flashing back to normal
            return

    # Main power still off -> starting shutdown process
    system_status(ON, 50, 6) # start flashing at 6 Hz to show
    #
    # Start the shutdown process
    # Release the Watch_Dog of the power supply
    GPIO.output(Watch_Dog_P, GPIO.HIGH)
    #
    if NO_SHUTDOWN :
        print "starting fake shutdown - going back to normal"
        system_status(ON, 20, 0.5) # Normal Operation, 20% ON, 80% OFF @ 0.5 Hz
        return
    else:
        system_status(ON, 50, 10)
        subprocess.call(['poweroff'], shell=True, \
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        sleep(10) # don't return, wait here



def main():
    try:
        init()
        while True:
            # set an interrupt on a falling edge and wait for a button press
            # this will start a shutdown process such that the power can be removed
            # before the Watch_Dog restarts the Pi.
            GPIO.wait_for_edge(Shutdown_P, GPIO.FALLING)
            # signal user that we've seen the button press
            # starting to flash the LED
            system_status(ON, 50, 4)
            # wait for 4 seconds to see if this was deliberate
            sleep(4)
            # check the button level again
            if GPIO.input(Shutdown_P) == 0:
                system_status(ON, 50, 10)
                sleep(1)
                # still pressed, is a serious request, reboot Pi
                # do not release the Watch_dog, we do not need an automatic
                # restart
                if NO_SHUTDOWN :
                    print "faking a Halt, resume"
                    system_status(ON, 20, 0.5) # back to normal operation
                else :
                    subprocess.call(['poweroff'], shell=True, \
                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                    sleep(10) # wait here for the Shutdown to happen
            # wait for a main power drop
            else:
                system_status(ON, 20, 0.5) # back to normal operation

    except KeyboardInterrupt as e:
        print ("\nCtrl-C  {0}".format(e))
    except Exception as e:
        print ("Exception {0}".format(e))
    finally:
        print "No GPIO Clean-Up!"
        # be aware! cleanup resets the pins to no-pull (foating pins), while the
        # default after a power-up is pull-low!
        # When this daemon is terminated, it is because of a powerdown, which
        # will reset the GPIO settings anyway.


if __name__ == '__main__':
    main()
There are enough comments in the code to allow you to hopefully understand and also modify it. You can "borrow" code from the other Python scripts described in the previous Forum post. I installed this code by using cron to start it at boot, but you can also modify the shell script in /etc/init.d to start and stop it. Here is what I added in cron (crontab -e):

Code: Select all

@reboot sudo python /home/pi/UPS4_daemon.py
There is no need to use and change the dts.blob file with this setup. Simple and straightforward.

Well, there you have it, an even less complex version of a fully automatic power supply for the Pi, that I think is very easy to build with parts that cost less than 10 Euros in total. Apart from the NiMH or NiCd cells, and even they do not cost much anymore. This design only uses through-hole parts, so I could (finally) adhere to one of my own design constraints in the V3 How-To.

So is there a caveat with this simple design? Yes, there are two.
When the mains is gone, and the UPS period has finished, The Pi is powerless, waiting for the mains to come on, but in the meantime, the DC-DC converter and the other components like the 555 timer continue to draw power from the batteries. This is only a few mill-amperes (I measured less than 10), but if the mains stays away for too long, your cells may get depleted.

The other one is that by executing a sudo poweroff when you are in the shell, it will not make the Pi powerless for more than a second and will restart right away. You can't use that method to make the whole thing powerless, you have to use the button. Keep that in mind.

Next up, I'm planning to do some more work on the back-up battery circuit, but that will have to wait until April, so stay tuned for more.

In the meantime, enjoy!
Last edited by paulv on Sun Jan 17, 2016 4:33 am, edited 4 times in total.

FM81
Posts: 518
Joined: Wed Apr 17, 2013 4:33 pm

Re: HOW-TO Automatic Power Supply with UPS V4

Tue Jan 12, 2016 6:42 am

paulv wrote:When the mains is gone, and the UPS period has finished, The Pi is powerless, waiting for the mains to come on, but in the meantime, the DC-DC converter and the other components like the 555 timer continue to draw power from the batteries. This is only a few mill-amperes (I measured less than 10), but if the mains stays away for too long, your cells may get depleted. Even though you can put the XL6009 in standby mode by lowering the EN pin, it will not go to zero.
Disconnecting NiMH-batteries can be done, for example, by an Opto-MOS AQV252G. (Think a power-MOSFET will do the same.)
Other way to decrease current in powerless-state: Have you experience in ATMEL's or such? An ATtiny13 uses in powerdown less than 1µA ...

Greetings, FM_81
A: What does the command 'cat /dev/urandom', can you tell me please?
B: Yeah, that's very simple: It feeds your cat with radioactive material!

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Automatic Power Supply with UPS V4

Tue Jan 12, 2016 7:36 am

Hi FM_81,

Thank you for bringing this up. The Opto-MOS AQV252G is an interesting device I did not know about.

I did consider using a MOSFET to switch off the power, and already did that as you can see in my version 3 design. I specifically omitted this feature for this newer design, to make it more simple and avoid working with SMD chips. If you use 2000mAH type cells, the mains can be gone for a very long time if the parts only draw 10mA in the "off" mode. This is unlikely to happen in normal situations, which is why the omission is not critical, as long as you know about it.

Using a micro controller does not fit my design requirements for this Forum (listed in the Version 3 link), because many of the Forum members will not have the experience nor the tools to work with them. I personally use them every now and then, but this is the Raspberry Pi Forum...

Enjoy!

Return to “Automation, sensing and robotics”