113 lines
4.1 KiB
Python
Executable File
113 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python
|
|
import struct
|
|
import RPi.GPIO as GPIO
|
|
import smbus
|
|
import time
|
|
import subprocess
|
|
import sys
|
|
import signal
|
|
|
|
GPIO_BUZZER_PIN=20 # controls the buzzer
|
|
GPIO_ON_BATT=6 # set when running on battery
|
|
GPIO_PWR_CTRL=26 # toggle this for 2 seconds before shutdown, or else the board won't respond to the first power-on event
|
|
GPIO_BTN=5 # button signal: 500ms pulse for reboot, stays high for shutdown
|
|
GPIO_BOOT=12 # enable the button
|
|
I2C_ADDR=0x36 # the chip that reads battery charge state is here
|
|
CRIT_BATT=20 # shut off at this percentage
|
|
|
|
class X728UPS:
|
|
|
|
def __init__(self):
|
|
GPIO.setmode(GPIO.BCM)
|
|
GPIO.setup(GPIO_BUZZER_PIN, GPIO.OUT, initial=GPIO.LOW)
|
|
GPIO.setup(GPIO_PWR_CTRL, GPIO.OUT, initial=GPIO.LOW)
|
|
GPIO.setup(GPIO_ON_BATT, GPIO.IN)
|
|
GPIO.setup(GPIO_BOOT, GPIO.OUT, initial=GPIO.HIGH)
|
|
GPIO.setup(GPIO_BTN, GPIO.IN)
|
|
GPIO.add_event_detect(GPIO_BTN, GPIO.RISING, callback=lambda ch: self.ButtonCallback())
|
|
signal.signal(signal.SIGUSR1, self.Shutdown)
|
|
self.buzzer_on=True
|
|
signal.signal(signal.SIGUSR2, self.ToggleBuzzer)
|
|
|
|
def ToggleBuzzer(self, signum, frame):
|
|
self.buzzer_on=not self.buzzer_on
|
|
signal.signal(signal.SIGUSR2, self.ToggleBuzzer)
|
|
|
|
def Shutdown(self, signum, frame):
|
|
print("shutdown requested by SIGUSR1", flush=True)
|
|
GPIO.output(GPIO_PWR_CTRL, GPIO.HIGH)
|
|
time.sleep(2)
|
|
GPIO.output(GPIO_PWR_CTRL, GPIO.LOW)
|
|
subprocess.run(["shutdown", "-h", "now"])
|
|
sys.exit()
|
|
|
|
def ButtonCallback(self):
|
|
if GPIO.input(GPIO_BTN):
|
|
t=0
|
|
while GPIO.input(GPIO_BTN) and t<1000:
|
|
time.sleep(0.01)
|
|
t=t+10
|
|
if t>=200 and t<=600: # nominal 500ms pulse for reboot, or stays high for shutdown until powered off
|
|
print("reboot requested", flush=True)
|
|
time.sleep(1) # so the message above gets logged
|
|
subprocess.run(["shutdown", "-r", "now"])
|
|
sys.exit()
|
|
else:
|
|
print("shutdown requested", flush=True)
|
|
GPIO.output(GPIO_PWR_CTRL, GPIO.HIGH)
|
|
time.sleep(2)
|
|
GPIO.output(GPIO_PWR_CTRL, GPIO.LOW)
|
|
subprocess.run(["shutdown", "-h", "now"])
|
|
sys.exit()
|
|
|
|
def readVoltage(self, bus):
|
|
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
|
|
return voltage
|
|
|
|
def readCapacity(self, bus):
|
|
address = I2C_ADDR
|
|
read = bus.read_word_data(address, 4)
|
|
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
|
|
capacity = swapped/256
|
|
return capacity
|
|
|
|
def main(self):
|
|
bus=smbus.SMBus(1)
|
|
|
|
while True:
|
|
v=self.readVoltage(bus)
|
|
c=self.readCapacity(bus)
|
|
if GPIO.input(GPIO_ON_BATT):
|
|
p="OB"
|
|
else:
|
|
p="OL"
|
|
if c<CRIT_BATT:
|
|
p="%s LB" % p
|
|
p="%s %1.2f %i" % (p, v, c)
|
|
print(p, flush=True)
|
|
if p.find("OB")!=-1: # on battery?
|
|
if p.find("LB")!=-1: # low battery?
|
|
print("low battery shutdown", flush=True)
|
|
if self.buzzer_on:
|
|
GPIO.output(GPIO_BUZZER_PIN, GPIO.HIGH)
|
|
GPIO.output(GPIO_PWR_CTRL, GPIO.HIGH)
|
|
time.sleep(2)
|
|
GPIO.output(GPIO_PWR_CTRL, GPIO.LOW)
|
|
subprocess.run(["shutdown", "-h", "now"])
|
|
while True: # wait forever
|
|
time.sleep(1)
|
|
else: # on battery, but not low
|
|
if self.buzzer_on:
|
|
GPIO.output(GPIO_BUZZER_PIN, GPIO.HIGH)
|
|
time.sleep(.25)
|
|
GPIO.output(GPIO_BUZZER_PIN, GPIO.LOW)
|
|
time.sleep(4.75)
|
|
else: # online
|
|
time.sleep(5)
|
|
|
|
app=X728UPS()
|
|
app.main()
|