Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dbus-serialbattery/battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ def __init__(self, port: str, baud: int, address: str):
self.soc_calc_reset_start_time: int = None
self.soc_calc: float = None # save soc_calc to preserve on restart
self.soc: float = None
self.soh: float = None
self.charge_fet: bool = None
self.discharge_fet: bool = None
self.balance_fet: bool = None
Expand Down Expand Up @@ -950,6 +951,7 @@ def manage_charge_voltage_limit(self) -> None:
+ (f" • penalty_sum: {safe_number_format(penalty_sum, '{:.3f}')} V" if utils.CVL_CONTROLLER_MODE == 1 else "")
+ "\n"
+ f"soc: {self.soc}% • soc_calc: {self.soc_calc}%\n"
+ f"soh: {self.soh}%\n"
+ f"current: {safe_number_format(self.current, '{:.2f}')}A"
+ (f" • current_calc: {safe_number_format(self.current_calc, '{:.2f}')} A\n" if self.current_calc is not None else "\n")
+ f"current_time: {current_time}\n"
Expand Down Expand Up @@ -2137,7 +2139,7 @@ def log_settings(self) -> None:
logger.info(f"Battery {self.type} connected to dbus from {self.port}")
logger.info("========== Settings ==========")
logger.info(
f"> Connection voltage: {self.voltage} V | Current: {self.current_calc} A | SoC: {self.soc}%"
f"> Connection voltage: {self.voltage} V | Current: {self.current_calc} A | SoC: {self.soc}% | SoH: {self.soh}%"
+ (f" | SoC calc: {self.soc_calc:.0f}%" if utils.SOC_CALCULATION else "")
)
logger.info(f"> Cell count: {self.cell_count} | Cells populated: {cell_counter}")
Expand Down
81 changes: 76 additions & 5 deletions dbus-serialbattery/bms/jkbms_pb.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,35 @@ def get_settings(self):
CapBatCell = unpack_from("<i", status_data, 130)[0] / 1000
SCPDelay = unpack_from("<i", status_data, 134)[0]
StartBalVol = unpack_from("<i", status_data, 138)[0] / 1000 # Start Balance Voltage
DevAddr = unpack_from("<i", status_data, 270)[0] # Device Addr
TIMPDischarge = unpack_from("<i", status_data, 274)[0]

CtrlBitMask = unpack_from("<H", status_data, 282)[0] # Controls
# Bit0: Heating enabled
HeatEN = 0x01 & CtrlBitMask
# Bit1: Disable Temp.-Sensor
DisTempSens = 0x01 & (CtrlBitMask >> 1)
# Bit2: GPS Heartbeat
GPSHeartbeat = 0x01 & (CtrlBitMask >> 2)
# Bit3: Port Switch 1:RS485 0: CAN
PortSwitch = 0x01 & (CtrlBitMask >> 3)
# Bit4: LCD Always ON
LCDAlwaysOn = 0x1 & (CtrlBitMask >> 4)
# Bit5: Special Charger
SpecialCharger = 0x1 & (CtrlBitMask >> 5)
# Bit6: Smart Sleep
SmartSleep = 0x1 & (CtrlBitMask >> 6)

TMPBatOTA = unpack_from("<h", status_data, 284)[0] # int 8
TMPBatOTAR = unpack_from("<h", status_data, 285)[0] # int 8
TIMSmartSleep = unpack_from("<h", status_data, 286)[0] # uint 8

# balancer enabled
self.balance_fet = True if BalanEN != 0 else False

# heating enabled
# self.control_allow_heating = True if HeatEN != 0 else False

# count of all cells in pack
self.cell_count = CellCount

Expand Down Expand Up @@ -144,21 +169,57 @@ def get_settings(self):
logger.debug("CapBatCell: " + str(CapBatCell))
logger.debug("SCPDelay: " + str(SCPDelay))
logger.debug("StartBalVol: " + str(StartBalVol))
logger.debug("DevAddr: " + str(DevAddr))
logger.debug("TIMPDischarge: " + str(TIMPDischarge))
logger.debug("CtrlBitMask: " + str(CtrlBitMask))
logger.debug("HeatEN: " + str(HeatEN))
logger.debug("DisTempSens: " + str(DisTempSens))
logger.debug("GPSHeartbeat: " + str(GPSHeartbeat))
logger.debug("PortSwitch: " + str(PortSwitch))
logger.debug("LCDAlwaysOn: " + str(LCDAlwaysOn))
logger.debug("SpecialCharger: " + str(SpecialCharger))
logger.debug("SmartSleep: " + str(SmartSleep))
logger.debug("TMPBatOTA: " + str(TMPBatOTA))
logger.debug("TMPBatOTAR: " + str(TMPBatOTAR))
logger.debug("TIMSmartSleep: " + str(TIMSmartSleep))

