Files
x728ups/x728ups
2025-03-28 12:39:52 -07:00

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()