Read the English manual: https://github.com/rcdrones/UPSPACK_V3/blob/master/README_en.md
# clone the github repo
sudo apt update && sudo apt install git -y
cd ~
git clone https://github.com/rcdrones/UPSPACK_V3.git
cd ~/UPSPACK_V3/UPS_GUI_py
# try to run the UPS GUI demo python script:
python UPS_GUI_demo.py
Traceback (most recent call last):
File "/home/pi/UPSPACK_V3/UPS_GUI_py/UPS_GUI_demo.py", line 3, in <module>
from upspackv2 import *
File "/home/pi/UPSPACK_V3/UPS_GUI_py/upspackv2.py", line 3, in <module>
import serial
ModuleNotFoundError: No module named 'serial'
# install missing dependencies
sudo apt install python3-pip
pip install pyserial
# you have to create an environment
cd ~/UPSPACK_V3/
python3 -m venv venv
source venv/bin/activate
# install the dependencies again
pip install pyserial
pip install RPi.GPIO
sudo apt install python3-tk -y
cd ~/UPSPACK_V3/UPS_GUI_py/
python UPS_GUI_demo.py
# the next errors show up if you don't have /dev/serial0 -> ttyAMA0 set up. Let's set up the rest.
sudo nano /boot/firmware/config.txt
# at the bottom of the file, under the [all] section, add:
enable_uart=1
dtoverlay=disable-bt
# save, exit, and sudo reboot
ls -l /dev
# serial0 -> ttyAMA0 should exist
cd ~/UPSPACK_V3
source venv/bin/activate
cd UPS_GUI_py
python UPS_GUI_demo.py
# if you see error "no $DISPLAY environment variable", then exit, and use -X
ssh -X pi@server
cd ~/UPSPACK_V3
source venv/bin/activate
cd UPS_GUI_py
python UPS_GUI_demo.py
# if no window pops up, set up the hardware first:
See: https://github.com/rcdrones/UPSPACK_V3/blob/master/README_en.md
ls -l /dev and check that you can see serial0
# ssh -X to the server again
cd UPSPACK_V3
source venv/bin/activate
cd UPS_GUI_py
python UPS_GUI_demo.py
It works! Disconnect the power to the UPS and you will see the Power disconnected message change.
If you see error: “could not open port /dev/ttyAMA0: [Errno 13] Permission denied: ‘/dev/ttyAMA0′”, then you need to run the program with sudo and specify with -E the environment which we’ve already been using:
cd ~/UPSPACK_V3/UPS_GUI_py/
sudo -E ~/UPSPACK_V3/venv/bin/python UPS_GUI_demo.py
With power disconnected:
So, at least their app works, which you can then re-write for your own needs.
Reading the voltage on a BP-TCA-12/2510 SN battery, rated at 4V 4400mAh 17.6 Wh:
Raspberry Pi Zero W (wifi) with no other load
92% battery at 7:00 hrs
44% battery at 23:00 hrs
draw was about 48% in 16 hours, or 3% battery use per hour.
Battery level read from the UPS:
Script to run it: run.sh, upspackv2.py and monitor.py
In crontab -e:
@reboot INFLUXDB_TOKEN="secret-token-here...==" /home/pi/voltagemonitor/run.sh
The main run.sh file:
#!/bin/bash
# make sure to edit your users's ~/.bashrc and add:
# export INFLUXDB_TOKEN="token-here"
# and
# source ~/.bashrc
cd /home/pi/voltagemonitor
sudo INFLUXDB_TOKEN=$INFLUXDB_TOKEN venv/bin/python monitor.py > log.txt 2>&1
A copy of this modified library in /home/pi/voltagemonitor/upspackv2.py :
#!/usr/bin/python3
import serial
import re
import RPi.GPIO as GPIO
import os,sys
import time
from datetime import datetime
def print2(*args, **kwargs):
formatted_time = datetime.now().strftime("%Y.%m.%d_%H:%M:%S:%f")[:-3]
print(f"{formatted_time}", *args, **kwargs)
class UPS2:
def __init__(self,port):
self.ser = serial.Serial(port,9600)
def get_data(self,nums):
try:
while True:
if not self.ser or not self.ser.is_open:
print2("Error: Serial connection lost. Attempting to reconnect...",file=sys.stderr)
try:
self.ser.open()
except serial.SerialException as e:
print2(f"Error: Failed to reopen serial connection: {e}",file=sys.stderr)
return None
self.count = self.ser.inWaiting()
if self.count !=0:
self.recv = self.ser.read(nums)
return self.recv
else:
time.sleep(0.9)
except (OSError, serial.SerialException) as e:
print2(f"Error: Failed to read from serial, error is: {e}",file=sys.stderr)
return None
def decode_uart(self):
self.uart_string = self.get_data(100)
if self.uart_string is None:
print2("Error: uart_string is None, there is no data from serial",file=sys.stderr)
return None
else:
print2("*** begin uart_string ***",file=sys.stderr)
print2(self.uart_string,file=sys.stderr)
print2("*** end uart_string ***",file=sys.stderr)
if b'\x00' in self.uart_string:
print2("Error: Received a bad serial string with null bytes.",file=sys.stderr)
return None
else:
# Continue processing if the string is valid
self.data = self.uart_string.decode('ascii','ignore')
# ensure the data is not null
if not self.data:
print2("No data available to search",file=sys.stderr)
return None
#self.pattern = r'\$ (.*?) \$'
self.pattern = r'SmartUPS.*?\$'
self.result = re.findall(self.pattern,self.data,re.S)
if not self.result:
print2("Error: Pattern not found in serial data",file=sys.stderr)
return None
self.tmp = self.result[0]
self.pattern = r'SmartUPS (.*?),'
self.version = re.findall(self.pattern,self.tmp)
self.pattern = r',Vin (.*?),'
self.vin = re.findall(self.pattern,self.tmp)
self.pattern = r'BATCAP (.*?),'
self.batcap = re.findall(self.pattern,self.tmp)
#self.pattern = r',Vout (.*)'
self.pattern = r',Vout (\d+) \$'
self.vout = re.findall(self.pattern,self.tmp)
print2("Parsed: " + self.version[0]+" "+self.vin[0]+" "+self.batcap[0]+" "+self.vout[0],file=sys.stderr)
return self.version[0],self.vin[0],self.batcap[0],self.vout[0]
def cleanup(self):
"""Closes the serial connection."""
if self.ser and self.ser.is_open:
self.ser.close()
print2("Serial connection closed.",file=sys.stderr)
class UPS2_IO:
def __init__(self,bcm_io=18):
self.shutdown_check_pin = bcm_io
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.shutdown_check_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(self.shutdown_check_pin, GPIO.FALLING, callback= self.RPI_shutdown,bouncetime=1000)
def RPI_shutdown(self,channel):
print2("detect bat LOW, system will shutdown in 10s!")
for i in range(10,0,-1):
print2(i,end = ' ',flush=True)
time.sleep(1)
print2("\nexecute System shudown!\n")
os.system("sudo shutdown -t now")
sys.exit()
def cleanup():
print2("clean up GPIO.")
GPIO.cleanup()
if __name__ == "__main__":
print2("This is UPS v2 class file")
The main monitor.py
#!/usr/bin/python3
from upspackv2 import *
import re
import serial
import time
from datetime import datetime
from influx_writer import InfluxDBWriter
ups = UPS2("/dev/ttyAMA0")
def print2(*args, **kwargs):
# Get the current time formatted with milliseconds
formatted_time = datetime.now().strftime("%Y.%m.%d_%H:%M:%S:%f")[:-3]
# Print the timestamp followed by the original arguments
print(f"{formatted_time}", *args, **kwargs)
def reflash_data():
try:
serialread = ups.decode_uart()
if serialread is None:
print2("got bad serial data from the UPS, skipping...",file=sys.stderr)
else:
version, vin, batcap, vout = serialread
# Check if any of the returned values are None
if version is None or vin is None or batcap is None or vout is None:
print2("Failed to retrieve valid data from UPS.",file=sys.stderr)
else:
# NG = not good, no power in, only UPS power
if vin == "NG":
ext_pwr="n"
else:
ext_pwr="y"
batcap_int = int(batcap)
if batcap_int< 30:
if batcap_int == 1:
cur_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
stop_time = "\nHalt time :"+cur_time
with open("log.txt","a+") as f:
f.write(stop_time)
#os.system("sudo shutdown -t now")
print2("would shut down now", file=sys.stderr)
sys.exit()
print2("SENDING: ups,device=sensorpi pwr=" + ext_pwr + " batpct=" + str(batcap) + " mV=" +str(vout) + " " + str(time.time_ns()),file=sys.stderr)
try:
influx_writer.write_data("ups,device=sensorpi pwr=" + ext_pwr + " batpct=" + str(batcap) + " mV=" +str(vout) + " " + str(time.time_ns()))
except Exception as e:
print2("Failed to write data to InfluxDB:", e, file=sys.stderr)
except serial.SerialException as e:
print2("Exception while reading from uart serial",file=sys.stderr)
finally:
ups.cleanup()
# set: export INFLUXDB_TOKEN="your_influxdb_token_here" in ~/.bashrc
token = os.getenv("INFLUXDB_TOKEN")
if not token:
raise EnvironmentError("INFLUXDB_TOKEN environment variable not set.")
org = "set-your-org-here"
bucket = "set-your-bucket-name-here"
url = "https://us-east-1-1.aws.cloud2.influxdata.com"
influx_writer = InfluxDBWriter(token, org, bucket, url)
while True:
reflash_data()
time.sleep(30)
Example output in log.txt, which you can tail -F log.txt
...
024.11.27_00:51:45:163 b'$ SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$ SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$ '
2024.11.27_00:51:45:164 *** end uart_string ***
2024.11.27_00:51:45:165 Parsed: V3.2P GOOD 100 5250
2024.11.27_00:51:45:166 SENDING: ups,device=sensorpi pwr=y batpct=100 mV=5250 1732686705166640778
2024.11.27_00:51:45:239 Serial connection closed.
2024.11.27_00:52:15:240 Error: Serial connection lost. Attempting to reconnect...
2024.11.27_00:52:17:354 *** begin uart_string ***
2024.11.27_00:52:17:355 b'$ SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$ SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$ '
2024.11.27_00:52:17:355 *** end uart_string ***
2024.11.27_00:52:17:357 Parsed: V3.2P GOOD 100 5250
2024.11.27_00:52:17:357 SENDING: ups,device=sensorpi pwr=y batpct=100 mV=5250 1732686737357896676
2024.11.27_00:52:17:424 Serial connection closed.
2024.11.27_00:52:47:426 Error: Serial connection lost. Attempting to reconnect...
2024.11.27_00:52:48:529 Error: Failed to read from serial, error is: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
2024.11.27_00:52:48:530 Error: uart_string is None, there is no data from serial
2024.11.27_00:52:48:531 got bad serial data from the UPS, skipping...
2024.11.27_00:52:48:532 Serial connection closed.
2024.11.27_00:53:18:533 Error: Serial connection lost. Attempting to reconnect...
2024.11.27_00:53:20:730 *** begin uart_string ***
2024.11.27_00:53:20:731 b'$ SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$ SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$ '
2024.11.27_00:53:20:732 *** end uart_string ***
2024.11.27_00:53:20:733 Parsed: V3.2P GOOD 100 5250
2024.11.27_00:53:20:734 SENDING: ups,device=sensorpi pwr=y batpct=100 mV=5250 1732686800734177740
2024.11.27_00:53:20:806 Serial connection closed.
2024.11.27_00:53:50:807 Error: Serial connection lost. Attempting to reconnect...
2024.11.27_00:53:53:193 *** begin uart_string ***
2024.11.27_00:53:53:194 b'PS V3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x00\x00\x00\x00\xe0\x80x>\x0f\xf8\xf8<\xc0\x80\xf8\xf8<\x0fx>>\xff\xc0'
2024.11.27_00:53:53:194 *** end uart_string ***
2024.11.27_00:53:53:195 Error: Received a bad serial string with null bytes.
2024.11.27_00:53:53:195 got bad serial data from the UPS, skipping...
2024.11.27_00:53:53:196 Serial connection closed.
2024.11.27_00:54:23:366 Error: Serial connection lost. Attempting to reconnect...
2024.11.27_00:54:27:174 *** begin uart_string ***
2024.11.27_00:54:27:175 b'$\x00 SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$ SmartUPS V3.2P,Vin GOOD,BATCAP 100,Vout 5250 $\n$'
2024.11.27_00:54:27:176 *** end uart_string ***
2024.11.27_00:54:27:176 Error: Received a bad serial string with null bytes.
...