diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index b0cfa10d..7bdcec5b 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -2,7 +2,7 @@ from battery import Protection, Battery, Cell from utils import is_bit_set, read_serial_data, logger import utils -from struct import unpack_from +from struct import unpack_from, pack import struct # Protocol registers @@ -68,8 +68,10 @@ REG_CELL_V_DELAYS = 0x3D REG_CHGOC_DELAYS = 0x3E REG_DSGOC_DELAYS = 0x3F -REG_GPSOFF = 0x40 -REG_GPSOFF_TIME = 0x41 +# Cut-off voltage turns off GPS protection board +REG_GPS_OFF = 0x40 +# Cut-off voltage delay for GPS protection board +REG_GPS_OFF_TIME = 0x41 REG_CAP_90 = 0x42 REG_CAP_70 = 0x43 REG_CAP_50 = 0x44 @@ -141,6 +143,27 @@ CMD_EXIT_FACTORY_MODE = b"\x00\x00" CMD_EXIT_AND_SAVE_FACTORY_MODE = b"\x28\x28" +# Weak current switch function +FUNC_SW_EN = 0x0001 # bit 0 +# Load lock function used to disconnect the load when short circuit is required to recover +FUNC_LOAD_EN = 0x0002 # bit 1 +# Enable balancer function +FUNC_BALANCE_EN = 0x0004 # bit 2 +# Charge balance, only turn on balance when charging +FUNC_BALANCE_CHARGING_ONLY = 0x0008 # bit 3 +# LED power indicator function +FUNC_LED = 0x0010 # bit 4 +# Compatible with LED modes +FUNC_LED_NUM = 0x0020 # bit 5 +# With history recording +FUNC_RTC = 0x0040 # bit 6 +# whether it is necessary to set the range when it is currently used for FCC update +FUNC_EDV = 0x0080 # bit 7 +# Additional GPS protection board is connected +FUNC_GPS_EN = 0x0100 # bit 8 +# Enable onboard buzzer / GPS protection board buzzer? +FUNC_BUZZER_EN = 0x0200 # bit 9 + def checksum(payload): return (0x10000 - sum(payload)) % 0x10000 @@ -217,6 +240,9 @@ def __init__(self, port, baud, address): self.soc_to_set = None self.factory_mode = False self.writable = False + self.trigger_force_disable_discharge = None + self.trigger_force_disable_charge = None + self.trigger_disable_balancer = None self.cycle_capacity = None # degree_sign = u'\N{DEGREE SIGN}' @@ -267,6 +293,9 @@ def get_settings(self): self.max_battery_discharge_current = float( unpack_from(">h", discharge_over_current)[0] / -100.0 ) + func_config = self.read_serial_data_llt(readCmd(REG_FUNC_CONFIG)) + if func_config: + self.func_config = func_config return True @@ -292,7 +321,119 @@ def write_soc(self): pack_voltage = struct.pack(">H", int(self.voltage * 10)) self.read_serial_data_llt(writeCmd(REG_CAP_100, pack_voltage)) + def force_charging_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_force_disable_charge = False + return True + + if value == 1: + self.trigger_force_disable_charge = True + return True + + return False + + def force_discharging_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_force_disable_discharge = False + return True + + if value == 1: + self.trigger_force_disable_discharge = True + return True + + return False + + def write_charge_discharge_mos(self): + if ( + self.trigger_force_disable_charge is None + and self.trigger_force_disable_discharge is None + ): + return False + + charge_disabled = 0 if self.charge_fet else 1 + if self.trigger_force_disable_charge is not None and self.control_allow_charge: + charge_disabled = 1 if self.trigger_force_disable_charge else 0 + logger.info( + f"write force disable charging: {'true' if self.trigger_force_disable_charge else 'false'}" + ) + self.trigger_force_disable_charge = None + + discharge_disabled = 0 if self.discharge_fet else 1 + if ( + self.trigger_force_disable_discharge is not None + and self.control_allow_discharge + ): + discharge_disabled = 1 if self.trigger_force_disable_discharge else 0 + logger.info( + f"write force disable discharging: {'true' if self.trigger_force_disable_discharge else 'false'}" + ) + self.trigger_force_disable_discharge = None + + mosdata = pack(">BB", 0, charge_disabled | (discharge_disabled << 1)) + + reply = self.read_serial_data_llt(writeCmd(REG_CTRL_MOSFET, mosdata)) + + if reply is False: + logger.error("write force disable charge/discharge failed") + return False + + def turn_balancing_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_disable_balancer = False + return True + + if value == 1: + self.trigger_disable_balancer = True + return True + + return False + + def write_balancer(self): + if self.trigger_disable_balancer is None: + return False + + disable_balancer = self.trigger_disable_balancer + logger.info( + f"write disable balancer: {'true' if self.trigger_disable_balancer else 'false'}" + ) + self.trigger_disable_balancer = None + new_func_config = None + + with self.eeprom(): + func_config = self.read_serial_data_llt(readCmd(REG_FUNC_CONFIG)) + if func_config: + self.func_config = unpack_from(">H", func_config)[0] + balancer_enabled = self.func_config & FUNC_BALANCE_EN + # Balance is enabled, force disable OR balancer is disabled and force enable + if (balancer_enabled != 0 and disable_balancer) or ( + balancer_enabled == 0 and not disable_balancer + ): + new_func_config = self.func_config ^ FUNC_BALANCE_EN + + if new_func_config: + new_func_config_bytes = pack(">H", new_func_config) + with self.eeprom(writable=True): + reply = self.read_serial_data_llt( + writeCmd(REG_FUNC_CONFIG, new_func_config_bytes) + ) + if reply is False: + logger.error("write force disable balancer failed") + return False + + return True + def refresh_data(self): + self.write_charge_discharge_mos() + self.write_balancer() result = self.read_gen_data() result = result and self.read_cell_data() return result diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index de995492..27d7e927 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -179,5 +179,16 @@ def read_serial_data_llt(self, command): if not bat.test_connection(): logger.error(">>> ERROR: Unable to connect") else: + # Allow to change charge / discharge FET + bat.control_allow_charge = True + bat.control_allow_discharge = True + + bat.trigger_disable_balancer = True + bat.trigger_force_disable_charge = True + bat.trigger_force_disable_discharge = True + bat.refresh_data() + bat.trigger_disable_balancer = False + bat.trigger_force_disable_charge = False + bat.trigger_force_disable_discharge = False bat.refresh_data() bat.get_settings()