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

Raspberry Pi UPS with Auto Restart

Sat Jun 04, 2016 4:25 am

This post describes a method to equip a Raspberry Pi with features that will allow it to be used autonomously, or in remote locations, in embedded applications (no console or keyboard), and for applications that need to be up and running 24x7 like web servers, file servers, security camera’s, thermostats, robots etc.

Update
After some more detailed analysis, see the follow-up postings, I had to change my initial interpretation of the specifications, to a lower output current rating for the Powerboost. I have changed this post to reflect that.

What that typically means for a 24x7 system is that the application needs to be watched and restarted if crashed or otherwise got terminated. It also means that if the kernel of the RPi itself gets unresponsive, it gets rebooted automatically. Lastly it also needs to have UPS provisions for brown-outs or other power losses, such that the RPi can shutdown in an orderly fashion, to protect the SD card from damage, and to let the application save critical run-time data. Lastly, it means that the RPi gets restarted when the main power comes back on.

There are several commercial offerings that claim or seem to fulfill all or most of the above requirements. I have purchased a couple myself, but eventually, they all ended up in my goodies bin, unused. I also designed a couple of variations myself, but this one is less complex, much more simple to understand and easy to build.

What I will describe here is a set of how-to instructions on how to setup your RPi so it will cover the above situations, and I’ll provide a hardware design for a power supply that uses a small rechargeable battery to provide power when the mains goes away. This power supply also has a hardware watchdog to wake-up and reboot the Pi in all cases. This is a DIY design, and I hope a great learning experience.

I’m not going to cover any other elements of preparing the RPi to function in an “embedded” application. There are other posts available that help you do that.

The hardware design I will describe is based on a module available from Adafruit, and is ideal for what we want to do. The little board contains one chip that will take care of the (complex) charging of a Li-Ion cell, and another chip takes care of the voltage boost to provide a pretty solid 5V level at 1.2Amp max. to the RPi. Here is a link with more information : https://www.adafruit.com/products/2465 The nice thing is that this little board allows you to switch the output power on and off, and it has a low-battery warning signal as well.

I have already posted a design for a supply with it here : viewtopic.php?f=37&t=145954

I actually have two RPi’s powered with that Adafruit module in combination with a start-stop button, and I really like it when I’m running tests of code that I write, or otherwise fiddle with the RPi.
The third RPi is now running with the setup I’m going to describe here. It will run a security camera 24x7 later.

To fully automate the starting and stopping of the RPi under all conditions with this setup, and use a battery as backup, you have to cut the power to the RPi. To make sure it restarts under all conditions, I have added a hardware watchdog that will start the RPi. If the mains drops during operation, the rechargeable cell will provide power to the RPi until the battery gets drained too much in which case a Python script will power-down the RPi, and wait for the main supply to be available again. This little board also allows you to draw power (about 1.0 Amp) from it while it is charging the cell (@max 1Amp) at the same time. If the charging current is less, the device can provide up to 1.2 Amps max. before it drops out. In that case, the chips on the Powerboost board will get hot, so you may have to apply forced cooling.

Results:
I have thoroughly tested this setup with a RPi Model (1) B with a LAN cable, keyboard and console connected to it over a period of several weeks. I don’t have a Model 2 so I can’t verify that one for you, but it should work. I also tested it with a Model 3 and that works too, but only mostly idling.

Heads-up
Technically all RPi's should work with this setup. However, because the maximum current the Powerboost can supply without using the battery is only about 1.0 Amp, realistically, this design will not work well with the Model 2 and 3 because the current requirements can be too large if you use the core(s) with some moderate to extensive computing.
The only real solution of course is to measure the current demands over some period of time while your program is running and then determine if you can use this setup.
I should add that for temporary current peaks, I found that the Powerboost uses the battery to boost the 1.0 Amp limit to just over 1.2Amp. The battery should get a chance to recharge, otherwise it will be drained over time. (If you watch F1 racing, this is like the KERR system.)

Testing the setup with my tiny 500mAh cell, I still got 28 minutes of UPS power for the Model 3 while the boost chip on the Powerboost didn’t even feel warm to the touch during the UPS operation. Note that it only ran the UPS script. Charging the cell after a low-bat shutoff will make the charge chip too hot to comfortably touch, even with only a 500mA charge current but that’s normal.

