-
Notifications
You must be signed in to change notification settings - Fork 2
/
UPS_report.py
201 lines (165 loc) · 9.28 KB
/
UPS_report.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/python
# -*- coding: utf-8 -*-
# adapted from scripts provided at GitHub: Geeekpi/upsplus by nickfox-taterli
# ar - 17-05-2021
import os
import time
from smbus2 import SMBus
from datetime import datetime, timezone
from math import log10, floor
from ina219 import INA219, DeviceRangeError
# Record starting time & format in two styles
StartTime = datetime.now(timezone.utc).astimezone()
TimeStampA = '{:%d-%m-%Y %H:%M:%S}'.format(StartTime)
TimeStampB = '{:%Y-%m-%d_%H:%M:%S}'.format(StartTime)
def round_sig(x, n=3):
if not x: return 0
power = -floor(log10(abs(x))) + (n - 1)
factor = (10 ** power)
return round(x * factor) / factor
DEVICE_BUS = 1
DEVICE_ADDR = 0x17
ina = INA219(0.00725,address=0x40)
ina.configure()
print("*** Data from INA219 at 0x40:")
print("Raspberry Pi supply voltage: %8.3f V" % round_sig(ina.voltage(),n=3))
print("Raspberry Pi current consumption: %8.3f A" % round_sig(ina.current()/1000,n=3))
print("Raspberry Pi power consumption: %8.3f W" % round_sig(ina.power()/1000,n=3))
print()
ina = INA219(0.005,address=0x45)
ina.configure()
print("*** Data from INA219 at 0x45:")
print( "Battery voltage: %8.3f V" % round_sig(ina.voltage(),n=3))
try:
if ina.current() > 0:
print("Battery current (charging): %8.3f A" % round_sig(abs(ina.current()/1000),n=3))
print("Power supplied to the batteries: %8.3f W" % round_sig(ina.power()/1000,n=3))
else:
print("Battery current (discharging): %8.3f A" % round_sig(abs(ina.current()/1000),n=2))
print("Battery power consumption: %8.3f W" % round_sig(ina.power()/1000,n=3))
except DeviceRangeError:
print('Out of Range Warning: BATTERY VOLTAGE EXCEEDING SAFE LIMITS!')
# Keep sampling in case of another type of error
except:
pass
finally:
print()
# Path for parameter files
PATH = str(os.getenv('HOME'))+'/UPS+/'
f = open(PATH+'PowerOffLimit.txt','rt')
line = f.readline()
POWEROFF_LIMIT = line.strip()
#line = f.readline()
#POWERLOSS_TIMER = line.strip()
f.close()
f = open(PATH+'GraceTime.txt','r')
GRACE_TIME = int(f.read())
f.close()
# Force restart (simulate power plug, write the corresponding number of seconds,
# shut down 5 seconds before the end of the countdown, and then turn on at 0 seconds.)
# bus.write_byte_data(DEVICE_ADDR, 0x1A, 0x1E)
# Restore factory settings
# (clear memory, clear learning parameters, can not clear the cumulative running time, used for after-sales purposes.)
# bus.write_byte_data(DEVICE_ADDR, 0x1B, 0x01)
aReceiveBuf = []
aReceiveBuf.append(0x00)
i = 0x01
while i < 0x100:
try:
with SMBus(DEVICE_BUS) as bus:
aReceiveBuf.append(bus.read_byte_data(DEVICE_ADDR, i))
i += 1
except TimeoutError as e:
# print('i=', i, ' - error:', e)
time.sleep(0.1)
print( "*** Remainder of report is based on data collected")
print(("*** by the UPS f/w and read from memory at 0x{:02X}").format(DEVICE_ADDR))
print( "--- except value(s) marked with *")
print()
print("UPS board MCU voltage: %6.3f V" % round_sig((aReceiveBuf[0x02] << 0o10 | aReceiveBuf[0x01])/1000,n=3))
print("Voltage at Pi GPIO header pins: %6.3f V" % round_sig((aReceiveBuf[0x04] << 0o10 | aReceiveBuf[0x03])/1000,n=3))
print("USB type C port input voltage: %6.3f V" % round_sig((aReceiveBuf[0x08] << 0o10 | aReceiveBuf[0x07])/1000,n=3))
print("Micro USB port input voltage: %6.3f V" % round_sig((aReceiveBuf[0x0A] << 0o10 | aReceiveBuf[0x09])/1000,n=3))
print()
# Learned from the battery internal resistance change, the longer the use, the more stable the data:
print("Battery temperature (estimate): %6.d°C" % round_sig(aReceiveBuf[0x0C] << 0o10 | aReceiveBuf[0x0B]))
#print()
print("Automatic detection of battery type (0=yes, 1=no) %6.d" % (aReceiveBuf[0x2A]))
# Fully charged voltage is learned through charging and discharging:
print("Batteries fully charged at (learned value): %6.3f V" % round_sig((aReceiveBuf[0x0E] << 0o10 | aReceiveBuf[0x0D])/1000,n=3))
# This value is inaccurate during charging:
print("Current voltage at battery terminals: %6.3f V" % round_sig((aReceiveBuf[0x06] << 0o10 | aReceiveBuf[0x05])/1000,n=3))
# Voltage below which UPS shuts down the Pi & powers off to conserve battery capacity
print("* My power-off voltage limit is set at: %6.3f V" % round_sig(float(POWEROFF_LIMIT)/1000,n=3))
# Fully discharged voltage is learned through charging and discharging (a.k.a. empty voltage):
print("Batteries fully discharged at (partially learned): %6.3f V" % round_sig((aReceiveBuf[0x10] << 0o10 | aReceiveBuf[0x0F])/1000,n=3))
# The deep discharge limit value is stored in memory at 0x11-0x12 by upsPlus.py:
# DISCHARGE_LIMIT (a.k.a. protection voltage)
DISCHARGE_LIMIT=(aReceiveBuf[0x12] << 0o10 | aReceiveBuf[0x11])/1000
print("Battery deep discharge limit is set at: %6.3f V" % round_sig(DISCHARGE_LIMIT,n=3))
# At least one complete charge and discharge cycle needs to pass before this value is meaningful:
print("Remaining battery capacity: %8.d %%" % (aReceiveBuf[0x14] << 0o10 | aReceiveBuf[0x13]))
# For a few seconds all blue charging level LEDs are off
# and only the batteries deliver power to the Pi
# as sampling of battery characteristics takes place.
# The interval between sampling events is normally 2 minutes.
print("Battery sampling ('blue LEDs off') interval: %8.d min" % (aReceiveBuf[0x16] << 0o10 | aReceiveBuf[0x15]))
print()
if aReceiveBuf[0x17] == 1:
print("Current power state: normal")
else:
print("Current power state: other")
print()
print(("{:<60s}").format("UPS power off/on timer registers 0x18 and 0x1A"))
print(("{:<60s}").format("and register 0x19 will be set to values selected"))
print(("{:<60s}").format("for a power failure event by the upsPlus.py script"))
print(("{:<60s}").format("immediately before halting the Raspberry Pi."))
print()
if (aReceiveBuf[0x08] << 0o10 | aReceiveBuf[0x07]) > 4000:
print('External power is connected to the USB type C input.\n')
print(("{:<60s}").format("Should the external power be interrupted long enough"))
print(("{:<60s}").format("to cause the battery voltage to drop below "+str(max(DISCHARGE_LIMIT,round_sig(float(POWEROFF_LIMIT)/1000,n=3)))+" V,"))
print(("{:<60s}").format("or remain interrupted for more than "+str(GRACE_TIME)+" min,"))
print(("{:<60s}").format("the Pi will be halted & the UPS will power it down.\n"))
elif (aReceiveBuf[0x0A] << 0o10 | aReceiveBuf[0x09]) > 4000:
print('External power is connected to the micro USB input.\n')
print(("{:<60s}").format("Should the external power be interrupted long enough"))
print(("{:<60s}").format("to cause the battery voltage to drop below "+str(max(DISCHARGE_LIMIT,round_sig(float(POWEROFF_LIMIT)/1000,n=3)))+" V,"))
print(("{:<60s}").format("or remain interrupted for more than "+str(GRACE_TIME)+" min,"))
print(("{:<60s}").format("the Pi will be halted & the UPS will power it down.\n"))
else:
# Not charging.
print("*** EXTERNAL POWER LOST! RUNNING ON BATTERY POWER!")
if (GRACE_TIME==0):
print("*** A shutdown command will be sent to the OS/Pi!")
else:
print("*** Grace time till shutdown: %d min" % GRACE_TIME)
print()
print(("{:<60s}").format("UPS power control registers: "
+ "0x18=" + str(aReceiveBuf[0x18])
+ " / 0x19=" + str(aReceiveBuf[0x19])
+ " / 0x1A=" + str(aReceiveBuf[0x1A])))
if aReceiveBuf[0x18] == 0:
print('0x18 - Power off (no restart) - timer not set.')
else:
print("0x18 - Power off (no restart) - timer set to: %3.d sec" % (aReceiveBuf[0x18]))
if aReceiveBuf[0x19] == 0x01:
print(("{:<60s}").format("0x19 - Automatic restart upon return of external power"))
else:
print(("{:<60s}").format("0x19 - No automatic restart upon return of external power"))
if aReceiveBuf[0x1A] == 0:
print('0x1A - Power off (with restart) - timer not set.')
else:
print("0x1A - Power off (with restart) - timer set to: %3.d sec" % (aReceiveBuf[0x1A]))
print()
print("Accumulated running time: %8.d min" % round((aReceiveBuf[0x1F] << 0o30 | aReceiveBuf[0x1E] << 0o20 | aReceiveBuf[0x1D] << 0o10 | aReceiveBuf[0x1C])/60))
print("Accumulated charging time: %8.d min" % round((aReceiveBuf[0x23] << 0o30 | aReceiveBuf[0x22] << 0o20 | aReceiveBuf[0x21] << 0o10 | aReceiveBuf[0x20])/60))
print("Current up time: %8.d min" % round((aReceiveBuf[0x27] << 0o30 | aReceiveBuf[0x26] << 0o20 | aReceiveBuf[0x25] << 0o10 | aReceiveBuf[0x24])/60))
print(("{:<s}{:>2d}").format("F/W version:",(aReceiveBuf[0x29] << 0o10 | aReceiveBuf[0x28])))
# Serial Number
UID0 = "%08X" % (aReceiveBuf[0xF3] << 0o30 | aReceiveBuf[0xF2] << 0o20 | aReceiveBuf[0xF1] << 0o10 | aReceiveBuf[0xF0])
UID1 = "%08X" % (aReceiveBuf[0xF7] << 0o30 | aReceiveBuf[0xF6] << 0o20 | aReceiveBuf[0xF5] << 0o10 | aReceiveBuf[0xF4])
UID2 = "%08X" % (aReceiveBuf[0xFB] << 0o30 | aReceiveBuf[0xFA] << 0o20 | aReceiveBuf[0xF9] << 0o10 | aReceiveBuf[0xF8])
print("Serial Number: " + UID0 + "-" + UID1 + "-" + UID2 )
print()
#EOF