diff --git a/CHANGELOG.md b/CHANGELOG.md index b7019cfa..24edc9ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * Added: JKBMS BLE - Show if balancing is active and which cells are balancing by @mr-manuel * Added: Post install notes by @mr-manuel * Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel +* Added: Read charge/discharge limits from JKBMS by @mr-manuel * Added: Script to install directly from repository by @mr-manuel * Added: Show charge mode (absorption, bulk, ...) in Parameters page by @mr-manuel * Added: Show charge/discharge limitation reason by @mr-manuel @@ -50,6 +51,7 @@ * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/397 by @transistorgit * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/421 by @mr-manuel +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/450 by @mr-manuel * Changed: Fixed black lint errors by @mr-manuel * Changed: Fixed cell balancing background for cells 17-24 by @mr-manuel * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 by @mr-manuel diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 2aa3c317..bf4e5cbf 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -150,12 +150,12 @@ def to_temp(self, sensor: int, value: float) -> None: :param value: the sensor value :return: """ + if sensor == 0: + self.temp_mos = min(max(value, -20), 100) if sensor == 1: 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) def manage_charge_voltage(self) -> None: """ @@ -232,17 +232,19 @@ def manage_charge_voltage_linear(self) -> None: ) self.charge_mode = ( - "Bulk dynamic (vS: " - + str(round(voltageSum, 2)) - + " - pS: " - + str(round(penaltySum, 2)) - + ")" + "Bulk dynamic" + # + " (vS: " + # + str(round(voltageSum, 2)) + # + " - pS: " + # + str(round(penaltySum, 2)) + # + ")" if self.max_voltage_start_time is None - else "Absorption dynamic (vS: " - + str(round(voltageSum, 2)) - + " - pS: " - + str(round(penaltySum, 2)) - + ")" + else "Absorption dynamic" + # + "(vS: " + # + str(round(voltageSum, 2)) + # + " - pS: " + # + str(round(penaltySum, 2)) + # + ")" ) elif self.allow_max_voltage: @@ -263,7 +265,7 @@ def manage_charge_voltage_linear(self) -> None: and voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT ): self.charge_mode += " + Balancing" - self.charge_mode += " (LM)" + self.charge_mode += " (Linear Mode)" def manage_charge_voltage_step(self) -> None: """ @@ -327,7 +329,11 @@ def manage_charge_current(self) -> None: charge_limits = [ self.max_battery_charge_current ] # gets removed after finished testing - charge_limits_new = {self.max_battery_charge_current: "None (Max Config Limit)"} + charge_limits_new = {utils.MAX_BATTERY_CHARGE_CURRENT: "Config Limit"} + + # if values are not the same, then the limit was read also from the BMS + if utils.MAX_BATTERY_CHARGE_CURRENT != self.max_battery_charge_current: + charge_limits_new.update({self.max_battery_charge_current: "BMS Limit"}) if utils.CCCM_CV_ENABLE: tmp = self.calcMaxChargeCurrentReferringToCellVoltage() @@ -399,9 +405,9 @@ def manage_charge_current(self) -> None: self.charge_limitation = ( charge_limits_new[min(charge_limits_new)] - + " (" - + str(round(min(charge_limits_new), 3)) - + ")" + # + " (" + # + str(round(min(charge_limits_new), 3)) + # + ")" ) if self.control_charge_current == 0: @@ -415,9 +421,13 @@ def manage_charge_current(self) -> None: discharge_limits = [ self.max_battery_discharge_current ] # gets removed after finished testing - discharge_limits_new = { - self.max_battery_discharge_current: "None (Max Config Limit)" - } + discharge_limits_new = {utils.MAX_BATTERY_DISCHARGE_CURRENT: "Config Limit"} + + # if values are not the same, then the limit was read also from the BMS + if utils.MAX_BATTERY_DISCHARGE_CURRENT != self.max_battery_discharge_current: + discharge_limits_new.update( + {self.max_battery_discharge_current: "BMS Limit"} + ) if utils.DCCM_CV_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToCellVoltage() @@ -493,9 +503,9 @@ def manage_charge_current(self) -> None: self.discharge_limitation = ( discharge_limits_new[min(discharge_limits_new)] - + " (" - + str(round(min(discharge_limits_new), 3)) - + ")" + # + " (" + # + str(round(min(discharge_limits_new), 3)) + # + ")" ) if self.control_discharge_current == 0: @@ -900,6 +910,14 @@ def log_settings(self) -> None: f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}A | " + f"MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}A" ) + if ( + utils.MAX_BATTERY_CHARGE_CURRENT != self.max_battery_charge_current + or utils.MAX_BATTERY_DISCHARGE_CURRENT != self.max_battery_discharge_current + ): + logger.info( + f"> MAX BATTERY CHARGE CURRENT: {self.max_battery_charge_current}A | " + + f"MAX BATTERY DISCHARGE CURRENT: {self.max_battery_discharge_current}A (read from BMS)" + ) logger.info(f"> CVCM: {utils.CVCM_ENABLE}") logger.info( f"> MIN CELL VOLTAGE: {utils.MIN_CELL_VOLTAGE}V | MAX CELL VOLTAGE: {utils.MAX_CELL_VOLTAGE}V" diff --git a/etc/dbus-serialbattery/bms/battery_template.py b/etc/dbus-serialbattery/bms/battery_template.py index 10b851ee..e6148e65 100644 --- a/etc/dbus-serialbattery/bms/battery_template.py +++ b/etc/dbus-serialbattery/bms/battery_template.py @@ -1,4 +1,9 @@ # -*- coding: utf-8 -*- + +# NOTES +# Please also update the feature comparison table, if you are adding a new BMS +# https://louisvdw.github.io/dbus-serialbattery/general/features/#bms-feature-comparison + from battery import Protection, Battery, Cell from utils import is_bit_set, read_serial_data, logger import utils @@ -21,6 +26,9 @@ def test_connection(self): result = False try: result = self.read_status_data() + # get first data to show in startup log + if result: + self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False @@ -32,12 +40,23 @@ def get_settings(self): # Set the current limits, populate cell count, etc # Return True if success, False for failure - # Uncomment if BMS does not supply capacity - # self.capacity = BATTERY_CAPACITY - self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + self.capacity = ( + utils.BATTERY_CAPACITY # if possible replace constant with value read from BMS + ) + self.max_battery_charge_current = ( + utils.MAX_BATTERY_CHARGE_CURRENT # if possible replace constant with value read from BMS + ) + self.max_battery_discharge_current = ( + utils.MAX_BATTERY_DISCHARGE_CURRENT # if possible replace constant with value read from BMS + ) self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count + + # provide a unique identifier from the BMS to identify a BMS, if multiple same BMS are connected + # e.g. the serial number + # If there is no such value, please leave the line commented. In this case the capacity is used, + # since it can be changed by small amounts to make a battery unique. On +/- 5 Ah you can identify 11 batteries + # self.unique_identifier = str() return True def refresh_data(self): diff --git a/etc/dbus-serialbattery/bms/ecs.py b/etc/dbus-serialbattery/bms/ecs.py index 41065b7e..a50e6072 100644 --- a/etc/dbus-serialbattery/bms/ecs.py +++ b/etc/dbus-serialbattery/bms/ecs.py @@ -46,6 +46,9 @@ def test_connection(self): self.find_LiPro_cells() + # get first data to show in startup log + self.refresh_data() + return self.get_settings() except IOError: return False diff --git a/etc/dbus-serialbattery/bms/jkbms.py b/etc/dbus-serialbattery/bms/jkbms.py index fd3e12bc..db880704 100644 --- a/etc/dbus-serialbattery/bms/jkbms.py +++ b/etc/dbus-serialbattery/bms/jkbms.py @@ -31,8 +31,6 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count @@ -84,6 +82,12 @@ def read_status_data(self): unpack_from(">xH", celldata, c * 3 + 1)[0] / 1000 ) + # MOSFET temperature + offset = cellbyte_count + 3 + temp_mos = unpack_from(">H", self.get_data(status_data, b"\x80", offset, 2))[0] + self.to_temp(0, temp_mos if temp_mos < 99 else (100 - temp_mos)) + + # Temperature sensors offset = cellbyte_count + 6 temp1 = unpack_from(">H", self.get_data(status_data, b"\x81", offset, 2))[0] offset = cellbyte_count + 9 @@ -91,11 +95,6 @@ def read_status_data(self): 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)) - offset = cellbyte_count + 12 voltage = unpack_from(">H", self.get_data(status_data, b"\x83", offset, 2))[0] self.voltage = voltage / 100 @@ -108,6 +107,18 @@ def read_status_data(self): else (current - self.CURRENT_ZERO_CONSTANT) / 100 ) + # Continued discharge current + offset = cellbyte_count + 66 + self.max_battery_discharge_current = float( + unpack_from(">H", self.get_data(status_data, b"\x97", offset, 2))[0] + ) + + # Continued charge current + offset = cellbyte_count + 72 + self.max_battery_charge_current = float( + unpack_from(">H", self.get_data(status_data, b"\x99", offset, 2))[0] + ) + offset = cellbyte_count + 18 self.soc = unpack_from(">B", self.get_data(status_data, b"\x85", offset, 1))[0] diff --git a/etc/dbus-serialbattery/bms/jkbms_ble.py b/etc/dbus-serialbattery/bms/jkbms_ble.py index 10d6bab7..b495aa8e 100644 --- a/etc/dbus-serialbattery/bms/jkbms_ble.py +++ b/etc/dbus-serialbattery/bms/jkbms_ble.py @@ -91,6 +91,11 @@ def test_connection(self): return False logger.info("JK BMS found!") + + # get first data to show in startup log + self.get_settings() + self.refresh_data() + return True def get_settings(self): @@ -147,11 +152,11 @@ def refresh_data(self): for c in range(self.cell_count): self.cells[c].voltage = st["cell_info"]["voltages"][c] + self.to_temp(0, st["cell_info"]["temperature_mos"]) self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) - self.to_temp("mos", st["cell_info"]["temperature_mos"]) - self.current = st["cell_info"]["current"] - self.voltage = st["cell_info"]["total_voltage"] + self.current = round(st["cell_info"]["current"], 1) + self.voltage = round(st["cell_info"]["total_voltage"], 2) self.soc = st["cell_info"]["battery_soc"] self.cycles = st["cell_info"]["cycle_count"] diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index c285d44a..9bfcd7a5 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -67,6 +67,9 @@ def test_connection(self): result = False try: result = self.read_hardware_data() + # get first data to show in startup log + if result: + self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False @@ -165,12 +168,10 @@ def read_gen_data(self): self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count + # 0 = MOS, 1 = temp 1, 2 = temp 2 for t in range(self.temp_sensors): temp1 = unpack_from(">H", gen_data, 23 + (2 * t))[0] - if t == 0: - self.to_temp("mos", utils.kelvin_to_celsius(temp1 / 10)) - else: - self.to_temp(t, utils.kelvin_to_celsius(temp1 / 10)) + self.to_temp(t, utils.kelvin_to_celsius(temp1 / 10)) return True diff --git a/etc/dbus-serialbattery/bms/renogy.py b/etc/dbus-serialbattery/bms/renogy.py index 64dee151..acfe2335 100644 --- a/etc/dbus-serialbattery/bms/renogy.py +++ b/etc/dbus-serialbattery/bms/renogy.py @@ -47,6 +47,9 @@ def test_connection(self): result = False try: result = self.read_gen_data() + # get first data to show in startup log + if result: + self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False diff --git a/etc/dbus-serialbattery/bms/revov.py b/etc/dbus-serialbattery/bms/revov.py index b0eb65bf..17764989 100755 --- a/etc/dbus-serialbattery/bms/revov.py +++ b/etc/dbus-serialbattery/bms/revov.py @@ -54,8 +54,12 @@ def test_connection(self): result = False try: result = self.read_gen_data() - except: - pass + # get first data to show in startup log + if result: + self.refresh_data() + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 078d833f..716df11f 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -44,7 +44,9 @@ FLOAT_CELL_VOLTAGE = 3.35 ; Specify cell voltage diff where CVL limit is kept until diff is equal or lower CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL = 0.010 ; Specify cell voltage diff where CVL limit is reset to max voltage, if value get above -CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT = 0.050 +; the cells are considered as imbalanced, if the cell diff exceeds 5% of the nominal cell voltage +; e.g. 3.2 V * 5 / 100 = 0.160 V +CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT = 0.080 ; -- CVL reset based on SoC option (step mode) ; Specify how long the max voltage should be kept, if reached then switch to float voltage diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 0763852e..4037fbbf 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230501)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230502)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}"