8

I have a Pi Model B Rev 2.0 (I think) and I intend to use it for a Home Automation system. As I have a prepayment meter on my electricity supply I occasionally run out of electrons to go around my home's Mains Supply!

To avoid problems I have obtained a UPS in the form of a UPis Basic made by PiModules. I have configured it so that I can poll the supply voltages via the Pi's own serial port (not the default configuration but a supported set-up documented in the manual).

At present it uses a dedicated GPIO pin (pin 13 on the header, GPIO27 I believe) and the built-in micro-controller uses that and a python script running from rc.local to tell the Pi to shutdown -h now when the pin is taken low - which happens when the UPS "Shutdown" button is pressed or if the battery supply runs to critically low in the event of main supply failure. For the record the script is:

#!/usr/bin/python

# import the  libraries to use time delays, send os commands and access GPIO pins
import PRi.GPIO as GPIO
import time
import os

GPIO.setmode(GPIO.BCM) # Set pin numbering to board numbering
GPIO.setup(27, GPIO.IN, pull_up_don=GPIO.PUD_UP) # Setup pin 27 as an input
while True: # Setup a whille loopto wait for a button press
    if(GPIO.input(27)==0): # Setup an if loop to run a shutdown command when button press sensed
        os.system("sudo shutdown -h now") # Send shutdown command to os
        break
    time.sleep(1) # Allow a sleep time of 1 second to reduce CPU usage

This strikes me as a little inelegant given that init has the built-in capability to handle power events. I should reiterate that I am using sysVinit on Raspbian Jessie NOT the default systemd for that release (for personal preference and familiarity reasons).

I would like to, initially, replace the above script with something that tells init to do its powerfailnow when the pin goes low - and to do a powerokwait if it subsequently returns high. Eventually I also want to poll the serial port and keep an eye on the responses to @rpi, @bat and @ups which return the current voltages on the Pi's 5V rail, the LiPo battery and the UPis own microUsb input respectively - so that the Pi can give an alert on/handle a power-fail (it should result in a powerfail init action and let the Pi report the power loss status to me, the user - assuming I haven't ready noticed!)

However I am having difficulty finding out how Linux UPS devices signal to init that the powerfail \ powerwait \ powerfailnow \ powerokwait commands defined in \etc\inittab should be performed.

Can anyone advise me, for instance, how "grown-up" UPS tell a Linux kernel on a normal PC that "power" events are happening and how I might reproduce the same in this system on a Pi?

  • Thanks @Jacobm001 for spotting the absence of the tag for the Python-ish part of this Question, in the last few hours I have been undergoing a crash course in the language (I am making some headway) but a short-time ago I wouldn't have known my Asp from my Elbow... – SlySven Dec 16 '15 at 5:03
  • I'm also curious how the Pi (or init) knows about power events. Hopefully someone answers soon. – Gene Dec 16 '15 at 7:05
  • I'd look into acpid and maybe you can change your script to react when something changes (pin high -> low) instead of polling the value each second? – Diederik de Haas Dec 16 '15 at 10:06
  • 2
    @PandaLion98 The Pi hardware doesn't have any power events, so there's nothing to be known about them. If some are implemented, init would only know about them if is told, either by the kernel (because of a driver event, which in the case of the pi would presumably be a driver for some additional hardware) or via a user land application. – goldilocks Dec 16 '15 at 17:28
5

Ah, ha! Some paragraphs in the man pages for init(8) refers to the depreciated interface of writing a one letter value to /etc/powerstatus (now replaced by /var/run/powerstatus) and then sending init a SIGPWR signal; the letter should be one of:

  • 'F'ailing power: [main power has failed and the] UPS is providing the power, execute the powerwait and powerfail entries.
  • Power 'O'kay: [main] power has been restored, execute the powerokwait entry.
  • 'L'ow power: the power is failing and the UPS has a [critically] low battery, execute the powerfailnow entry.
If the specified file does not exist or contains anything other than the letters F, O or L, init will behave as if it has read the letter F.

Beneath this is the advisory:

Usage of SIGPWR and /etc/powerstatus is discouraged. Someone wanting to interact with init should use the /run/initctl control channel - see the source code of the sysvinit package for more documentation about this.

So while this might be an answer it isn't the answer - next, I need to look at the source code which is kept as a non-GNU project hosted at the GNU's hosting site.

1

I guess the cleanest approach would be to have a kernel device driver manage GPIO27 and set up to receive an interrupt when it goes low. The interrupt handler would notify init. Page http://elinux.org/RPi_Low-level_peripherals says that Raspbian Wheezy supports GPIO interrupts.

I apologize for the low-quality response, I have not looked at the Linux's gpio drivers and how to extend/enhance them. I have also not looked up the current approved method for notifying init from within an interrupt handler. Hopefully this post will stimulate better responses.

  • Yeah, an interrupt would seem better than polling - Diederik de Haas mentioned that above. The focus of my investigation at the present though is "The interrupt handler would notify init..." it is that mechanism that is puzzling, the powerstat file / SIGPWR signal looks relatively simple and straightforward but seems to be deprecated. I'm trying to find out now about real-life usage of the initctl pipe... – SlySven Dec 18 '15 at 19:51
1

By delving into the source code of the SysV init available from the Free Software Foundation's Savannah server I was able to send requests to my RPi's init by filling out a struct init_request as detailed in the initreq.h header file. Specifically this required the magic, sleeptime and, for my purposes the cmd fields being filled out, with the latter being set to one of INIT_CMD_POWERFAIL, INIT_CMD_POWERFAILNOW or INIT_CMD_POWEROK.

My daemon/program which needs to be run as a user with the permission to write to the init control pipe {originally at /dev/initctrl but moved on Debian and thus Raspbian to /run/initctrl} was then able to send that structure to init which then responded appropriately by responding to the following entries in /etc/inittab:

# What to do when the power fails/returns.
pf::powerwait:/etc/init.d/powerfail start
pn::powerfailnow:/etc/init.d/powerfail now
po::powerokwait:/etc/init.d/powerfail stop

Note: this interface - or at least the power supply notification has NOT been adopted by the new fangled systemd - even though, by what might be regarded as a bit of cargo cult programming it does try to ensure that the initctrl pipe exists. On the other hand this does exactly what I want it to do on my RPi system!

  • While I admire your dedication to this problem of yours, I can't help but notice that you have spent a significant amount of time to implement a solution which is functionally equivalent of your initial script and is not even future proof. – Dmitry Grigoryev Mar 7 '17 at 15:35
  • Well, whilst it is functionally equivalent to the initial system, it uses the recommended provided interface rather than the deprecated one. As to future proofing, isn't it up to the persons writing the future system to implement the known interface of the system they are attempting to supplant to ensure backwards compatibility where possible. – SlySven Mar 8 '17 at 16:28
  • After all, getting a notification of main power failure is pretty important to a system with a UPS so a half decent process/system manager ought to be aware of how its predecessor did things and look out for other processes using that API - a interface that exists but is ignored seems a bit short-sighted... 8-/ According to code at: github.com/systemd/systemd/blob/master/src/initctl/initctl.c all a UPS telling systemd that the main power has failed this way is going to log message: "Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!" – SlySven Mar 8 '17 at 16:41
  • "This is not implemented in systemd. Upgrade your UPS daemon" - that's all you need to know about backwards compatibility in Linux ;) – Dmitry Grigoryev Mar 8 '17 at 16:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.