Jump to content

Script to check the power status, and shutdown if battery is low!


Chris Bognar

Recommended Posts

If anyone is interested, I've created a shell script that can be run with cron to check A/C and battery status, and do a graceful shutdown when a battery threshold is met.  It also logs power outages, battery drain when off A/C, and when it posts a shutdown (if any).  This is a simple script, nothing fancy, but works for me.  It has a granularity of one minute (cron is limited to one minute intervals), but I've been able to keep the system up on heavy load for 4-5 minutes before the script generates a shutdown on low battery.

 

The script is generous -- it waits for 916mV or less before a shut-down.  This value was provided by the KOBOL team on their Wiki as the recommended shutdown threshold for the battery, but can be changed via the script.  You can comment out any file-logging lines, or change the file location, also from within the script.  I've heavily commented it, should be pretty easy to follow.

 

Copy the script below into a file named whatever you choose, place it where you want to run it, and cron it to get it going.  It should only create disk writes when the power is out or when shutting down from battery drain on no A/C (a few lines once per minute), and will create no disk writes  if you comment out the logging.

 

Spoiler

#!/bin/bash

# Basic calls for power stats and variables
BATTINFO=`cat /sys/class/power_supply/gpio-charger/online` # A/C online or offline
RAW_VOLTAGE=`cat /sys/bus/iio/devices/iio:device0/in_voltage2_raw` # Raw battery voltage
VOLTAGE_SCALE=`cat /sys/bus/iio/devices/iio:device0/in_voltage_scale` # Voltage scaling
CRITICAL_MV=916 # Your shutdown threshold, default script value is 916
CURRENT_MV=`echo "$RAW_VOLTAGE * $VOLTAGE_SCALE" | bc` # Converts raw voltage into ADS mV
FILE_LOCATION="/root/LOW_BATTERY" # Set another log location here, if you like

#<- Logs to your file when A/C is down.  Comment out if you don't care about this.
if [[ $BATTINFO -lt 1 ]]
then
printf "\nTick . . .          " >> $FILE_LOCATION
echo `date +"%A, %B %d, %Y . . . %r . . .%n"` >> $FILE_LOCATION
printf "Current battery:  "$CURRENT_MV" mV\t\tCritical threshold:  "$CRITICAL_MV" mV\n" >> $FILE_LOCATION
fi
#<- End of A/C down logging.

#<-- Main powerdown checking starts here.
if [[ $BATTINFO -lt 1 && `echo $CURRENT_MV | awk '{print int($1)}'` -lt $CRITICAL_MV ]]
then

#<--- Shutdown logging.  Comment out if you don't care about this.
printf "\n--------------------------------------------------------------------------------\n"
printf "Critical threshold!\n"
echo `date` >> $FILE_LOCATION
printf "KOBOL was forced to shutdown due to low battery status and main power offline!\n" >> $FILE_LOCATION
printf "--------------------------------------------------------------------------------\n" >> $FILE_LOCATION
#<--- End of shutdown logging.

shutdown -h now
fi
#<-- Main powerdown checking ends here.


 

 

Let me know how it works for you, if you use it.  I've been testing it for the past couple of days, and it's worked as intended.  I'm always open to code tips, and I'm still learning, so any advice is appreciated.

Link to comment
Share on other sites

Looks pretty good man!  Thanks for sharing!

 

In fact, I only decide to write the following because you asked.  ;)  So the following are, at best, quite minor (hopefully constructive) criticisms.  What you wrote is perfectly fine, I have certainly seen much worse.  :thumbup:

 

34 minutes ago, Chris Bognar said:

code tips

 

I would probably put the variable comments on a line above each variable, so they are not so long.  Maybe even with a line between each variable.  For readability.

 

For same reasons, I would indent that loop so it's easier to see what's going on.  A proper editor will do this for you.

 

It's also generally considered good form to keep it under 80 chars wide.  You look like you are pretty close.  Some times can't be avoided with things like those long printf statements.  Having said that, if you ever looked at Armbian scripts themselves, you will see they certainly don't follow that.  :D

 

There are some style guides around for shell scripts (and other languages).  Also the Wooledge Bash FAQ is very good (substance more than style), I learned a lot there.

 

Personally, I like to use a lot more white space and (in general) write plenty comments explaining things (although comments are not as much needed in the case of such a simple script as this one).  Example here.  I don't understand why people cram all their scripts together so much, it's not like we have to pay by the line/character or something.  :D  Not saying you are, just in general I mean.

 

Cheers!  :beer:

Link to comment
Share on other sites

@Chris Bognar Nice script :thumbup:

 

Actually we already implemented in latest release an auto graceful shutdown when system has been running on battery for more than 10min. https://github.com/armbian/build/pull/2376

It's all done with udev rules and systemd unit file so in a way it's pretty elegant :P but pretty simple.

 

We are still planning to deliver at some point an helios64 service daemon that will manage the UPS feature in more advanced way as you are doing ( and to make it easy to configure / manage in OMV too). Other feature of this dedicated helios64 daemon is to control the fan is a more smart way, taking in consideration HDD temperature.

 

Anyhow cool script and thanks for sharing it here, I'm sure it will interest many.

 

(side note: I agree with @TRS-80 a bit compact  and not easy to read, and would nicer as a script trigger by systemd timer)

Link to comment
Share on other sites

  • gprovost changed the title to Script to check the power status, and shutdown if battery is low!

