Skip to content

Commit

Permalink
Fix #397 wrong cell voltage reading daly bms (#484)
Browse files Browse the repository at this point in the history
* fix daly rs485 communication for cell voltages

* fix read_serialport_data length calculation, fix daly cell voltage packet offset calculation, add checksum check also. Harden general package validity check.

* revert read_serialport_data() to the original state

* copy read_serialport_data() to daly.py

* fix missing bracket

* black reformatting

---------

Co-authored-by: transitorgit <[email protected]>
Co-authored-by: Louis Van Der Walt <[email protected]>
  • Loading branch information
3 people authored May 2, 2023
1 parent 716063e commit da4bc76
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 28 deletions.
7 changes: 3 additions & 4 deletions etc/dbus-serialbattery/battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ def to_temp(self, sensor: int, value: float) -> None:
self.temp1 = min(max(value, -20), 100)
if sensor == 2:
self.temp2 = min(max(value, -20), 100)
if sensor == 'mos':
self.temp_mos = min(max(value, -20), 100
if sensor == "mos":
self.temp_mos = min(max(value, -20), 100)

def manage_charge_voltage(self) -> None:
"""
Expand Down Expand Up @@ -574,7 +574,7 @@ def get_max_temp(self) -> Union[float, None]:
return self.extract_from_temp_values(
extractor=lambda temp1, temp2: max(temp1, temp2)
)

def get_mos_temp(self) -> Union[float, None]:
if self.temp_mos is not None:
return self.temp_mos
Expand All @@ -594,7 +594,6 @@ def log_cell_data(self) -> bool:
return True

def log_settings(self) -> None:

logger.info(f"Battery {self.type} connected to dbus from {self.port}")
logger.info("=== Settings ===")
cell_counter = len(self.cells)
Expand Down
121 changes: 106 additions & 15 deletions etc/dbus-serialbattery/daly.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self, port, baud, address):

# command bytes [StartFlag=A5][Address=40][Command=94][DataLength=8][8x zero bytes][checksum]
command_base = b"\xA5\x40\x94\x08\x00\x00\x00\x00\x00\x00\x00\x00\x81"
cellvolt_buffer = b"\xA5\x40\x94\x08\x00\x00\x00\x00\x00\x00\x00\x00\x82\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00"
cellvolt_buffer = b"\xA5\x40\x94\x08\x00\x00\x00\x00\x00\x00\x00\x00\x82"
command_soc = b"\x90"
command_minmax_cell_volts = b"\x91"
command_minmax_temp = b"\x92"
Expand All @@ -31,7 +31,7 @@ def __init__(self, port, baud, address):
command_cell_balance = b"\x97"
command_alarm = b"\x98"
BATTERYTYPE = "Daly"
LENGTH_CHECK = 4
LENGTH_CHECK = 1
LENGTH_POS = 3
CURRENT_ZERO_CONSTANT = 30000
TEMP_ZERO_CONSTANT = 40
Expand Down Expand Up @@ -246,13 +246,16 @@ def read_cells_volts(self, ser):
buffer[1] = self.command_address[0] # Always serial 40 or 80
buffer[2] = self.command_cell_volts[0]

maxFrame = int(self.cell_count / 3) + 1
if (int(self.cell_count) % 3) == 0:
maxFrame = int(self.cell_count / 3)
else:
maxFrame = int(self.cell_count / 3) + 1
lenFixed = (
maxFrame * 12
) # 0xA5, 0x01, 0x95, 0x08 + 1 byte frame + 6 byte data + 1byte reserved
maxFrame * 13
) # 0xA5, 0x01, 0x95, 0x08 + 1 byte frame + 6 byte data + 1byte reserved + chksum

cells_volts_data = read_serialport_data(
ser, buffer, self.LENGTH_POS, self.LENGTH_CHECK, lenFixed
cells_volts_data = self.read_serialport_data(
ser, buffer, self.LENGTH_POS, 0, lenFixed
)
if cells_volts_data is False:
logger.warning("read_cells_volts")
Expand All @@ -269,14 +272,24 @@ def read_cells_volts(self, ser):
for idx in range(self.cell_count):
self.cells.append(Cell(True))

# logger.warning("data " + bytes(cells_volts_data).hex())

while (
bufIdx < len(cells_volts_data) - 4
): # we at least need 4 bytes to extract the identifiers
b1, b2, b3, b4 = unpack_from(">BBBB", cells_volts_data, bufIdx)
if b1 == 0xA5 and b2 == 0x01 and b3 == 0x95 and b4 == 0x08:
frame, frameCell[0], frameCell[1], frameCell[2] = unpack_from(
">Bhhh", cells_volts_data, bufIdx + 4
)
(
frame,
frameCell[0],
frameCell[1],
frameCell[2],
_,
chk,
) = unpack_from(">BhhhBB", cells_volts_data, bufIdx + 4)
if sum(cells_volts_data[bufIdx : bufIdx + 12]) & 0xFF != chk:
logger.warning("bad cell voltages checksum")
return False
for idx in range(3):
cellnum = (
(frame - 1) * 3
Expand All @@ -287,9 +300,9 @@ def read_cells_volts(self, ser):
self.cells[cellnum].voltage = (
None if cellVoltage < lowMin else cellVoltage
)
bufIdx += 10 # BBBBBhhh -> 11 byte
bufIdx += 1

bufIdx += 13 # BBBBBhhhBB -> 13 byte
else:
logger.warning("bad cell voltages header")
return True

def read_cell_voltage_range_data(self, ser):
Expand Down Expand Up @@ -350,7 +363,7 @@ def generate_command(self, command):
return buffer

def read_serial_data_daly(self, ser, command):
data = read_serialport_data(
data = self.read_serialport_data(
ser, self.generate_command(command), self.LENGTH_POS, self.LENGTH_CHECK
)
if data is False:
Expand All @@ -359,8 +372,86 @@ def read_serial_data_daly(self, ser, command):
start, flag, command_ret, length = unpack_from("BBBB", data)
checksum = sum(data[:-1]) & 0xFF

if start == 165 and length == 8 and checksum == data[12]:
if start == 165 and length == 8 and len(data) > 12 and checksum == data[12]:
return data[4 : length + 4]
else:
logger.error(">>> ERROR: Incorrect Reply")
return False

# Read data from previously openned serial port
def read_serialport_data(
self,
ser,
command,
length_pos,
length_check,
length_fixed=None,
length_size=None,
):
try:
ser.flushOutput()
ser.flushInput()
ser.write(command)

length_byte_size = 1
if length_size is not None:
if length_size.upper() == "H":
length_byte_size = 2
elif length_size.upper() == "I" or length_size.upper() == "L":
length_byte_size = 4

count = 0
toread = ser.inWaiting()

while toread < (length_pos + length_byte_size):
sleep(0.005)
toread = ser.inWaiting()
count += 1
if count > 50:
logger.error(">>> ERROR: No reply - returning")
return False

# logger.info('serial data toread ' + str(toread))
res = ser.read(toread)
if length_fixed is not None:
length = length_fixed
else:
if len(res) < (length_pos + length_byte_size):
logger.error(
">>> ERROR: No reply - returning [len:" + str(len(res)) + "]"
)
return False
length_size = length_size if length_size is not None else "B"
length = unpack_from(">" + length_size, res, length_pos)[0]

# logger.info('serial data length ' + str(length))

count = 0
data = bytearray(res)

packetlen = (
length_fixed
if length_fixed is not None
else length_pos + length_byte_size + length + length_check
)
while len(data) < packetlen:
res = ser.read(packetlen - len(data))
data.extend(res)
# logger.info('serial data length ' + str(len(data)))
sleep(0.005)
count += 1
if count > 150:
logger.error(
">>> ERROR: No reply - returning [len:"
+ str(len(data))
+ "/"
+ str(length + length_check)
+ "]"
)
return False

return data

except Exception as e:
logger.error(e)
return False
5 changes: 2 additions & 3 deletions etc/dbus-serialbattery/dbushelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def setup_vedbus(self):
# Create TimeToSoC items
for num in TIME_TO_SOC_POINTS:
self._dbusservice.add_path("/TimeToSoC/" + str(num), None, writeable=True)
#Create TimeToGO item
# Create TimeToGO item
self._dbusservice.add_path("/TimeToGo", None, writeable=True)

logger.info(f"publish config values = {PUBLISH_CONFIG_VALUES}")
Expand Down Expand Up @@ -338,7 +338,6 @@ def publish_battery(self, loop):
loop.quit()

def publish_dbus(self):

# Update SOC, DC and System items
self._dbusservice["/System/NrOfCellsPerBattery"] = self.battery.cell_count
self._dbusservice["/Soc"] = round(self.battery.soc, 2)
Expand Down Expand Up @@ -488,7 +487,7 @@ def publish_dbus(self):
if self.battery.current
else None
)

# Update TimeToGo
self._dbusservice["/TimeToGo"] = (
self.battery.get_timetosoc(SOC_LOW_WARNING, crntPrctPerSec)
Expand Down
4 changes: 2 additions & 2 deletions etc/dbus-serialbattery/jkbms.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ def read_status_data(self):
temp2 = unpack_from(">H", self.get_data(status_data, b"\x82", offset, 2))[0]
self.to_temp(1, temp1 if temp1 < 99 else (100 - temp1))
self.to_temp(2, temp2 if temp2 < 99 else (100 - temp2))

# MOSFET temperature
offset = cellbyte_count + 3
temp_mos = unpack_from(">H", self.get_data(status_data, b"\x80", offset, 2))[0]
self.to_temp('mos', temp_mos if temp_mos < 99 else (100 - temp_mos))
self.to_temp("mos", temp_mos if temp_mos < 99 else (100 - temp_mos))

offset = cellbyte_count + 12
voltage = unpack_from(">H", self.get_data(status_data, b"\x83", offset, 2))[0]
Expand Down
1 change: 0 additions & 1 deletion etc/dbus-serialbattery/minimalmodbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -1812,7 +1812,6 @@ def _extract_payload(
)

if mode == MODE_ASCII:

# Validate the ASCII header and footer.
if response[_BYTEPOSITION_FOR_ASCII_HEADER] != _ASCII_HEADER:
raise InvalidResponseError(
Expand Down
1 change: 0 additions & 1 deletion etc/dbus-serialbattery/util_max17853.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,6 @@ def v_cell_d(self):
b_lim = False

for index, v in enumerate(V_Cells):

if v > 3.55:
b_lim = True
if v > vc_max:
Expand Down
3 changes: 1 addition & 2 deletions etc/dbus-serialbattery/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ def calcLinearRelationship(inValue, inArray, outArray):
if inArray[0] > inArray[-1]: # change compare-direction in array
return calcLinearRelationship(inValue, inArray[::-1], outArray[::-1])
else:

# Handle out of bounds
if inValue <= inArray[0]:
return outArray[0]
Expand Down Expand Up @@ -321,7 +320,7 @@ def open_serial_port(port, baud):
return ser


# Read data from previously openned serial port
# Read data from previously opened serial port
def read_serialport_data(
ser: serial.Serial,
command,
Expand Down

0 comments on commit da4bc76

Please sign in to comment.