forked from geekworm-com/UPS2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ups2_control.py
443 lines (385 loc) · 18 KB
/
ups2_control.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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
import smbus
import time
import struct
import logging
import datetime
logger = logging.getLogger(__name__)
BQ27441_I2C_ADDRESS = 0x55 # Default I2C address of the BQ27441-G1A
BQ27441_UNSEAL_KEY = 0x8000 # Secret code to unseal the BQ27441-G1A
BQ27441_DEVICE_ID = 0x0421 # Default device ID
#
# Standard Command 标准命令
#
# The fuel gauge uses a series of 2-byte standard commands to enable system
# reading and writing of battery information. Each command has an associated
# sequential command-code pair.
BQ27441_COMMAND_CONTROL = 0x00
BQ27441_COMMAND_TEMP = 0x02
BQ27441_COMMAND_VOLTAGE = 0x04
BQ27441_COMMAND_FLAGS = 0x06
BQ27441_COMMAND_NOM_CAPACITY = 0x08
BQ27441_COMMAND_AVAIL_CAPACITY = 0x0A
BQ27441_COMMAND_REM_CAPACITY = 0x0C
BQ27441_COMMAND_FULL_CAPACITY = 0x0E
BQ27441_COMMAND_AVG_CURRENT = 0x10
BQ27441_COMMAND_STDBY_CURRENT = 0x12
BQ27441_COMMAND_MAX_CURRENT = 0x14
BQ27441_COMMAND_AVG_POWER = 0x18
BQ27441_COMMAND_SOC = 0x1C
BQ27441_COMMAND_INT_TEMP = 0x1E
BQ27441_COMMAND_SOH = 0x20
BQ27441_COMMAND_REM_CAP_UNFL = 0x28
BQ27441_COMMAND_REM_CAP_FIL = 0x2A
BQ27441_COMMAND_FULL_CAP_UNFL = 0x2C
BQ27441_COMMAND_FULL_CAP_FIL = 0x2E
BQ27441_COMMAND_SOC_UNFL = 0x30
BQ27441_COMMAND_TRUEREM_CAPACITY = 0x6A
#
# Control Sub-commands 控制子命令
#
# Issuing a Control(self) command requires a subsequent 2-byte subcommand. These
# additional bytes specify the particular control function desired. The
# Control(self) command allows the system to control specific features of the fuel
# gauge during normal operation and additional features when the device is in
# different access modes.
BQ27441_CONTROL_STATUS = 0x00
BQ27441_CONTROL_DEVICE_TYPE = 0x01
BQ27441_CONTROL_FW_VERSION = 0x02
BQ27441_CONTROL_DM_CODE = 0x04
BQ27441_CONTROL_PREV_MACWRITE = 0x07
BQ27441_CONTROL_CHEM_ID = 0x08
BQ27441_CONTROL_BAT_INSERT = 0x0C
BQ27441_CONTROL_BAT_REMOVE = 0x0D
BQ27441_CONTROL_SET_HIBERNATE = 0x11
BQ27441_CONTROL_CLEAR_HIBERNATE = 0x12
BQ27441_CONTROL_SET_CFGUPDATE = 0x13
BQ27441_CONTROL_SHUTDOWN_ENABLE = 0x1B
BQ27441_CONTROL_SHUTDOWN = 0x1C
BQ27441_CONTROL_SEALED = 0x20
BQ27441_CONTROL_PULSE_SOC_INT = 0x23
BQ27441_CONTROL_RESET = 0x41
BQ27441_CONTROL_SOFT_RESET = 0x42
BQ27441_CONTROL_EXIT_CFGUPDATE = 0x43
BQ27441_CONTROL_EXIT_RESIM = 0x44
#
# Control Status Word - Bit Definitions 控制状态词
#
# Bit positions for the 16-bit data of CONTROL_STATUS.
# CONTROL_STATUS instructs the fuel gauge to return status information to
# Control(self) addresses 0x00 and 0x01. The read-only status word contains status
# bits that are set or cleared either automatically as conditions warrant or
# through using specified subcommands.
BQ27441_STATUS_SHUTDOWNEN = 1 << 15
BQ27441_STATUS_WDRESET = 1 << 14
BQ27441_STATUS_SS = 1 << 13
BQ27441_STATUS_CALMODE = 1 << 12
BQ27441_STATUS_CCA = 1 << 11
BQ27441_STATUS_BCA = 1 << 10
BQ27441_STATUS_QMAX_UP = 1 << 9
BQ27441_STATUS_RES_UP = 1 << 8
BQ27441_STATUS_INITCOMP = 1 << 7
BQ27441_STATUS_HIBERNATE = 1 << 6
BQ27441_STATUS_SLEEP = 1 << 4
BQ27441_STATUS_LDMD = 1 << 3
BQ27441_STATUS_RUP_DIS = 1 << 2
BQ27441_STATUS_VOK = 1 << 1
#
# Flag Command - Bit Definitions 标志命令
#
# Bit positions for the 16-bit data of Flags(self)
# This read-word function returns the contents of the fuel gauging status
# register, depicting the current operating status.
BQ27441_FLAG_OT = 1 << 15
BQ27441_FLAG_UT = 1 << 14
BQ27441_FLAG_FC = 1 << 9
BQ27441_FLAG_CHG = 1 << 8
BQ27441_FLAG_OCVTAKEN = 1 << 7
BQ27441_FLAG_ITPOR = 1 << 5
BQ27441_FLAG_CFGUPMODE = 1 << 4
BQ27441_FLAG_BAT_DET = 1 << 3
BQ27441_FLAG_SOC1 = 1 << 2
BQ27441_FLAG_SOCF = 1 << 1
BQ27441_FLAG_DSG = 1 << 0
dict_flag = {'OT': BQ27441_FLAG_OT, 'UT': BQ27441_FLAG_UT, 'FC': BQ27441_FLAG_FC, \
'CHG': BQ27441_FLAG_CHG, 'OCVTAKEN': BQ27441_FLAG_OCVTAKEN, 'ITPOR': BQ27441_FLAG_ITPOR, \
'CFGUPMODE': BQ27441_FLAG_CFGUPMODE, 'BAT_DET': BQ27441_FLAG_BAT_DET, \
'SOC1': BQ27441_FLAG_SOC1, 'SOCF': BQ27441_FLAG_SOCF, 'DSG': BQ27441_FLAG_DSG}
#
# Extended Data Commands 控制数据命令
#
# Extended data commands offer additional functionality beyond the standard
# set of commands. They are used in the same manner; however, unlike standard
# commands, extended commands are not limited to 2-byte words.
BQ27441_EXTENDED_OPCONFIG = 0x3A # OpConfig(self)
BQ27441_EXTENDED_CAPACITY = 0x3C # DesignCapacity(self)
BQ27441_EXTENDED_DATACLASS = 0x3E # DataClass(self)
BQ27441_EXTENDED_DATABLOCK = 0x3F # DataBlock(self)
BQ27441_EXTENDED_BLOCKDATA = 0x40 # BlockData(self)
BQ27441_EXTENDED_CHECKSUM = 0x60 # BlockDataCheckSum(self)
BQ27441_EXTENDED_CONTROL = 0x61 # BlockDataControl(self)
#
# Configuration Class, Subclass ID's # 配置类,子类的ID
#
# To access a subclass of the extended data, set the DataClass(self) function
# with one of these values.
# Configuration Classes
BQ27441_ID_SAFETY = 2 # Safety
BQ27441_ID_CHG_TERMINATION = 36 # Charge Termination
BQ27441_ID_CONFIG_DATA = 48 # Data
BQ27441_ID_DISCHARGE = 49 # Discharge
BQ27441_ID_REGISTERS = 64 # Registers
BQ27441_ID_POWER = 68 # Power
# Gas Gauging Classes
BQ27441_ID_IT_CFG = 80 # IT Cfg
BQ27441_ID_CURRENT_THRESH = 81 # Current Thresholds
BQ27441_ID_STATE = 82 # State
# Ra Tables Classes
BQ27441_ID_R_A_RAM = 89 # R_a RAM
# Calibration Classes
BQ27441_ID_CALIB_DATA = 104 # Data
BQ27441_ID_CC_CAL = 105 # CC Cal
BQ27441_ID_CURRENT = 107 # Current
# Security Classes
BQ27441_ID_CODES = 112 # Codes
#
# OpConfig Register - Bit Definitions 选项寄存器
#
# Bit positions of the OpConfig Register
BQ27441_OPCONFIG_BIE = 1 << 13
BQ27441_OPCONFIG_BI_PU_EN = 1 << 12
BQ27441_OPCONFIG_GPIOPOL = 1 << 11
BQ27441_OPCONFIG_SLEEP = 1 << 5
BQ27441_OPCONFIG_RMFCC = 1 << 4
BQ27441_OPCONFIG_BATLOWEN = 1 << 2
BQ27441_OPCONFIG_TEMPS = 1 << 0
class UPS2Control():
# this is the sample python 3 code that how to view the batery information
# BQ27441 Python Library by ugeek (sp@geekworm.com)
#
# General Constants 常量
#
def __init__(self, battery_cap=2500):
# Change 2500 to your battery capacity (mAh)
self.MY_BATTERY_CAP = battery_cap
self.bus=smbus.SMBus(1)
time.sleep(1)
self.writeCap(self.MY_BATTERY_CAP)
def readControlWord(self, cmd):
self.bus.write_word_data(BQ27441_I2C_ADDRESS, 0x00, cmd)
# up is below
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x00,cmd & 0x00FF)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x01,cmd >> 8)
return self.bus.read_word_data(BQ27441_I2C_ADDRESS, 0x00)
def executeControlWord(self, cmd):
self.bus.write_word_data(BQ27441_I2C_ADDRESS, 0x00, cmd)
# # subCommandMSB = (cmd >> 8);
# # subCommandLSB = (cmd & 0x00FF)
# # command = [subCommandLSB, subCommandMSB]
# #print "Control Word [0x%04x]" % ((subCommandLSB << 8) | subCommandMSB)
# new_cmd = (cmd >> 8) | ((cmd & 0x00FF) << 8)
# print "Execute Control Word [0x%04x]" % cmd
# bus.write_word_data(BQ27441_I2C_ADDRESS,0x00,0x0001)
# bus.write_word_data(BQ27441_I2C_ADDRESS,0x00,new_cmd)
# #bus.write_word_data(BQ27441_I2C_ADDRESS,0x00,(subCommandLSB << 8) | subCommandMSB)
# #bus.write_byte_data(BQ27441_I2C_ADDRESS,0x00,subCommandLSB)
# #bus.write_byte_data(BQ27441_I2C_ADDRESS,0x01,subCommandMSB)
def writeExtendedCommand(self, addr, val):
# print "Write Extend Command addr[0x%02x]" % addr ,", val[0x%02x]" % val
self.bus.write_byte_data(BQ27441_I2C_ADDRESS, addr, val)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0,addr)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,1,val)
# bus.write_byte(BQ27441_I2C_ADDRESS,val)
# bus.write_byte_data(BQ27441_I2C_ADDRESS, 0, addr)
# bus.write_byte_data(BQ27441_I2C_ADDRESS, 1, val)
def device_id(self):
logger.debug('get device_id')
return self.readControlWord(BQ27441_CONTROL_DEVICE_TYPE)
def dm_id(self):
logger.debug('get dm_id')
return self.readControlWord(BQ27441_CONTROL_DM_CODE)
def fw_version(self):
logger.debug('get fw_version')
return self.readControlWord(BQ27441_CONTROL_FW_VERSION)
def chem_id(self):
logger.debug('get chem_id')
return self.readControlWord(BQ27441_CONTROL_CHEM_ID)
def controlStatus(self):
logger.debug('get controlStatus')
status = self.readControlWord(BQ27441_CONTROL_STATUS)
return status
def softReset(self):
logger.debug('perform soft_reset')
self.executeControlWord(BQ27441_CONTROL_SOFT_RESET)
def setConfigUpdate(self):
logger.debug("Set Config Update...")
self.executeControlWord(BQ27441_CONTROL_SET_CFGUPDATE)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x00,0x13)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x01,0x00)
def exitConfigUpdate(self):
logger.debug("Exit Config Update...")
self.executeControlWord(BQ27441_CONTROL_EXIT_CFGUPDATE)
def seal(self):
logger.debug("seal")
self.executeControlWord(BQ27441_CONTROL_SEALED)
# executeControlWord(BQ27441_CONTROL_SEALED)
def unseal(self):
logger.debug("unseal")
self.executeControlWord(BQ27441_UNSEAL_KEY)
self.executeControlWord(BQ27441_UNSEAL_KEY)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x00,0x00)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x01,0x80)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x00,0x00)
# bus.write_byte_data(BQ27441_I2C_ADDRESS,0x01,0x80)
def writeCap(self, cap):
self.unseal()
self.setConfigUpdate()
self.writeExtendedCommand(BQ27441_EXTENDED_CONTROL, 0x00)
self.writeExtendedCommand(BQ27441_EXTENDED_DATACLASS, 0x52)
self.writeExtendedCommand(BQ27441_EXTENDED_DATABLOCK, 0x00)
block = self.bus.read_i2c_block_data(BQ27441_I2C_ADDRESS, BQ27441_EXTENDED_BLOCKDATA, 32)
block = list(struct.unpack('32B', bytearray(block)[0:32]))
block[0x0a] = cap >> 8
block[0x0b] = cap & 0xFF
new_checksum = ~sum(block) & 0xFF
self.bus.write_byte_data(BQ27441_I2C_ADDRESS, 0x4a, cap >> 8) # writing new capacity
self.bus.write_byte_data(BQ27441_I2C_ADDRESS, 0x4b, cap & 0xFF) # writing new capacity
time.sleep(1)
# write checksum
self.bus.write_byte_data(BQ27441_I2C_ADDRESS, 0x60, new_checksum) # trying to write on BlockDataChecksum(self)
time.sleep(1)
self.softReset()
self.exitConfigUpdate()
self.seal()
def availCap(self):
val = self.bus.read_i2c_block_data(BQ27441_I2C_ADDRESS, BQ27441_COMMAND_AVAIL_CAPACITY, 2)
new_val = struct.unpack('H', bytearray(val)[0:2])
return new_val[0]
def desCap(self):
val = self.bus.read_i2c_block_data(BQ27441_I2C_ADDRESS, BQ27441_EXTENDED_CAPACITY, 2)
new_val = struct.unpack('H', bytearray(val)[0:2])
return new_val[0]
def voltage(self):
val = self.bus.read_i2c_block_data(BQ27441_I2C_ADDRESS, BQ27441_COMMAND_VOLTAGE, 2)
new_val = struct.unpack('H', bytearray(val)[0:2])
return new_val[0]
def soc(self):
val = self.bus.read_i2c_block_data(BQ27441_I2C_ADDRESS, BQ27441_COMMAND_SOC, 2)
new_val = struct.unpack('H', bytearray(val)[0:2])
return new_val[0]
def soh(self):
val = self.get_status_u(BQ27441_COMMAND_SOH)
return val & 0x00FF
def get_status_u(self,reg):
status = self.bus.read_i2c_block_data(BQ27441_I2C_ADDRESS, reg, 2)
(new_status,) = struct.unpack('H', bytearray(status)[0:2])
return new_status
def get_status(self, reg):
status = self.bus.read_i2c_block_data(BQ27441_I2C_ADDRESS, reg, 2)
(new_status,) = struct.unpack('h', bytearray(status)[0:2])
return new_status
# def unseal():
# executeControlWord(BQ27441_UNSEAL_KEY)
# executeControlWord(BQ27441_UNSEAL_KEY)
def get_all_info(self):
info = {}
info["status_control"] = self.get_status_u(BQ27441_COMMAND_CONTROL)
info["status_temp"] = self.get_status_u(BQ27441_COMMAND_TEMP)
info["status_voltage"] = self.get_status_u(BQ27441_COMMAND_VOLTAGE)
info["status_nom_capacity"] = self.get_status_u(BQ27441_COMMAND_NOM_CAPACITY)
info["status_avail_capacity"] = self.get_status_u(BQ27441_COMMAND_AVAIL_CAPACITY)
info["status_rem_capacity"] = self.get_status_u(BQ27441_COMMAND_REM_CAPACITY)
info["status_full_capacity"] = self.get_status_u(BQ27441_COMMAND_FULL_CAPACITY)
info["status_avg_current"] = self.get_status(BQ27441_COMMAND_AVG_CURRENT)
info["status_stdby_current"] = self.get_status(BQ27441_COMMAND_STDBY_CURRENT)
info["status_max_current"] = self.get_status(BQ27441_COMMAND_MAX_CURRENT)
info["status_avg_power"] = self.get_status(BQ27441_COMMAND_AVG_POWER)
info["status_soc"] = self.get_status_u(BQ27441_COMMAND_SOC)
info["status_int_temp"] = self.get_status_u(BQ27441_COMMAND_INT_TEMP)
info["status_soh"] = self.soh()
info["status_rem_cap_unfil"] = self.get_status_u(BQ27441_COMMAND_REM_CAP_UNFL)
info["status_rem_cap_fil"] = self.get_status_u(BQ27441_COMMAND_REM_CAP_FIL)
info["status_full_cap_unfil"] = self.get_status_u(BQ27441_COMMAND_FULL_CAP_UNFL)
info["status_full_cap_fil"] = self.get_status_u(BQ27441_COMMAND_FULL_CAP_FIL)
info["status_soc_unfl"] = self.get_status_u(BQ27441_COMMAND_SOC_UNFL)
info["status_truerem_capacity"] = self.get_status_u(BQ27441_COMMAND_TRUEREM_CAPACITY)
return info
def get_basic_info(self):
basic_info = {}
basic_info["battery_soc"] = self.get_status_u(BQ27441_COMMAND_SOC)
basic_info["battery_voltage"] = self.get_status_u(BQ27441_COMMAND_VOLTAGE)
basic_info["battery_current"] = self.get_status(BQ27441_COMMAND_AVG_CURRENT)
basic_info["battery_soh"] = self.soh()
return basic_info
def print_basic_info(self):
print(self.get_basic_info_string)
def get_basic_info_string(self):
# print "[Basic Info]"
# print "1.Voltage:", float(battery_voltage) / 1000, "V"
# print "2.SOC: ", battery_soc , "%"
# print "3.Current:", float(battery_current) / 1000 , "A"
# print "4.SOH: ", battery_soh, "%"
basic_info = self.get_basic_info()
battery_voltage = basic_info['battery_voltage']
battery_current = basic_info['battery_current']
battery_soc = basic_info['battery_soc']
print("Voltage:{voltage:.3f}V - Current:{current:.3f}A - SOC:{soc:.2f}%" .format(
voltage=float(battery_voltage) / 1000,
current=float(battery_current) / 1000,
soc=battery_soc))
def print_all_info(self):
all_infos = self.get_all_info()
print("Control: {}".format(all_infos['status_control']))
print("Temperature: {} K".format(all_infos['status_temp']/10.))
print("Voltage: {} mV".format(all_infos['status_voltage']))
print("Nominal Available Capacity: {} mAh".format(all_infos['status_nom_capacity']))
print("Full Available Capacity: {} mAh".format(all_infos['status_avail_capacity']))
print("Remaining Capacity: {} mAh".format(all_infos['status_rem_capacity']))
print("Full Charge Capacity: {} mAh".format(all_infos['status_full_capacity']))
print("Average Current: {} mA".format(all_infos['status_avg_current']))
print("Standby Current: {} mA".format(all_infos['status_stdby_current']))
print("MaxLoad Current: {} mA".format(all_infos['status_max_current']))
print("Average Power: {} mW".format(all_infos['status_avg_power']))
print("State Of Charge: {} %".format(all_infos['status_soc']))
print("Internal Temperature: {} K".format(all_infos['status_int_temp'] /10.))
print("State Of Health: {} %".format(all_infos['status_soh']))
print("Remaining Capacity Unfiltered: {} mAh".format(all_infos['status_rem_cap_unfil']))
print("Remaining Capacity Filtered: {} mAh".format(all_infos['status_rem_cap_fil']))
print("Full Charge Capacity Unfiltered:{} mAh".format(all_infos['status_full_cap_unfil']))
print("Full Charge Capacity Filtered: {} mAh".format(all_infos['status_full_cap_fil']))
print("State Of Charge Unfiltered: {} %".format(all_infos['status_soc_unfl']))
print("True Remaining Capacity: {} mAh".format(all_infos['status_truerem_capacity']))
def log_all_info(self, filename = 'ups2_log.txt'):
all_info = self.get_all_info()
now_time = datetime.datetime.now()
time_str = datetime.datetime.strftime(now_time, '%Y-%m-%d %H:%M:%S')
data = time_str + ',' + \
str(all_info['status_control']) + ',' + \
str(all_info['status_temp']) + ',' + \
str(all_info['status_voltage']) + ',' + \
str(all_info['status_nom_capacity']) + ',' + \
str(all_info['status_avail_capacity']) + ',' + \
str(all_info['status_rem_capacity']) + ',' + \
str(all_info['status_full_capacity']) + ',' + \
str(all_info['status_avg_current']) + ',' + \
str(all_info['status_stdby_current']) + ',' + \
str(all_info['status_max_current']) + ',' + \
str(all_info['status_avg_power']) + ',' + \
str(all_info['status_soc']) + ',' + \
str(all_info['status_int_temp']) + ',' + \
str(all_info['status_soh']) + ',' + \
str(all_info['status_rem_cap_unfil']) + ',' + \
str(all_info['status_rem_cap_fil']) + ',' + \
str(all_info['status_full_cap_unfil']) + ',' + \
str(all_info['status_full_cap_fil']) + ',' + \
str(all_info['status_soc_unfl']) + ',' + \
str(all_info['status_truerem_capacity']) + '\n'
print(data)
with open(filename, 'a+') as f:
f.write(data)
if __name__ == "__main__":
ups = UPS2Control(8000)
ups.print_basic_info()
ups.print_all_info()
all_infos = ups.get_all_info()
for k,v in all_infos.items():
print(k, v)