I haven't done much work outside of shell scripting and config files, and I'm not experienced enough to work inside the systemd framework for fear of trashing the system beyond my knowledge.  I've been reading the bash guide TRS-80 offered as a reference, and I've come away with a better understanding of some of the more complex ideas, but I've been browsing it for 3 days and still have quite a bit to read and absorb.  Many thanks for his advice and fast replies.

 

Here is a cleaned up, easier to read version of my original script . . . I haven't modified it operationally because it works like I need it to, but hopefully my style will improve as I get more involved with it.  Also thanks to gprovost for his quick replies and exceptional help to the community as a whole.  This is truly a fantastic machine and labor of love for the KOBOL team.

 

Spoiler

#!/bin/bash

### Basic calls for power stats and variables
	# A/C online or offline
	BATTINFO=`cat /sys/class/power_supply/gpio-charger/online`

	# Raw battery voltage
	RAW_VOLTAGE=`cat /sys/bus/iio/devices/iio:device0/in_voltage2_raw`

	# Voltage scaling
	VOLTAGE_SCALE=`cat /sys/bus/iio/devices/iio:device0/in_voltage_scale`

	# Your shutdown threshold, default script value is 916
	CRITICAL_MV=916

	# Converts raw voltage into ADS mV
	CURRENT_MV=`echo "$RAW_VOLTAGE * $VOLTAGE_SCALE" | bc`

	# Set another log location here, if you like
	FILE_LOCATION="/root/LOW_BATTERY"
### End of variable and call block


### Logs to your file when A/C is down, every minute with battery status.
### If power is restored, logging stops.
### Comment out if you don't care about this.

if [[ $BATTINFO -lt 1 ]]
then
	printf "\nTick . . .          " >> $FILE_LOCATION
	echo `date +"%A, %B %d, %Y . . . %r . . .%n"` >> $FILE_LOCATION
	printf "Battery:  "$CURRENT_MV" mV\t\tCritcal:  "$CRITICAL_MV" mV\n" >> $FILE_LOCATION
fi

### End of A/C down logging.


### Main powerdown checking starts here.

if [[ $BATTINFO -lt 1 && `echo $CURRENT_MV | awk '{print int($1)}'` -lt $CRITICAL_MV ]]
then

	### Shutdown logging.  Logs only when this script calls a shutdown.
	### If power is restored before the battery reaches the threshhold,
	### no shutdown will occur.

	printf "\n-------------------------------------------------\n"
	printf "Critical threshold!\n"
	echo `date` >> $FILE_LOCATION
	printf "Shutdown due to low battery and main power offline!\n" >> $FILE_LOCATION
	printf "---------------------------------------------------\n" >> $FILE_LOCATION

	### End of shutdown logging.


	### And since we have both power down AND critical threshold, shutdown is called.

	shutdown -h now
fi

### Main powerdown checking ends here, along with script.
### The script won't be called again for one minute, so will not run
### again unless your box takes more than one minute to shut down.

 

 

 

Edited by TRS-80
put long output inside spoiler
Link to comment
Share on other sites

1 hour ago, Chris Bognar said:

I've been reading the bash guide TRS-80 offered as a reference, and I've come away with a better understanding of some of the more complex ideas, but I've been browsing it for 3 days and still have quite a bit to read and absorb.

 

Dude, I have been visiting there (on and off) for years, and even then, usually only to look up whatever I am interested in at the time!  I therefore applaud your effort...  :D  :beer:

 

Looking better!  Couple more minor feedback, only because of my autism / ADD I cannot help myself:  :D

 

# You can put this all on same line, loks nicer, IMO:

if [[ $BATTINFO -lt 1 ]]; then

    # do stuff

fi

 

Personally I would also not indent the variable declarations, nor put comments at ends of processes unless there was some reason to.  But I gave you enough grief already.  :D

 

You will find your own style in time (for your own stuff; when contributing you always want to just follow the style of the project).  I also learned a lot by studying other people's code.

 

Next thing you know, you will be submitting patches to Armbian build scripts...  :beer:

Link to comment
Share on other sites

I have built a small UPS to provide 5V to an Orange Pi PC Plus, currently running Armbian 5.1.  My interface Pi <> UPS is a few digital pins and the control of the UPS is an ATtiny84 14-pin ARM IC. On the Pi side, I'm using a CPP program and the WiringOP library.

I have no trouble detecting a low battery and issuing a 'shutdown now' command but I need to monitor the shutdown process and issue an 'ack' signal to the UPS so that it can shut itself down completely once the PI has shut down fully.   
At the moment I'm issuing the 'ack' at the same time as the 'shutdown now' command and the UPS is programmed to wait 20 seconds before shutting down. This seems a bit hit and miss to me and I'd like to be able to detect closer to when the Pi has actually shut down.

Does anyone have any clues for me?

Peter

 

Link to comment
Share on other sites

Maybe create a service that toggles a pin as a watchdog and setup the service in a way it gets shutdown at the latest possible stage? Then you still have to wait a few seconds, but there is less variation.

Or maybe the device has a power led which turns off? Or network led? Then you could hook onto that.

Or connect the DEBUG UART tx to your ATiny and use that do detect when after shutdown has begun no more message are sent.

Or keep it like it is but make sure to issue a 'sync' command before actually starting the shutdown. This way the shutdown will probably have less time variation.

 

Btw. the ATtiny is a AVR not ARM micro controller.

 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...

Important Information

Terms of Use - Privacy Policy - Guidelines