WARNING:
I'm not a Li-Ion or Lipo expert, which is why I stayed away from using them until now.
Make sure you use a cell that can absorb the maximum charging current of 1 Amp the Powerboost can sink, or reduce the max current by modifying the Powerboost (change the Prog1 resistor R16 (marked 1001 and is located right above the USB vias) from 1K0 (1000mA) as an example to 2K0(500mA), or 2K2(450mA). Here is the schematic of the Powerboost circuitry :
https://cdn-learn.adafruit.com/assets/a ... 1429650091
If in doubt (don't take risks!), use cells with a capacity that is 20-50% higher than the 1 Amp max charge rate. I found that the typical safe maximum charge rate of a cell seems to be 1C (1 x the mAh capacity), but a safer value would be 20-50% below that value, so a 1,200mAh is minimum and a 1,500mAh cell should be OK. Check the specifications though!

This is the tiny Lipo cell I use : http://www.ebay.com/itm/251493181437, and here is a typical specification for these 043040 450-500mAh cells : http://www.powerstream.com/p/H043040SP%20with%20BMS.pdf Note: My cell does not show the manufacturer, so I cannot be 100% sure that the specification exactly matches my cell. In order to use this cell reliably, I had to limit the maximum charge current to 450 mA.

In my other RPi UPS supplies, I use the Li-Ion 14500 cells as you can see in the picture from the post I provided the link for. Or I use the Li-Ion 18650 with 2400mAh.

Here is the schematic for this “embedded” or autonomous design.
li-ion UPSV2.0.gif
li-ion UPSV2.0.gif (57.47 KiB) Viewed 14243 times
It looks a little complicated at first sight, but once you get a feel for all the parts and their functionality, it’s actually not that bad at all. I have taken great pains to avoid special components, so with this bunch of ordinary parts, just about anybody with some soldering and electronic experience can built it. The good news is, that you can most likely fit it inside the casing you use for your RPi. I use a very small 500mAh cell that only measures 40X30X4mm and that provides power for about 30 minutes, depending on your RPi model and the attached devices. Plenty of juice to cover a brown-out, a power glitch, and provide a safe power-down to protect the SD card. If you need more juice, you can easily add larger capacity cells without modifications to the hardware of software.

Let’s go over the building blocks of the hardware design.
The Adafruit Powerboost 1000c gets it power from a micro USB connecter, so you can use the same supply as you normally would use for your RPi. This power is also used to charge the cell, and the output voltage is boosted to get 5V2 on the output pins. The output can be available through a USB connector which is included in the package. I don’t use that connector because I use a good USB cable with a micro USB adapter that I cut to the right length. I solder the power wires directly to the pins on the Powerboost. (look at the picture of the post I showed above)

Low Battery Signal
The Powerboost has a low-battery signal (LBO) that will change state from high to low when the voltage is less than about 3V3. Because the voltage level of the LBO pin can be as high as the charging voltage of the cell, 4V2, I used R9 and R10 to reduce that to a safe level for the RPi. R12 is my standard serial resistor that will limit the current to a safe value for the largely unprotected GPIO pins of the RPi. A software script monitors the level of this pin to shut the RPi down and cut the power to the RPi to stop further draining of the battery.

Manual Shutdown Circuit
I use an optional momentary push button that will allow you to powerdown the RPi manually, so you can force a shutdown to make the whole system powerless without a console and keyboard. This circuit around S1 has to operate also when the RPi is running on the cell, so I feed this circuit with the cell voltage. Because this voltage ranges between 4V2 and 3V5, I use resistors R6 and R7 to lower the voltage to a safe level. I use R11 in combination with C5 to provide switch de-bounce noise filtering. This signal is also monitored by the software script. R11 also functions as a safety for the current limit to the GPIO pin.

Sensing Availability of Main Power
The sensing of the availability of the main power is not really required. I use it in the event logging, and to measure how long the RPi ran on batteries, and also to register what kind of power glitches (in duration) happened over a long period. A serious power loss may be an issue for you if your RPi is located remotely, and so you may want to create some code to send you an email if the power goes away for a longer period. The simple circuit to monitor the main supply is made with two resistors, again to bring the voltage to a safe value for the GPIO pins, through R1 and R2. C1 provides some glitch filtering and R8 is my standard current protection resistor. This GPIO pin also gets monitored with a software script.

The UPS Hardware Watchdog
I have used this hardware watchdog design in previous other automatic power supplies, (Google for “raspberry pi paulv automatic UPS“) and this version only has a few minor component value changes. The watchdog circuit is based around an astable multivibrator, configured with a 555 timer chip, meaning that it continuously fires pulses when power is applied. The 555 is powered by the main 5V so the firing will only start when the main supply is present, not when the RPi runs on the battery. If the RPi runs off the battery, and it gets depleted enough such that the low-bat signal is activated, you don’t want to restart the RPi again, because that would drain the battery further to a point where it can be damaged. The RPi should only (automatically) restart when the main supply is available.

The components for the watchdog have been selected such that there is a period of about 8.5 seconds with a positive pulse (R3 and C3) and about 40 seconds (R4 and C3) in-between pulses. The idea is that this positive pulse will turn on the supply through the EN pin, and it will stay on for about 8.5 seconds. In the meantime, the RPI has started to boot, and an overlay is loaded that will set a GPIO pin high during this boot process, and well before the watchdog pulse goes away again. This special GPIO pin stays high until the RPi gets rebooted or goes through a power-down sequence. The time between RPi power on and the activation of the overlay has changed several seconds over the last kernel versions. It went from about 3.5 seconds to about 5 seconds now on a Model (1) B. That is why I selected a period of 8.5 sec. to be (relatively) safe from future updates.

The watchdog will continue to send pulses when there is a main supply, and this is intended and causes no “harm”. In an earlier prototype, I disabled the 555 through the reset pin, but at a cost of another GPIO pin and it added nothing, so it went in the bit bucket. When a powerdown, halt, shutdown or a reboot is requested, the overlay will change the GPIO pin level and this will cut the power of the RPi. (Yes, I know. The watchdog can overlap and keep the power on, but then the power gets cut at the end of the watchdog pulse.) At the next firing of the watchdog it will start the power-on cycle again. If the RPi exceeded the current the Powerboost can supply, the Powerboost circuits will shutdown. The only way to get the supply restarted again is to remove power and apply it again. This will be done automatically with the hardware watchdog.

The required Device Tree Overlay to control the power is loaded by editing the /boot/config.txt file:

Code: Select all

sudo nano /boot/config.txt
At the very end, add this line:

Code: Select all

dtoverlay=gpio-poweroff,gpiopin=22,active_low
Save the file and close the editor.

Making sure the RPi is always running
The “lowest” level protection is to make sure that the kernel and systemd is responsive and not hanging.

At a “higher” user or application level, we also need to protect the application(s) itself from hangs or accidental terminations. To do that all, we’ll use a combination of hardware and software watchdogs. (I know, it seems like a complete kennel) With the use of systemd, we can easily accomplish this. If you want to know more about the various RPi watchdogs, look at this post : viewtopic.php?t=147501&p=972709

To setup and use the watchdog protection, you need to start with an edit of a systemd configuration file.

Code: Select all

sudo nano /etc/systemd/system.conf
And change this line to read as follows (also remove the ‘#’):

Code: Select all

RuntimeWatchdogSec=10
This single line activates the hardware watchdog from the RPi SOC, and will provide protection for an unresponsive kernel, or issues with the systemd manager itself. The value of 10 (seconds) means that if there is no activity within that period, systemd considers this a failed state and will take action.

As soon as this is activated after a reboot, systemd will start to “ping” the hardware watchdog of the RPi SOC every 5 seconds. If there is no ping for 10 seconds, the RPi will be automatically rebooted and start with a fresh setup.

You can add further protection for the shutdown and reboot processes themselves by changing the next parameter as well:

Code: Select all

ShutdownWatchdogSec=10min
This statement will activate a timer that watches over the combined shutdown and boot period. I have tried but not been able to verify or test that feature myself. If you know a simple way to test that, please let me/us know.
Save the file and close the editor. Reboot the RPi to make the watchdog active.

After the boot, you can check the operation. First of all, you should have two watchdog devices in /dev. Check that by running :

Code: Select all

ls –al /dev/watchd*
You should have two devices listed. It means that the hardware watchdog from the SOC is activated.

Next, run a shell fork bomb to make the kernel unresponsive, but first turn of swapping! :

Code: Select all

sudo systemctl stop dphys-swapfile.service
Actually, swapping to an SD card is not a great idea, especially for embedded applications, so you may want to disable swapping all together:

Code: Select all

sudo systemctl disable dphys-swapfile.service

Then run this fork bomb by typing or cut & paste the following at a shell prompt :

Code: Select all

: (){ :|:& };:
It will take a little while but you’ll see some warning messages appear on the console, and shortly thereafter, the RPi will reboot. So, if the kernel hangs or gets unresponsive, the RPi is rebooted and we will remain in control.

When you have a prompt back again, you can run the following test:

Code: Select all

sudo poweroff
This would normally result in a shutdown of the Pi and the BCM cpu gets halted, only the graphics core is still running. But not anymore with the systemd watchdog enabled. At the end of the shutdown you’ll now see different messages on the console:
Reboot Powerdown…
Starting Power-Off…
And then it seems the kernel is crashing. It’s not, but notice that the RPi will reboot all by itself in 10 seconds. You cannot put it in the Halt state anymore, from now on, it will always reboot. When you see the Starting Power-Off message you can remove power and prevent the RPi from restarting again if that is what you want.

User level application Watchdog
It would be nice if we could also add some protection to our own application. If it is critical, you want it to always run, or restarted when it was terminated (by an outside event, or by an unhandled exception), or gets hung in a loop all by itself, or a bug. Whatever.

Systemd provides an API to a software watchdog system (separate from the one we just activated) that can be used on or for any application program. All you need to do is to add some code to the application to allow it to communicate with systemd. In short, the application needs to notify systemd that has finished the initialization, and will start the main program, and after that, it needs to ping the systemd software watchdog in regular periods. If the application does not, or cannot update this watchdog, it will be terminated and restarted by systemd. If that happens more than a certain number of times within a certain time period, the RPi will be rebooted.

Protecting the User Application with systemd
The first thing you need to do is to prepare the activation of your application with systemd, and tell it what to do in case there is a problem.
To do that, you need to create a service file for you application. I called mine my_auto_ups. Open up an editor with :

Code: Select all

sudo nano /etc/systemd/system/my_auto_ups.service
Cut & paste the following into the open editor:

Code: Select all

# This service installs a Python script that controls the UPS supply.
# The script should never die, and if it does, it will be restarted.
# The Python script also updates the systemd watchdog, so if the script
# hangs, systemd will restart it. The wd timelimit is 10 s, the ping is
# every 5 s.
# If the script is restarted 4 x within 180 s., the RPi is rebooted.

[Unit]
Description=Installing the automatic UPS supply script
Requires=basic.target
After=multi-user.target

[Service]
Type=notify
WatchdogSec=10s
ExecStart=/usr/bin/python /home/pi/my_auto_ups.py
Restart=always

# The number of times the service is restarted within a time period can
# be set. If that condition is met, the RPi is rebooted
#
StartLimitBurst=4
StartLimitInterval=180s
# actions can be none|reboot|reboot-force|reboot-immidiate
StartLimitAction=reboot

# Limits for the starting and stopping of the Python script
#
TimeoutStartSec=4s
TimeoutStopSec=4s

[Install]
WantedBy=multi-user.target
Save the file and close the editor.

The script that controls the UPS supply
The Python script that will watch the UPS supply and take action, is actually quite simple. The trick is to act upon solid and valid inputs coming from the supply when the main power is lost, or back, and when the battery is at a critical level. Lastly, it needs to watch for a user request to manually stop the RPi, so you can pull the plug. When your application is running unattended, it is nice if you have a log file that keeps track on the states of the supply. When there are critical events, you also would want to log that in the syslog file.

Here is my version of the script: ( I named it my_auto_ups.py)

Code: Select all

#!/usr/bin/python
#-------------------------------------------------------------------------------
# FileName:     my_auto_ups.py
# Purpose:      This script interfaces with the Pi Power & UPS hardware to
#               handle brown-outs and loss of the main power. The script will be
#               able to boot and shutdown the RPi automatically without power
#               issues, to protect the SD card. The BCM hardware watchdog is used
#               to protect against kernel hangs. On top of that, the systemd
#               watchdog will ensure that the application and the RPi will
#               always restart.
#
# Note:         All dates are in European format DD-MM-YY[YY]
#
# Author:       Paul W. Versteeg
#
# Created:      May-2016
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    To get a copy of the GNU General Public License
#    go to <http://www.gnu.org/licenses/>
#-------------------------------------------------------------------------------
#
# ===== External Dependencies & Mode of Operation:
#
# ===== The systemd watchdog
# This script uses the systemd watchdog API to restart this app when it
# fails, and to reboot the RPi after it fails a few times to restart properly.
# It also uses the RPi bcm2835-wdt watchdog to help protect against
# kernel and systemd hang-ups. The RPi will get rebooted when the wdog is
# not updated regularly.
#
# To activate the software watchdog, change the following parameters in the
# /etc/systemd/system.conf file:
# RuntimeWatchdogSec=10 (15 is maximum for RPi)
# ShutdownWatchdogSec=10min (max timeout for reboot if it is not functioning)
#
# ====== Mode of operation
# When there is a mains supply, the RPi will boot normally. When the mains goes
# away, the UPS function will keep the RPi running, until the battery gets
# depleted. When that is recognized, the RPi will shutdown until the main
# supply returns.
# The systemd software watchdog in combination with the hardware wdog is used to
# make sure that this script gets restarted if it fails. If it fails too many
# times, the RPi will get rebooted. If the kernel or systemd process gets
# unresponsive, the RPi is rebooted as well.
#
# To make the RPi powerless and disable the automatic restart, you first need to
# turn the main power off. You can then use the stop request button to powerdown
# the RPi, or issue the sudo poweroff command in the shell.
#
# The script reports to a log file and also to /var/log/syslog.
# If run by the shell, it print messages to the console
#===============================================================================
#
# ===== the systemd configuration file for my_auto_ups.service is located here:
# /etc/systemd/system/
#
#===============================================================================


import RPi.GPIO as GPIO
from time import sleep, time, strftime
import subprocess
import sys
import socket
import os

# ==== constants
__author__ = 'Paul W. Versteeg'
VERSION = "3.1"

RUN_AS_DAEMON = True    # set during testing by the shell, uses print to console
DEBUG = True
TEST= False             # if True = no powerdown or reboot while testing

# ==== variables
sock_rpt = True         # Used to report only the first socket message
main_pwr = True         # flag to reduce redundent reporting

# ==== GPIO setup
STOP_REQ    = 23    # Stop/start button input, active low to shutdown RPi
UPS_LOW_BAT = 24    # Critical battery level, active low, shutdown RPi
MAIN_PWR    = 25    # Presence of main power, 1 = power


def init():
    '''
    Initializes a number of settings and prepares the environment
    before we start the main application.

    '''
    global wd_ping, pwr_at_boot

    try:
        # create and setup the log file
        if not os.path.isfile('/home/pi/auto_ups.log'):
            # create the file and set the access mode
            subprocess.call(['touch /home/pi/auto_ups.log'], shell=True, \
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            subprocess.call(['chmod goa+w /home/pi/auto_ups.log'], shell=True, \
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        write_log("\n===== Starting auto UPS script V{0}".format(VERSION))

        # get the watchdog timeout period set in the systemd service file
        if RUN_AS_DAEMON : # this will only work if started by systemd
            wd_usec = os.environ.get('WATCHDOG_USEC', None)
            if wd_usec == None or wd_usec == 0:
                sys.stderr.write("Auto UPS terminating : incorrect watchdog interval sequence\n")
                exit(1)
        else: # used when invoked by the shell
            wd_usec = 20000000 # 20 seconds

        wd_usec = int(wd_usec)
        # use half the time-out value in seconds for the watchdog ping routine to
        # account for Linux housekeeping chores
        wd_ping = wd_usec / 1000000 / 2
        #
        # ----- setting up of the GPIO Ports & Interrupt Service Routines
        #
        write_log("Starting hardware interface setup")

        if DEBUG: GPIO.setwarnings(True)
        else: GPIO.setwarnings(False)

        # Use the Raspberry Pi BCM pins
        GPIO.setmode(GPIO.BCM)

        # This port senses the presence of the main power
        GPIO.setup(MAIN_PWR, GPIO.IN)

        # This port is connected to a button
        # It is used to shutdown the RPi without the automatic restart
        GPIO.setup(STOP_REQ, GPIO.IN, pull_up_down=GPIO.PUD_UP)

        # This port is connected to the low battery signal pin
        # It is used to shutdown the RPi and wait for main power to return
        GPIO.setup(UPS_LOW_BAT, GPIO.IN)

        # --- these definitions need to be after the port definitions,
        # --- otherwise the detections will not work!
        #
        # setup the event to detect the button press to powerdown the RPi
        GPIO.add_event_detect(STOP_REQ, GPIO.FALLING, callback=stop_req, bouncetime=20)
        #
        # notify systemd that we've finished the initialization
        retval = sd_notify(0, "READY=1")
        # check for a fatal error
        if retval <> 0:
            write_log("Fatal sd_notify() error for script start, retval={0}".format(retval))
            sys.stderr.write("Auto UPS terminating : fatal sd_notify() error for script start\n")
            os._exit(1)  # force the exit to the OS

        # start the first ping to the systemd sw watchdog and check for errors
        retval = sd_notify(0, "WATCHDOG=1")
        if retval <> 0:
            write_log("Fatal sd_notify() error for watchdog ping, retval={0}".format(retval))
            sys.stderr.write("Auto UPS terminating : fatal sd_notify() error for watchdog ping\n")
            os._exit(1)  # force the exit to the OS

        # if we have lost main power during the boot proces, shutdown the RPi
        if GPIO.input(MAIN_PWR) == 0 :
            write_log("No pwr at boot -> Executing poweroff")
            sys.stderr.write("Auto UPS : No pwr at boot -> Executing Poweroff\n")
            subprocess.call(['sudo poweroff'], shell=True, \
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            # do not return, wait it out here. This will cause a reboot
            # by the systemd watchdog if something goes wrong during shutdown.
            sleep(40)

        write_log("Watchdog ping interval = {0} sec.".format(str(wd_ping)))
        sys.stderr.write("Auto UPS : watchdog ping interval = {0} sec.\n".format(str(wd_ping)))
        write_log("Setup is finished. Starting Main")
        sys.stderr.write("Auto UPS : Setup is finished. Starting Main\n")

    except Exception as e:
        write_log("Exception in init(), Reboot & Enable UPS watchdog : \n{0}\n".format(str(e)))
        sys.stderr.write("Auto UPS : Exception in init(), Reboot & Enable UPS watchdog\n{0}\n".format(e))
        os._exit(1) # force the exit to the OS



def write_log(msg):
    '''
    Function to create a log of the app events and messages.

    If the script is started from the shell, all messages are send to the
    console as well to help in the testing process.

    Messages are appended to the log file, so it needs to be cleaned every once
    in a while. Due to the small amount of messages, logrotate is overkill.

    '''
    try:

        dstamp = strftime("%d-%m-%Y")
        tstamp = strftime("%H:%M:%S")

        if RUN_AS_DAEMON == False :
            # write messages to the console as well
            print(str(dstamp)+"\t"+(tstamp)+"\t"+str(msg))
        # open the log file and append results
        with open("/home/pi/auto_ups.log", "a") as fout:
            # Tabs are used to seperate the fields so technically it's not a real CSV format.
            # MS-Excel reads it anyway.
            fout.write (str(dstamp)+"\t"+(tstamp)+"\t"+str(msg)+"\n")

    except Exception as e:
        sys.stderr.write("Auto UPS: Unexpected Exception in write_log()\n{0}".format(e))
        return(1)



def sd_notify(unset_environment, s_cmd):

    '''
    Notify the systemd manager about start-up completion and ping the watchdog.

    From:
    https://github.com/kirelagin/pysystemd-daemon/blob/master/sddaemon/__init__.py

    This is a reimplementation in Python of systemd's reference sd_notify().

    It is used here to signal systemd about the start-up completion notification
    and for pinging the systemd software watchdog. If this script gets
    unresponsive, systemd will restart it. If it fails a few times in a row,
    systemd will reboot the RPi.

    '''
    global sock_rpt

    sock = None

    if RUN_AS_DAEMON == False :
        print "Not running as a daemon, cannot communicate with systemd socket"
        return(0)

    try:
        if not s_cmd:
            sys.stderr.write("Auto UPS error : missing command to send\n")
            return(1)

        s_adr = os.environ.get('NOTIFY_SOCKET', None)
        if sock_rpt : # report this only one time
            write_log("Notify socket = {0}".format(str(s_adr)))
            sys.stderr.write("Auto UPS : Notify socket = {0}\n".format(str(s_adr)))
            # this will normally return : /run/systemd/notify
            sock_rpt = False

        if not s_adr:
            write_log("Error, missing socket")
            sys.stderr.write("Auto UPS : error, missing socket\n")
            return(1)

        sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
        sock.sendto(s_cmd, s_adr)
        # sendto() returns number of bytes send
        if sock.sendto(s_cmd, s_adr) == 0:
            write_log("Error, incorrect sock.sendto return value")
            sys.stderr.write("Auto UPS error : incorrect sock.sendto return value\n")
            return(1)

    except exception as e:
        write_log("Unexpected Exception in sd_notify()\n{0}".format(e))
        sys.stderr.write("Auto UPS: Unexpected Exception in sd_notify()\n{0}".format(e))
        os._exit(1) # force the exit to the OS

    finally:
        # terminate the socket connection
        if sock:
            sock.close()
        if unset_environment:
            if 'NOTIFY_SOCKET' in os.environ:
                del os.environ['NOTIFY_SOCKET']
    return(0) # so we can test the return value for a successful execution



def stop_req(STOP_REQ):
    '''
    Interrupt service routine that gets called when the start/stop button
    is pressed while the RPi is running.

    Powerdown in this case means that the RPI will become powerless and the
    systemd watchdog will reboot the RPi in 10 seconds after poweroff.

    To avoid incorrect button presses, the button must be pressed for >7 seconds
    otherwise, it is deemed an invalid request.

    '''
    try:
        sleep(1)
        if GPIO.input(STOP_REQ) == 1 :
            # False signal
            return

        # remove the edge detection for the duration of this ISR
        GPIO.remove_event_detect(STOP_REQ)

        write_log("Stop request detected")

        edge_start = time() # create a timestamp reference
        cnt = 0
        while (time() - edge_start) <= 7 : # check within a 7 Sec window
            sleep(1) # check every second
            if GPIO.input(STOP_REQ) == 0 : # Looking for valid stop request levels
                cnt += 1
            else: # when we have captured a glitch, start all over again
                cnt = 0
            # With 5 consecutive levels, or when the max time is up, leave.
            if cnt == 5 :
                break

        if cnt == 5 :
            # Valid stop request. Start the powerdown.
            # just in case we're not running as root, use sudo here:
            if TEST :
                print ">>> poweroff"
                # rearm the event detection
                GPIO.add_event_detect(STOP_REQ, GPIO.FALLING, callback=stop_req, bouncetime=20)
                return
            else:
                write_log("Executing poweroff")
                sys.stderr.write("Auto UPS : Executing Poweroff\n")
                subprocess.call(['sudo poweroff'], shell=True, \
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                # do not return, wait it out here. This will cause a reboot
                # by the systemd watchdog if something goes wrong.
                sleep(40)
        else:
            write_log("Invalid Stop request : cnt={0} - rearm".format(cnt))
            # rearm the event detection
            GPIO.add_event_detect(STOP_REQ, GPIO.FALLING, callback=stop_req, bouncetime=20)
            return

    except Exception as e:
        write_log("Unexpected Exception in stop_req()\n{0}".format(e))
        sys.stderr.write("Auto UPS: Unexpected Exception in stop_req()\n{0}\n".format(e))
        return



def chk_4_low_batt():
    '''
    This function shuts the RPi down when the UPS battery level is getting low.

    The low_bat signal has no real Schmitt-Trigger, so when the battery is
    getting low, the low-bat signal jitters. This code will try to filter false
    signals, and will only shut the RPi down when there is a solid low level.

    '''
    try:
        sleep(1) # s small grace period
        if GPIO.input(UPS_LOW_BAT) == 1 :
            # invalid low bat signal
            return

        # verify if we indeed have a valid low batt level
        edge_start = time()
        cnt = 0
        while (time() - edge_start) <= 4 : # check within a 4 Sec window
            sleep(0.5) # check every .5 second
            if GPIO.input(UPS_LOW_BAT) == 0 : # Looking for valid low batt levels
                cnt += 1
            else: # when we have captured a glitch, start all over again
                cnt = 0
            # With 5 consecutive levels, or when the max time is up, leave.
            if cnt == 5 :
                break

        if cnt == 5 :
            #
            # Start the poweroff process.
            if TEST :
                print "low_bat_detected : sudo poweroff"
                return
            else:
                write_log("Low bat detected -> executing poweroff")
                sys.stderr.write("Auto UPS : low bat detected -> executing poweroff\n")
                subprocess.call(['sudo poweroff'], shell=True, \
                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                # Wait it out here. This will cause a reboot by the systemd
                # watchdog if there is a problem.
                sleep(40)

        if cnt <> 0 :
            write_log("Invalid low bat detected")

        return

    except Exception as e:
        # if there is an exception, still force the poweroff
        write_log("Unexpected Exception in chk_4_low_bat() - poweroff\n{0}".format(e))
        sys.stderr.write("Auto UPS : Unexpected Exception in chk_4_low_bat() - poweroff\n{0}\n".format(e))
        subprocess.call(['sudo poweroff'], shell=True, \
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        sleep(40) # wait it out here, which will cause a reboot by systemd anyway.



def chk_4_main_pwr():
    '''
    This function watches for changes (loss|return) of the main power.

    This event gets reported in the log so that we know how long the system ran
    on battery power until either the mains came back, or at what time the
    battery got depleted and forced a shutdown.

    '''
    global main_pwr

    try:
        # determine what kind of main power change we got
        edge_start = time() # set timestamp for maximum search duration
        loss_cnt = 0 # count valid power losses
        pres_cnt = 0 # count valid power presence

        while (time() - edge_start) <= 5 : # check within a 5 Sec window
            sleep(0.5) # check every .5 second
            if GPIO.input(MAIN_PWR) == 0 : # Looking for valid lost main levels
                loss_cnt += 1   # counting mains lost levels
                pres_cnt = 0    # reset presence count
            else:
                pres_cnt += 1   # counting main presence levels
                loss_cnt = 0    # reset loss count
            if DEBUG :
                print "pwr lost cnt = {0} - pwr pres cnt = {1}".format(loss_cnt, pres_cnt)
            # With 5 consecutive levels, leave.
            if loss_cnt == 5 or pres_cnt == 5:
                break

        # if we're here, a count was 5, or we hit the time limit.
        if loss_cnt == 5 :
            if main_pwr == True :
                write_log("Main power is lost")
                sys.stderr.write("Auto UPS : Main power is lost\n")
                main_pwr = False
        elif pres_cnt == 5 :
            if main_pwr == False :
                write_log("Main power is present")
                sys.stderr.write("Auto UPS : Main power is present\n")
                main_pwr = True
        else:
            # if neither count was 5, it was invalid, report but do nothing else
            write_log("Bad main power event: loss={0} pres={1}, rearm".format(loss_cnt, pres_cnt))
            sys.stderr.write("Auto UPS : Bad main power event, rearm\n")
        return

    except Exception as e:
        write_log("Unexpected Exception in main_pwr_action()\n{0}".format(e))
        sys.stderr.write("Auto UPS : Unexpected Exception in main_pwr_action()\n{0}\n".format(e))
        return


def main():

    init()

    try:
        while True :
            # wait for a button press to shutdown the RPi
            # ping the systemd watchdog
            sd_notify(0, "WATCHDOG=1")
            # watch out for a low bat signal
            chk_4_low_batt()
            # watch out for a main power change
            chk_4_main_pwr()
            # sleep time before we ping the watchdog again
            sleep(wd_ping)

    except KeyboardInterrupt:
        if RUN_AS_DAEMON == False :
            print "\nCtrl-C"

    except Exception as e:
        write_log("Unexpected Exception in main()\n{0}".format(e))
        sys.stderr.write("Auto UPS : Unexpected Exception in main()\n{0}".format(e))
    finally:
        write_log("Terminated")



if __name__ == '__main__':
    main()
Make sure you make the Python script file executable.
You’ll notice that I don’t use edge interrupts for the UPS_LOW_BAT and the MAIN_PWR signals. The reason is that the LBO pin will have a lot of jitter before there is a solid low. Also, there are issues with the GPIO edge recognition. (search for “paulv GPIO characterization” if you want to know more) If you filter out those unwanted glitches, you may miss the real important edge, so I use a level check in the main loop instead. The same is true for the main power.

The rest is pretty much documented in the code.

If you make changes, make sure the script runs without errors before installing it in the boot sequence, because systemd will restart the failed code right away and after 4 times, it will reboot the RPi. You only have a few seconds to issue “sudo systemctl disable my_auto_ups.service” when you have a prompt, so be careful.

Testing the setup
You can run a first test by changing the global variables RUN_AS_DAEMON to False and to prevent rebooting, set TEST to True. Then you can start it from a shell prompt with ./my_auto_ups.py and watch the console for messages and errors.

The next step is to execute it within the systemd environment, and check the communication through the socket. First change the RUN_AS_DAEMON variable back to True. Then run:

Code: Select all

sudo systemctl start my_auto_ups.service
Check the successful installation with :

Code: Select all

sudo systemctl status my_auto_ups.service
While the script is running, you can follow the log file with :

Code: Select all

tail -f auto_ups.log
You can stop the script with:

Code: Select all

sudo systemctl stop my_auto_ups.service
Now you can test the rebooting and powerdown if you change the TEST variable back to False.

After successful tests, you can install the script in the boot sequence with :

Code: Select all

sudo systemctl enable my_auto_ups.service
While the script is running, you can follow the log file again with :

Code: Select all

tail -f auto_ups.log
If you open another session with PuTTY, you can test the restarting of the script by just using

Code: Select all

ps ax 
To find the process_id number of the Python script and then run :

Code: Select all

sudo kill <process_id>
If you then run ps ax again, you’ll see that the script has a new process_id, and the restarting will have been logged in the file. If you do that 4 times in a row within 180 seconds, the RPi will be rebooted.

Test the UPS supply.
While the system is running, and you are monitoring the log file, pull the main supply plug. After a few seconds you’ll see that the loss of the mains gets logged. Apply main power again, and you’ll see that logged after a few seconds as well. Now take the mains away and wait for the battery to drain, and the low_bat signal to shutdown the RPi. (depending on the capacity of the cell you use, this may take a while…)

Because there is no mains, the UPS watchdog is not running, so the RPi will become and stay powerless. If you now add the mains again, the watchdog will start and apply power again, so the RPi will boot and the cell will be charged at the same time.

The next test is the stop request with the button. Just press the button for more than 7 seconds and the RPi will go through a power-off sequence and become power-less. The watchdog will turn it back on if you do nothing else. However, if you want to completely make the system powerless, you also need to pull the main supply, so the watchdog is no longer running and the RPi stays powerless.

Note that parts of the Powerboost circuit stays powered by the battery, so I use a connector to attach the cell allowing me to pull that. You could also use a switch to take the power away. The drain on the battery is very minimal, but if you don’t use the supply for longer periods, I suggest you disconnect the battery from the Powerboost circuit.

After this all works OK, you can now add your own application to the mix. If you can, I suggest you get that started through a systemd service file. If you also want to have that application “watched” by systemd, you can use the my_auto_ups service file as a starting point and modify that. You will then also need to add the sd_notify() function to your code, and add the notification and watchdog ping pieces to it as well. If your application is in C, you can use the standard sd_notify() code.

I hope you will be able to follow the instructions and have no problems building the hardware to get it all to work.

Enjoy!

PS Watch for more information in the follow-up posts!
Last edited by paulv on Wed Apr 19, 2017 6:35 am, edited 12 times in total.

Moe
Posts: 230
Joined: Sun Jan 25, 2015 2:44 pm

Re: Raspberry Pi UPS with Auto Restart

Mon Jun 06, 2016 9:39 pm

This is good stuff, thanks for posting! I plan to do something similar, but my needs are less complex.

Is GPIO 22 the one that stays high until shutdown is complete? If so, could I just set it high in my python script and leave it there until the Pi shuts down and turns it off?

My plan would be to OR the main supply and the GPIO output to control the enable, so restoration of the main supply would reboot the Pi.
Submarine communication systems engineer and amateur robot enthusiast.

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

Re: Raspberry Pi UPS with Auto Restart

Tue Jun 07, 2016 8:01 pm

Hi Moe,

Yes you can use a Python script to set the port high (I indeed use GPIO-22, can be any general purpose port that does not have a real pull-up by default), and rely on the power-down to reset that pin back to float (if you use GPIO.cleanup()) or pulled low when the RPi gets powerless.

Keep in mind though that there is an undefined point in time when the Python script gets activated in the boot sequence. If the system runs a disc check it maybe minutes after it started to boot. Using the overlay cuts that time to a "dependable" 5 seconds.

Success!

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

Re: Raspberry Pi UPS with Auto Restart

Mon Jun 13, 2016 8:11 pm

While still learning about Li-Ion and Lipo cells, I put a little circuit together with an ADC that let me plot the voltage and the current level over time.

Here is a resulting graph of the charging of my tiny 043040 Lipo cell:
043040 Charge.gif
043040 Charge.gif (33.46 KiB) Viewed 14196 times
As you can see, the peak charging current is about 630mA for a short period, and although this is within the specification of 1.5C (750mA), it is still a little high to my liking.
BTW, I read somewhere that the noise on the graph is coming from the chemical processes within the Lipo cell, and this seems to be normal. I see this on Li-Ion cells as well.

I modified one of my Powerboost circuits by changing the Prog1 resistor that sets the maximum charge current from 1K0 to 2K2. This caps the current to 454mA, which is better for the 500mAh Lipo cell I'm using.

Here is a voltage/current graph of the modified Powerboost circuit while charging the same cell.
043040 Limited Charge.gif
043040 Limited Charge.gif (31.65 KiB) Viewed 14196 times
As you can see, the current is now capped at 454mA.

While I'm at it, here is a discharge V/A curve for this cell while powering an RPi Model 3.
043040 Discharge.gif
043040 Discharge.gif (26.01 KiB) Viewed 14196 times
I get about 29 minutes of power for an almost idling RPi, plenty for my purpose that will actually power a Model (1) A or B.

Enjoy!
Last edited by paulv on Fri Jun 17, 2016 6:56 am, edited 2 times in total.

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

Re: Raspberry Pi UPS with Auto Restart

Wed Jun 15, 2016 8:30 am

Some more observations and some proposed changes to the Adafruit Powerboost 1000c.

After I modified the maximum charge current to about 450mA, I did another charge analysis on one of my 18650 2400mAh cells, just to see the effect.
Here is the resulting graph:
18650 Charge2.gif
18650 Charge2.gif (26.27 KiB) Viewed 14120 times
The effect of the modification on the CC and CV is easy to see. The time to fully charge the cell went from about 4hrs to more than 9hrs though.

Here are some more observations from using the Powerboost circuit to specifically power RPi's.
(This was most likely never the intended purpose)

From the specifications of the MCP73871-2CC chip, I understand that the maximum current the Powerboost is able to supply to the RPi is a factor of the input current, the charge current for the cell and the mode it is in. The mode is selected by the SEL pin, and is fixed for a wall-wart type supply.
The maximum input current is fixed at 1.8 Amps. According to the specs. of the MCP charge controller, the maximum output is 1.65 Amps, and this is a combination of the charging current and the payload current. The payload has priority over the charging current. The charging current is also reduced when the die temperature is above a certain level, this is also fixed. In a follow-up post, you can see that the Powerboost package cannot supply more than 1.0Amp total to the RPi, without the battery. With the battery, the current can be temporarily boosted to 1.2Amps, but it needs some time to be recharged otherwise it drains.

The standard package can supply a 1.0 Amp maximum charge current, leaving the remaining for the Rpi, however, the power consumption of the RPi has priority, so that really means that the actual charge current is a factor of the RPi consumption, and the remaining is used for the charging of the cell up to a maximum of 1Amp. The power consumption of an RPi fluctuates a lot, depending on the activity of the RPi itself and the USB peripherals, like a USB WiFi adapter. So this means to me that there is hardly any Constant Current mode in the charging process.

Because I'm using the cell really only as an emergency backup, this should not pose any real problems. So far so good I think.

If you are using cells with a capacity of 1000mAh capacity or less, I suggest you reduce the maximum current by changing the Prog1 resistor (R16), like I described in an earlier post. Follow the specs of the cell you are using.

There are however two more modifications that will make the Powerboost a better fit for our RPi's.
The way the MCP73871 actively switches the main supply and that of the battery, allows it to provide "extra" juice if there is a power request that the main supply cannot fulfill. The result is normally a sagging of the voltage of the main supply. The MCP chip can provide extra power by using the cell to boost the output temporarily. As we know, the power demands of a RPi can vary a lot, and if the voltage drops below a certain value, the RPi will get very unstable, so this is a great feature.

Unfortunately, the Powerboost circuit has been programmed such that this feature is not really working with our RPi's. The input voltage measurement, called VPCC, can be set to trip at levels that are determined by two resistors. R6 and R7 on the schematic diagram. Unfortunately, the resistor combination of 100K and 270K results in a calculated tripping voltage of 4.55V. (I actually measured that the tripping level starts at 4.66V, probably due to tolerances) As we know, either value is too low for the RPi. My suggestion is to use either a combination of 82K and 240K, which results in a tripping voltage of 4.83V, or to use 91K and 270K which provides 4.88V.

The last suggestion for a modification has to do with the tripping point for the charge completion of the cell. This is set with resistor (R17) on the Prog3 input, and that is currently a 100K resistor resulting in a 10mA charge completion current. In the three types of cells I tried, during dozens of discharge/charge cycles, this almost never happens. In my opinion, it takes far too long for the charge completion LED to come on, and sometimes it does not, eben when there is no more current flowing to the cell. Unplugging and plugging the cell does result in the completion of the cycle. In too many cases though this means that the charge cycle is never terminated, and that is not OK I think.
Sometime soon, I'm going to modify R17 to either 82K resulting in a 12.2mA threshold, or use 75K to get a 13.3mA charge completion level and see if that solves the problem.

If you have other observations, experiences or suggestions, please chime in!

Enjoy!
Last edited by paulv on Sun Jun 26, 2016 1:01 pm, edited 1 time in total.

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

Re: Raspberry Pi UPS with Auto Restart

Thu Jun 16, 2016 12:58 pm

Here are some more details of my actual measurements of the Powerboost package.

I already measured that the maximum current the Powerboost can supply to the RPi payload is 1.0Amps continuously, and that the cell is used to temporarily boost that current to 1.2Amps.

To make the following analysis, I supplied the Powerboost with a constant voltage from a lab power supply, set at 5.0V. I connected a dynamic load to the output of the Powerboost, and while changing the payload current on the dynamic load, I also registered the lab supply current going in to the Powerboost, and the voltage level at the payload output of the PowerBoost.

Here are two graphs that show the effect of the output voltage and input current with and without a cell connected to the Powerboost.
The first one shows the maximum current without the cell (to eliminate the boost)
The horizontal axis are the payload current steps set by the dynamic load.
Load no-batt.gif
Load no-batt.gif (8.87 KiB) Viewed 14039 times
After loading the Powerboost with a little more than 1.0 Amp, the device shut off. The output voltage was a healthy 5.0V at the 1.0 Amp level.

Here is the same measurement, but now with a battery connected to the Powerboost, so it can use the cell to boost the current.
Load with-batt.gif
Load with-batt.gif (9.5 KiB) Viewed 14039 times
You'll notice that the maximum current was boosted by the cell to just over 1.2Amp, while the output voltage was still a respectable 4.94V. Notice how the load current curve flattends above the 1.0Amp setting. This is where the cell is used to boost the output current. When I went beyond the 1.2Amp level, the Powerboost shut down again.

The shutting down can only be undone by removing the output load, or the input. My hardware watchdog will take care of that automatically. However, you don't want to have this happening, because it will crash your RPi.

The best remedy is to make sure you never get close to the 1.2Amp level, but with the greatly varying current demands a RPi poses, while it is running, this is not so easy. If you are living on the edge, the best method will be to measure the output or input current level to/from the Powerboost, or the output voltage level from the Powerboost, with an ADC or an inline current shunt, and shutdown the RPi in an orderly fashion when this is about to happen. In my case, using a Model (1) A or B, I never get close to this abyss.

In an earlier post I warned about the temporarily boosting of the current by the cell, because the cell needs time get recharged itself. If this happens too often, you'll deplete the cell. Again the analogy is that of the KERR system in F1 racing. You can boost power, but you need to harvest it first.

I have some more stuff coming next.

Enjoy!
Last edited by paulv on Wed Apr 19, 2017 6:32 am, edited 2 times in total.

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

Re: Raspberry Pi UPS with Auto Restart

Thu Jun 16, 2016 3:09 pm

And I have some more information, actually some more bad news for power hungry RPi's...

I wanted to measure how well the cell can support the Powerboost output at various current levels, to test the UPS functionality.
In this case, there is no power supplied to the main input of the Powerboost. To measure this, I connected the output of the Powerboost to a dynamic load, and I varied the current levels of that load to measure the output voltage level and the current the cell itself provides to the Powerboost. To measure the cell current, I added a 100 milli-Ohm shunt in the cell power connection, and I measure the voltage across it to work out the current.

I did this measurement with the three cells I have used before.
Here is the graph for the 18650 Li-ion with 2400mAh capacity:
Load 18650.gif
Load 18650.gif (10.12 KiB) Viewed 13988 times
As you can see, there is a very disappointingly meager 850mA as a maximum output coming from this cell. Beyond 800mA, the output voltage starts to plummet. I don't have more room for another graph in this post, but I'll show you an abysmal efficiency graph in a next post as well.

Here for the 14500 Li-ion with 1200mAh capacity:
[UPDATE]
My 14500 cells are NOT 1200mAh! I got duped on Fleabay. Little did I know that these cells do not exist with this capacity. The maximum seems to be 900mAh for this size. The two I got turn out to be less than 600mAh, and weigh 15 grams, the real ones with 900mAh weigh in at 18 grams. Bummer! I cannot trust these two cells so they will go in the recycle bin. Be aware of these scams and if it sounds too good to be true (high capacity or low price), it probably is...
Load 14500.gif
Load 14500.gif (9.66 KiB) Viewed 13988 times
This smaller capacity cell actually performs a little better, so it's not just a function of the Powerboost alone, although it must take the overall blame for the poor performance.

And finally the 043040 Li-po with 500mAh capacity:
Load 043040.gif
Load 043040.gif (8.88 KiB) Viewed 13988 times
This cell, not surprisingly, tops out at 600mA, but the voltage stays above the 5V level.

So, as an emergency backup, the Powerboost is even less useful than I anticipated. From the specifications of the TPS61090RSAR boost chip and the MCP73871-2CC charge regulator, I expected more in terms of real performance, alas, real life is different.

What this means for the UPS phase if your application draws more power than the amount the Powerboost can supply while in UPS mode, is that as soon as the main power goes away, you have to find a way to significantly reduce the power the RPi consumes. One way can be to quickly terminate the most processor intensive application(s) and shut them down orderly. It can also mean that you may have to terminate the usage of the network and even the USB devices like the WiFi adapter after it has sent out a distress signal by email.

In my own application with my classic Model 1 A or B, I'm still OK, but I will try to test some ways to reduce power when needed, and see what savings can be made that way.

Stay tuned...

Enjoy!

After I wrote this post, I started to work on the harnessing of the power budget of the RPi models. I found very little that described what could be done, so I write a separate post for others to easily find.
Here it is : viewtopic.php?f=29&t=152692

Enjoy!
Last edited by paulv on Thu Jul 14, 2016 11:57 am, edited 3 times in total.

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

Re: Raspberry Pi UPS with Auto Restart

Thu Jun 16, 2016 6:00 pm

Here is a graph of the efficiency of the Powerboost conversion from cell energy (I*V) to output energy(I*V) with increasing load:
18650 efficiency.gif
18650 efficiency.gif (7.15 KiB) Viewed 13839 times
!Enjoy
Last edited by paulv on Fri Jun 17, 2016 6:28 am, edited 1 time in total.

gordon77
Posts: 4225
Joined: Sun Aug 05, 2012 3:12 pm

Re: Raspberry Pi UPS with Auto Restart

Thu Jun 16, 2016 7:05 pm

Hopefully goes up in heat not smoke ;)

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

Re: Raspberry Pi UPS with Auto Restart

Fri Jun 17, 2016 7:14 am

Hi Gordon(77),

Heat yes, but no smoke. (;-))
The Powerboost is well protected against overheating and even a short.
The cells I use have built-in protection against over- and under-voltage and a short as well.
I'm still "green" using Li-ion and Li-poly cells, but I think I'm safe...

Cheers!

medicdude
Posts: 11
Joined: Sun Jun 19, 2016 4:35 am

Re: Raspberry Pi UPS with Auto Restart

Mon Jun 20, 2016 5:40 am

I'm concerned your use of a shunt to measure battery current is in-appropriate due to the changing internal resistance of the battery, assuming that you're calculating for current based on a fixed total resistance on the circuit.

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

Re: Raspberry Pi UPS with Auto Restart

Tue Jun 21, 2016 4:32 am

Hey Medicdude,

Thank you for posting what seems like a warning. Unfortunately I do not fully comprehend what you are trying to convey.

For the sake of the readers of this post and myself, can you please substantiate (point to an article or reference) or otherwise explain why you think it is in-appropriate to use the industry standard technique (a DMM uses it too) to measure current flowing in or out of a battery?

medicdude
Posts: 11
Joined: Sun Jun 19, 2016 4:35 am

Re: Raspberry Pi UPS with Auto Restart

Wed Jun 22, 2016 7:21 pm

My concern is that calculating amperage from a shunt's voltage drop requires other assumptions (or measurements) about the circuit. If fixed assumptions are used they will change as the battery is loaded or discharged. Since you did not mention anything else in your explanation of the shunt application I assumed you were calculating wrong, it was late at night :P You're probably doing it right, i'm just asking for a more detailed explaination of how you calculated amperage from the shunt.

I intended it as a heads up for any beginners who might be calculating wrong values with a shunt.

medicdude
Posts: 11
Joined: Sun Jun 19, 2016 4:35 am

Re: Raspberry Pi UPS with Auto Restart

Wed Jun 22, 2016 7:32 pm

Also it is my understanding that most multimeters use a current transformer and not a shunt to measure amperage, could be wrong there though.

medicdude
Posts: 11
Joined: Sun Jun 19, 2016 4:35 am

Re: Raspberry Pi UPS with Auto Restart

Wed Jun 22, 2016 8:13 pm

Ok I've gone back to my brain and realized that the shunt has a known fixed resistance and the current can be calculated from the voltage drop across it.

Not sure where I went wrong originally XD

User avatar
GeekMike
Posts: 6
Joined: Mon Jun 27, 2016 8:42 pm

Re: Raspberry Pi UPS with Auto Restart

Mon Jun 27, 2016 9:02 pm

Power states and Heartbeat signals communicated via GPIO pins.

We recognized the same potential power problems with SBC’s (Single Board Computers) like Raspberry about a year and a half ago. Our solution is simple: Create a power management controller to manage battery and computer uptime.

It works like this: When main power drops, a battery will provide power to the SBC and communicate to the SBC that main power is off via GPIO. When the battery degrades a GPIO pin communicates to the SBC that a reboot (power shut off) is imminent.

Shortly after communicating a reboot to the SBC, power to the SBC is terminated.

Power to the SBC is turned on shortly after main power is restored, which will cause the SBC to start up. In addition to power management, we included an optional GPIO heartbeat from the SBC to indicate to the power controller that all is well with the SBC. If the heartbeat stops, power is cycled causing a reboot.

Our idea is patent pending. Let us know if you are interested.

mosespi
Posts: 508
Joined: Mon May 12, 2014 3:35 pm
Location: 34,-118
Contact: Website

Re: Raspberry Pi UPS with Auto Restart

Tue Jun 28, 2016 6:14 am

GeekMike wrote:Power states and Heartbeat signals communicated via GPIO pins.

We recognized the same potential power problems with SBC’s (Single Board Computers) like Raspberry about a year and a half ago. Our solution is simple: Create a power management controller to manage battery and computer uptime.

It works like this: When main power drops, a battery will provide power to the SBC and communicate to the SBC that main power is off via GPIO. When the battery degrades a GPIO pin communicates to the SBC that a reboot (power shut off) is imminent.

Shortly after communicating a reboot to the SBC, power to the SBC is terminated.

Power to the SBC is turned on shortly after main power is restored, which will cause the SBC to start up. In addition to power management, we included an optional GPIO heartbeat from the SBC to indicate to the power controller that all is well with the SBC. If the heartbeat stops, power is cycled causing a reboot.

Our idea is patent pending. Let us know if you are interested.
At least for a US patent.. watch out for prior art. There has been plenty discussed on these forums for years, and several products have come out specifically for the Pi. The other 3 'rules' for US patents.. they have to be useful, novel, and non-obvious. I've known of hardware watchdogs for.. a long time!

Although I'm sure if you insist and pay enough.. a patent attorney will try anyway!

Regards,
-Moses
Power problems? MoPower UPS for the Pi
http://www.allspectrum.com/mopower/

User avatar
DougieLawson
Posts: 36319
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website Twitter

Re: Raspberry Pi UPS with Auto Restart

Tue Jun 28, 2016 7:10 am

http://www.uugear.com/witty-pi-realtime ... pberry-pi/ looks like prior art - so your patent fails.
Note: Having anything humorous in your signature is completely banned on this forum. Wear a tin-foil hat and you'll get a ban.

Any DMs sent on Twitter will be answered next month.

This is a doctor free zone.

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

Re: Raspberry Pi UPS with Auto Restart

Tue Jun 28, 2016 7:23 am

A rather shameless plug for a "product" that claims something new?
Look at this earlier post for a power supply that already uses a hardware watchdog :
viewtopic.php?f=37&t=50470&start=25
This was posted in August 2013.
I came up with this part of the design myself, but in retrospect it is so simple that I'm 99% sure I was not the first one with that idea.
Don't spend your money on patent lawyers. Use Google, it's free.

mjf2708
Posts: 8
Joined: Sat Aug 10, 2013 4:42 pm

Re: Raspberry Pi UPS with Auto Restart

Wed Mar 28, 2018 3:12 pm

I've just successfully built this, and it works well - thanks @paulv.

I discovered one small side-effect though - the Low (red) LED on the Powerboost 1000C is permanently on. Looking at the Adafruit schematic, this is because the transistor driving the LED is biased on by R10 in the UPS circuit (happens whether connected to RPi or not, and if mains power present or not - confirmed this by temporarily disconnecting R10). Not an issue, as we have the 'true' LBO through the UPS, and better to reduce the voltage appearing on the GPIO pin.

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

Re: Raspberry Pi UPS with Auto Restart

Wed Mar 28, 2018 3:30 pm

Well done mj2708!

I have the same observation. Doesn't bother me though, because it helps me to see if the RPi is powered or not. On my system, the red LED is on when the RPi is off, and when it runs, this LED is off.

The green LED is on when the main power is present, and the blue shows power to the RPi.

It works really well, I have been using this setup in two versions, the simple one for my development work and powering my tunnel AP/server, and one with the automatic reboot for my embedded application, running an MQTT broker and small webserver.

Enjoy!

Return to “Automation, sensing and robotics”