status_data = self.read_serial_data_jkbms_pb(self.command_about, 300)
serial_nr = status_data[86:101].decode("utf-8")
vendor_id = status_data[6:18].decode("utf-8")
hw_version = (status_data[22:26].decode("utf-8") + " / " + status_data[30:35].decode("utf-8")).replace("\x00", "")
sw_version = status_data[30:34].decode("utf-8") # will be overridden
# vendor_version start 0: 16 chars
# hw_version start 16: 8 chars
# sw_version start 24: 8 chars
# oddruntim start 32: 1 UINT32
# pwr_on_time start 36: 1 UINT32

vendor_id = status_data[6:21].decode("utf-8").split("\x00", 1)[0] # 16 chars
hw_version = status_data[22:29].decode("utf-8").split("\x00", 1)[0] # 8 chars
sw_version = status_data[30:37].decode("utf-8").split("\x00", 1)[0] # 8 chars
bms_version = hw_version + " / " + sw_version

ODDRunTime = unpack_from("<I", status_data, 38)[0] # 1 unit32 # runtime of the system in seconds
PWROnTimes = unpack_from("<I", status_data, 42)[0] # 1 unit32 # how many startups the system has done
serial_nr = status_data[46:61].decode("utf-8").split("\x00", 1)[0] # serialnumber 16 chars max
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May it be, that the protocol differs from hardware version to hardware version? I cannot believe, that this data is provided twice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serialnumber is provided two times in the memory.
I have tested it with Hardware Version 15 and 19.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

usrData = status_data[102:117].decode("utf-8").split("\x00", 1)[0] # usrData 16 chars max
pin = status_data[118:133].decode("utf-8").split("\x00", 1)[0] # pin 16 chars max
usrData2 = status_data[134:149].decode("utf-8").split("\x00", 1)[0] # usrData 2 16 chars max
ble_id = serial_nr + "-" + str(DevAddr)

self.unique_identifier_tmp = serial_nr
self.version = sw_version
self.hardware_version = hw_version
self.hardware_version = bms_version

logger.debug("Serial Nr: " + str(serial_nr))
logger.debug("Ble Id: " + str(ble_id))
logger.debug("Vendor ID: " + str(vendor_id))
logger.debug("HW Version: " + str(hw_version))
logger.debug("SW Version: " + str(sw_version))
logger.debug("BMS Version: " + str(bms_version))
logger.debug("User data: " + str(usrData))
logger.debug("User data 2: " + str(usrData2))
logger.debug("pin: " + str(pin))
logger.debug("PWROnTimes: " + str(PWROnTimes))
logger.debug(
"ODDRunTime: " + str(ODDRunTime) + "s; " + str(ODDRunTime / 60) + "m; " + str(ODDRunTime / 60 / 60) + "h; " + str(ODDRunTime / 60 / 60 / 24) + "d"
)

# init the cell array
for _ in range(self.cell_count):
Expand Down Expand Up @@ -215,6 +276,9 @@ def read_status_data(self):
# SOC
self.soc = unpack_from("<B", status_data, 173)[0]

# SOH
self.soh = unpack_from("<B", status_data, 190)[0]

# cycles
self.history.charge_cycles = unpack_from("<i", status_data, 182)[0]

Expand All @@ -228,9 +292,16 @@ def read_status_data(self):
bal = unpack_from("<B", status_data, 172)[0]
charge = unpack_from("<B", status_data, 198)[0]
discharge = unpack_from("<B", status_data, 199)[0]
# heat = unpack_from("<B", status_data, 215)[0]

self.charge_fet = 1 if charge != 0 else 0
self.discharge_fet = 1 if discharge != 0 else 0
self.balancing = 1 if bal != 0 else 0
# self.heating = 1 if heat != 0 else 0

# HeatCurrent mA
# self.heat_current = unpack_from("<H", status_data, 236)[0]
# self.heat_power = 0 if self.heating != 1 else self.heat_current * self.voltage

# show wich cells are balancing
if self.get_min_cell() is not None and self.get_max_cell() is not None:
Expand Down
Loading