Chris Bognar Posted December 9, 2020 Posted December 9, 2020 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. 5 Quote
TRS-80 Posted December 9, 2020 Posted December 9, 2020 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. 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. 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. Not saying you are, just in general I mean. Cheers! 0 Quote
gprovost Posted December 9, 2020 Posted December 9, 2020 @Chris Bognar Nice script 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 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) 0 Quote
Chris Bognar Posted December 12, 2020 Author Posted December 12, 2020 (edited) 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 December 12, 2020 by TRS-80 put long output inside spoiler 1 Quote
TRS-80 Posted December 12, 2020 Posted December 12, 2020 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... Looking better! Couple more minor feedback, only because of my autism / ADD I cannot help myself: # 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. 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... 0 Quote
OldBikerPete Posted June 20, 2021 Posted June 20, 2021 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 0 Quote
Heisath Posted June 20, 2021 Posted June 20, 2021 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. 0 Quote
djurny Posted June 20, 2021 Posted June 20, 2021 Hi, Have you checked "nut" (network UPS tools) to see if they have some prefab solutions? Perhaps a custom nut driver, as I recall they have this type of behavior already available for "regular" UPSes. Groetjes, 1 Quote
Recommended Posts
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.