This repository has been archived by the owner on Nov 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbatteryModule.py
108 lines (93 loc) · 3.99 KB
/
batteryModule.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
from serialUtility import Ser, inst
from math import log
class batteryModule:
def __init__(self):
# volt & temp
self.cellVolt = [0] * 6
self.cellBalance = [False] * 6
self.moduleVolt = 0
self.temperature = [0] * 2
self.moduleAddr = 0
# status
self.alerts = 0
self.faults = 0
self.COVFaults = 0
self.CUVFaults = 0
self.isBalanced = False
def update(self):
# read both status and temp&volt
self.readStatus()
self.balance()
self.readValues()
def readStatus(self):
# read any alerts or faults
addr = bytes([self.moduleAddr << 1])
command = addr + bytes([inst['REG_ALERT_STATUS']]) + bytes([0x04])
response = Ser.query(command, 7, False)
self.alerts = response[3]
self.faults = response[4]
self.COVFaults = response[5]
self.CUVFaults = response[6]
def readValues(self):
# read temperature and voltage
print("Module %d readValue()" % self.moduleAddr)
addr = bytes([self.moduleAddr << 1])
# ADC Auto mode, read every ADC input we can (Both Temps, Pack, 6 cells)
command = addr + bytes([inst['REG_ADC_CTRL']]) + bytes([0b00111101])
Ser.query(command, 3, True)
# enable temperature measurement VSS pins
command = addr + bytes([inst['REG_IO_CTRL']]) + bytes([0b00000011])
Ser.query(command, 3, True)
# start all ADC conversions
command = addr + bytes([inst['REG_ADC_CONV']]) + bytes([0b1])
Ser.query(command, 3, True)
# start reading registers at the module voltage registers
# read 18 bytes
# 2 bytes each for ModuleV, CellV 1-6, Temp1, Temp2
command = addr + bytes([inst['REG_GPAI']]) + bytes([0x12])
response = Ser.query(command, 22, False)
# response bytes composed of:
# 0: address
# 1: command
# 2: requested number of values
# 3-20: value
# 21: CRC check
CRC = int.from_bytes(Ser.genCRC(response[:-1]), 'big')
if response[0] == (self.moduleAddr << 1) and response[1] == inst['REG_GPAI'] and response[2] == 0x12 and response[21] == CRC:
self.moduleVolt = (response[3]*256+response[4])*0.002034609
for i in range(6):
self.cellVolt[i] = (response[5+i*2]*256 + response[6+i*2])*0.000381493
self.temperature[0] = self.calTemp(response[17], response[18])
self.temperature[1] = self.calTemp(response[19], response[20])
print("Successfully read temp and volt")
return True
print("Invalid response for readValue()")
return False
def calTemp(self, left:int, right:int):
# calculate the temperature given reading from readValues()
# use steinhart/hart equation
temp = (1.78/((left*256+right+2)/33046)-3.57)*1000
result = 1/(0.0007610373573+(0.0002729524832*log(temp))+log(temp)**3*0.0000001022822735)
return result - 273.15
def balance(self):
# balance point
# should call balance before readValue()
balanceVolt = 3.9
balanceState = 0
addr = bytes([self.moduleAddr << 1])
# resets balance time and should be done before setting balance resistors again
command = addr + bytes([inst['REG_BAL_CTRL']]) + bytes([0])
Ser.query(command, 4, True)
# check necessarity for balancing
for i in range(6):
if self.cellVolt[i] > balanceVolt:
self.cellBalance[i] = True
balanceState |= (1 << i)
if False in self.cellBalance:
command = addr + bytes([inst['REG_BAL_TIME']]) + bytes([0x82])
# balance for 2min, the last byte is time
Ser.query(command, 4, True)
command = addr + bytes([inst['REG_BAL_CTRL']]) + bytes([balanceState])
# write balance state to register
Ser.query(command, 4, True)
print("Balancing cell")