Skip to content

Commit

Permalink
Daly_Can set SOC support (#150)
Browse files Browse the repository at this point in the history
* catch can errors (cable disconnections)

* manual and auto SOC reset by CAN
  • Loading branch information
transistorgit authored Jan 6, 2025
1 parent 4e6e6e6 commit 86ed2e6
Showing 1 changed file with 70 additions and 23 deletions.
93 changes: 70 additions & 23 deletions dbus-serialbattery/bms/daly_can.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
bytearray_to_string,
INVERT_CURRENT_MEASUREMENT,
logger,
AUTO_RESET_SOC,
MAX_BATTERY_CHARGE_CURRENT,
MAX_BATTERY_DISCHARGE_CURRENT,
MIN_CELL_VOLTAGE,
)
from struct import unpack_from
from struct import unpack_from, pack_into
from time import time
import sys
from can import Message
from can import Message, CanOperationError
from time import sleep


Expand All @@ -33,9 +34,15 @@ def __init__(self, port, baud, address):
self.cell_max_voltage = None
self.cell_min_no = None
self.cell_max_no = None
# self.cell_count = 0 # init here if testing with bms without battery connected
self.poll_interval = 1000
self.poll_step = 0
self.type = self.BATTERYTYPE
self.has_settings = True
self.reset_soc = 0
self.soc_to_set = None
self.last_charge_mode = self.charge_mode

self.can_bus = None
self.device_address = int.from_bytes(address, byteorder="big") if address is not None else 0x01
self.error_active = False
Expand Down Expand Up @@ -142,38 +149,46 @@ def refresh_data(self):
# call all functions that will refresh the battery data.
# This will be called for every iteration (1 second)
# Return True if success, False for failure
self.reset_soc = self.soc if self.soc else 0

self.request_daly_can()
sleep(0.1)

result = self.read_daly_can()
# check if connection success
self.write_soc()
if AUTO_RESET_SOC:
self.update_soc_on_bms()

return result

def request_daly_can(self):
data = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00")

if self.can_transport_interface.can_bus is None:
raise RuntimeError("Can Interface not initialised")
raise RuntimeError("CAN Interface not initialised")

message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_SOC][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_MINMAX_CELL_VOLTS][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_MINMAX_TEMP][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_FET][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_STATUS][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_CELL_VOLTS][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
# unused
# message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_TEMP][0] & 0xffff00ff) | (self.device_address << 8), data=data)
# self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_CELL_BALANCE][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_ALARM][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
try:
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_SOC][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_MINMAX_CELL_VOLTS][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_MINMAX_TEMP][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_FET][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_STATUS][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_CELL_VOLTS][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
# unused
# message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_TEMP][0] & 0xffff00ff) | (self.device_address << 8), data=data)
# self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_CELL_BALANCE][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
message = Message(arbitration_id=(self.CAN_FRAMES[self.COMMAND_ALARM][0] & 0xFFFF00FF) | (self.device_address << 8), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)
except CanOperationError:
logger.error("CAN Bus Error while sending data. Check cabeling")

def read_daly_can(self):
try:
Expand Down Expand Up @@ -430,3 +445,35 @@ def read_daly_can(self):
line = exception_traceback.tb_lineno
logger.error(f"Exception occurred: {repr(exception_object)} of type {exception_type} in {file} line #{line}")
return False

def reset_soc_callback(self, path, value):
if value is None:
return False

if value < 0 or value > 100:
return False

self.reset_soc = value
self.soc_to_set = value
return True

def write_soc(self):
if self.soc_to_set is None:
return False

data = bytearray(8)
pack_into(">Hxxxxxx", data, 0, self.soc_to_set * 10)

message = Message(arbitration_id=(0x161E0080 | (self.device_address << 8)), data=data)
self.can_transport_interface.can_bus.send(message, timeout=0.2)

logger.info(f"write soc {self.soc_to_set}%")
self.soc_to_set = None # Reset value, so we will set it only once

def update_soc_on_bms(self):
if self.last_charge_mode is not None and self.charge_mode is not None:
if not self.last_charge_mode.startswith("Float") and self.charge_mode.startswith("Float"):
# we just entered float mode, so the battery must be full
self.soc_to_set = 100
self.write_soc()
self.last_charge_mode = self.charge_mode

0 comments on commit 86ed2e6

Please sign in to comment.