-
CommentStreams:C4473590b5c6d8cc05cca487c77ac93d
As others have mentioned, I was finding that the provided scripts were immediately shutting down the x728 but were not doing a safe shutdown of the raspberry pi. The power was immediately being cut to the raspberry pi resulting in a crash.
See the last command for an example
pi@raspberrypi:~ $ last ... reboot system boot 5.15.76-v7l+ Wed Dec 31 18:00 - 10:22 (19348+16:22) pi pts/0 192.168.68.70 Thu Dec 22 09:48 - crash (-19348+15:48) pi tty7 :0 Thu Dec 22 09:47 - crash (-19348+15:47) pi tty1 Thu Dec 22 09:47 - crash (-19348+15:47) ...
I created a new script to do a safe shutdown and while I was at it, I added functionality and made the script configurable so you can choose the behavior you want for a given system. I also added a testing mode that provides more output, doesn't do a shutdown and cleanly exits the GPIO settings for the next test run.
I have created a bin directory in the pi home for testing. The script can be setup in the root crontab as follows, which will create a log file in /home/pi/log.
@reboot python -u /home/pi/bin/auto_shutdown.py >> /home/pi/log/auto_shutdown.log 2>&1
You can then tail the logfile to see the current status.
pi@raspberrypi:~/log $ tail -20f auto_shutdown.log
12/24/2022, 21:38:02 : System Startup 12/24/2022, 21:38:02 : AC Power GOOD 12/24/2022, 21:38:02 : Battery Capacity Good (96.23%) 12/24/2022, 21:38:02 : Battery Voltage Good (4.22V) 12/25/2022, 09:26:47 : AC Power Lost - Shutdown in 15 minutes 12/25/2022, 09:28:47 : AC Power Restored - Shutdown cancelled 12/25/2022, 09:56:09 : AC Power Lost - Shutdown in 15 minutes 12/25/2022, 10:11:00 : Shutdown in progress due to AC power loss
12/25/2022, 10:16:25 : System Startup 12/25/2022, 10:16:25 : AC Power GOOD 12/25/2022, 10:16:25 : Battery Capacity Good (98.77%) 12/25/2022, 10:16:25 : Battery Voltage Good (4.22V)
As you can see, if power is restored prior to the automatic shutdown, it will return to it's normal state. This is to prevent unneeded shutdowns in the event the power is cycling as it is being restored from the grid.
Here is the last command showing good shutdowns with this script.
pi@raspberrypi:~ $ last ... reboot system boot 5.15.76-v7l+ Wed Dec 31 18:00 - 21:36 (19351+03:36) pi pts/0 192.168.68.70 Sat Dec 24 21:08 - 21:11 (00:03) pi tty7 :0 Sat Dec 24 21:07 - 21:11 (00:04) pi tty1 Sat Dec 24 21:07 - down (00:04) ...
I recommend using testing mode from the command line and then once you have all the configurations the way you want them, turn off testing mode and schedule in crontab. Remember, testing mode will provide mush more output but will not trigger a shutdown.
Here is the script. Please use it at your own risk. You will want to set the configurations appropriate for your raspberry pi purpose. I am using all three triggers because I want a clean shutdown after 15 minutes or if there is an issue with the batteries so I'm triggering well before the batteries are drained. I hope some find this helpful.
pi@raspberrypi:~/bin $ cat auto_shutdown.py
- !/usr/bin/env python
import RPi.GPIO as GPIO import time import os import datetime import struct import smbus import sys
- Start Config
SHUTDOWN_TRIGGER = "All" # (AC Power,Capacity,Voltage,All) - determines what settings will trigger a shutdown. AC_LOSS_WAIT_MINUTES = 15 # Number of minutes after power loss before shutdown is issued CAPACITY_THRESHOLD = 50 # Shutdown will occur when capacity drops below the stated percentage CAPACITY_STATUS_LOW = 20 # Low capacity warning below this level VOLTAGE_THRESHOLD = 3.70 # Shutdown will occur when voltage drops below the stated percentage VOLTAGE_STATUS_LOW = 3.50 # Low voltage warning below this level TEST_MODE = "N" # (Y,N) - Will show extra output and will not perform shutdown when TEST_MODE = "Y", normal operation TEST_MODE = "N" BUZZER_ON = "N" # (Y,N) - BUZZER_ON = "Y" and buzzer will beep when power is initially lost and right before shutdown BUZZER_SECONDS = 0.5 # Number of seconds the buzzer will sound
- End Config
WAIT_SECONDS = AC_LOSS_WAIT_MINUTES * 6 WAIT_STR = str(AC_LOSS_WAIT_MINUTES)
GPIO_PORT = 26 I2C_ADDR = 0x36
GPIO.setmode(GPIO.BCM) GPIO.setup(GPIO_PORT, GPIO.OUT)
PLD_PIN = 6 BUZZER_PIN = 20 GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(PLD_PIN, GPIO.IN) GPIO.setup(BUZZER_PIN, GPIO.OUT) GPIO.output(BUZZER_PIN, 0) POWER_LOST_SEC = 0
def soundBuzzer(BUZZER_ON,BUZZER_SECONDS,BUZZER_PIN):
if ( BUZZER_ON == "Y" ):
GPIO.output(BUZZER_PIN, 1)
time.sleep(BUZZER_SECONDS)
GPIO.output(BUZZER_PIN, 0)
def acPower(PLD_PIN):
AC_STATUS = "GOOD"
i = GPIO.input(PLD_PIN)
if ( i == 0 ):
AC_STATUS = "GOOD"
elif ( i == 1 ):
AC_STATUS = "LOST"
return AC_STATUS
def readVoltage(bus,VOLTAGE_STATUS_LOW):
address = I2C_ADDR
read = bus.read_word_data(address, 2)
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
VOLTAGE = swapped * 1.25 /1000/16
VOLTAGE_STATUS = "Good"
if ( VOLTAGE <= VOLTAGE_STATUS_LOW ):
VOLTAGE_STATUS = "Low"
return VOLTAGE,VOLTAGE_STATUS
def readCapacity(bus,CAPACITY_STATUS_LOW):
address = I2C_ADDR
read = bus.read_word_data(address, 4)
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
CAPACITY = swapped/256
CAPACITY_STATUS = "Good"
if ( CAPACITY >= 100 ):
CAPACITY_STATUS = "Full"
elif ( CAPACITY <= CAPACITY_STATUS_LOW ):
CAPACITY_STATUS = "Low"
return CAPACITY,CAPACITY_STATUS
def safeShutdown(GPIO_PORT):
GPIO.output(GPIO_PORT, GPIO.HIGH)
os.system('sudo shutdown -h now')
time.sleep(3)
GPIO.output(GPIO_PORT, GPIO.LOW)
bus = smbus.SMBus(1) # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
now = datetime.datetime.now() date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
AC_STATUS = acPower(PLD_PIN) VOLTAGE,VOLTAGE_STATUS = readVoltage(bus,VOLTAGE_STATUS_LOW) CAPACITY,CAPACITY_STATUS = readCapacity(bus,CAPACITY_STATUS_LOW)
print ("******************************************") print(date_time + " : System Startup") print(date_time + " : AC Power " + AC_STATUS) print(date_time + " : Battery Capacity " + CAPACITY_STATUS + " (" + str(round(CAPACITY,2)) + "%)" ) print(date_time + " : Battery Voltage " + VOLTAGE_STATUS + " (" + str(round(VOLTAGE,2)) + "V)" ) if ( TEST_MODE == "Y" ):
print(date_time + " : Started in Test Mode, No actual shutdown will occur, program will exit after shutdown triggered.")
while True:
- print ("******************************************")
now = datetime.datetime.now()
date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
AC_STATUS = acPower(PLD_PIN) VOLTAGE,VOLTAGE_STATUS = readVoltage(bus,VOLTAGE_STATUS_LOW) CAPACITY,CAPACITY_STATUS = readCapacity(bus,CAPACITY_STATUS_LOW)
if ( SHUTDOWN_TRIGGER == "AC Power" or SHUTDOWN_TRIGGER == "All" ):
if ( AC_STATUS == "GOOD" ):
if ( POWER_LOST_SEC > 0 ):
print(date_time + " : AC Power Restored - Shutdown cancelled")
POWER_LOST_SEC = 0
else:
if ( TEST_MODE == "Y" ):
print(date_time + " : AC Power " + AC_STATUS)
elif ( AC_STATUS == "LOST" ):
if ( TEST_MODE == "Y" ):
print(date_time + " : AC Power " + AC_STATUS)
POWER_LOST_SEC += 1
if ( POWER_LOST_SEC == 1 ):
print(date_time + " : AC Power Lost - Shutdown in " + str(AC_LOSS_WAIT_MINUTES) + " minutes")
soundBuzzer(BUZZER_ON,BUZZER_SECONDS,BUZZER_PIN)
if ( POWER_LOST_SEC >= WAIT_SECONDS ):
print(date_time + " : Shutdown in progress due to AC power loss")
soundBuzzer(BUZZER_ON,BUZZER_SECONDS,BUZZER_PIN)
if ( TEST_MODE != "Y" ):
safeShutdown(GPIO_PORT)
else:
print(date_time + " : Started in Test Mode, No actual shutdown will occur, program will exit.")
break
if ( SHUTDOWN_TRIGGER == "Capacity" or SHUTDOWN_TRIGGER == "All" ):
if ( CAPACITY > CAPACITY_THRESHOLD ):
if ( TEST_MODE == "Y" ):
print(date_time + " : Battery Capacity " + CAPACITY_STATUS + " (" + str(round(CAPACITY,2)) + "%)" )
elif ( CAPACITY <= CAPACITY_THRESHOLD ):
print(date_time + " : Battery Capacity " + CAPACITY_STATUS + " (" + str(round(CAPACITY,2)) + "%)" )
print(date_time + " : Shutdown in progress due to battery Capacity below " + str(CAPACITY_THRESHOLD) + "% threshold")
soundBuzzer(BUZZER_ON,BUZZER_SECONDS,BUZZER_PIN)
if ( TEST_MODE != "Y" ):
safeShutdown(GPIO_PORT)
else:
print(date_time + " : Started in Test Mode, No actual shutdown will occur, program will exit.")
break
if ( SHUTDOWN_TRIGGER == "Voltage" or SHUTDOWN_TRIGGER == "All" ):
if ( VOLTAGE > VOLTAGE_THRESHOLD ):
if ( TEST_MODE == "Y" ):
print(date_time + " : Battery Voltage " + VOLTAGE_STATUS + " (" + str(round(VOLTAGE,2)) + "V)" )
elif ( VOLTAGE <= VOLTAGE_THRESHOLD ):
print(date_time + " : Battery Voltage " + VOLTAGE_STATUS + " (" + str(round(VOLTAGE,2)) + "V)" )
print(date_time + " : Shutdown in progress due to battery Voltage below " + str(VOLTAGE_THRESHOLD) + "V threshold")
soundBuzzer(BUZZER_ON,BUZZER_SECONDS,BUZZER_PIN)
if ( TEST_MODE != "Y" ):
safeShutdown(GPIO_PORT)
else:
print(date_time + " : Started in Test Mode, No actual shutdown will occur, program will exit.")
break
time.sleep(10)
GPIO.cleanup()