Skip to content

Commit

Permalink
Merge pull request #833 from mr-manuel/dev
Browse files Browse the repository at this point in the history
Changes 2023.09.27
  • Loading branch information
mr-manuel committed Oct 6, 2023
2 parents 60bdde9 + dd61ed0 commit f603dfb
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 90 deletions.
19 changes: 16 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# Changelog

## Breaking changes

* Driver version greater or equal to `v1.0.20230629beta` and smaller or equal to `v1.0.20230926beta`:

With `v1.0.20230927beta` the following values changed names:
* `BULK_CELL_VOLTAGE` -> `SOC_RESET_VOLTAGE`
* `BULK_AFTER_DAYS` -> `SOC_RESET_AFTER_DAYS`

## v1.0.x

* Added: Bluetooth: Show signal strength of BMS in log by @mr-manuel
* Added: Create unique identifier, if not provided from BMS by @mr-manuel
* Added: Current average of the last 5 minutes by @mr-manuel
Expand All @@ -13,11 +22,12 @@
* Added: JKBMS BMS connect via CAN (experimental, some limits apply) by @IrisCrimson and @mr-manuel
* Added: LLT/JBD BMS - Discharge / Charge Mosfet and disable / enable balancer switching over remote console/GUI with https://github.com/Louisvdw/dbus-serialbattery/pull/761 by @idstein
* Added: LLT/JBD BMS - Show balancer state in GUI under the IO page with https://github.com/Louisvdw/dbus-serialbattery/pull/763 by @idstein
* Added: Load to bulk voltage every x days to reset the SoC to 100% for some BMS by @mr-manuel
* Added: Load to SOC reset voltage every x days to reset the SoC to 100% for some BMS by @mr-manuel
* Added: Save custom name and make it restart persistant by @mr-manuel
* Added: Temperature names to dbus and mqtt by @mr-manuel
* Added: Use current average of the last 300 cycles for time to go and time to SoC calculation by @mr-manuel
* Added: Validate current, voltage, capacity and SoC for all BMS. This prevents that a device, which is no BMS, is detected as BMS. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/479 by @mr-manuel
* Changed: `VOLTAGE_DROP` now behaves differently. Before it reduced the voltage for the check, now the voltage for the charger is increased in order to get the target voltage on the BMS by @mr-manuel
* Changed: Daly BMS - Fix readsentence by @transistorgit
* Changed: Enable BMS that are disabled by default by specifying it in the config file. No more need to edit scripts by @mr-manuel
* Changed: Fixed Building wheel for dbus-fast won't finish on weak systems https://github.com/Louisvdw/dbus-serialbattery/issues/785 by @mr-manuel
Expand All @@ -29,10 +39,13 @@
* Changed: Improved battery voltage handling in linear absorption mode by @ogurevich
* Changed: Improved driver disable script by @md-manuel
* Changed: Improved driver reinstall when multiple Bluetooth BMS are enabled by @mr-manuel
* Changed: JKBMS - Driver do not start if manufacturer date in BMS is empty https://github.com/Louisvdw/dbus-serialbattery/issues/823 by @mr-manuel
* Changed: JKBMS_BLE BMS - Improved driver by @seidler2547 & @mr-manuel
* Changed: LLT/JBD BMS - Fix cycle capacity with https://github.com/Louisvdw/dbus-serialbattery/pull/762 by @idstein
* Changed: LLT/JBD BMS - Fixed https://github.com/Louisvdw/dbus-serialbattery/issues/730 by @mr-manuel
* Changed: LLT/JBD BMS - Fixed https://github.com/Louisvdw/dbus-serialbattery/issues/769 by @mr-manuel
* Changed: LLT/JBD BMS - Fixed https://github.com/Louisvdw/dbus-serialbattery/issues/778 with https://github.com/Louisvdw/dbus-serialbattery/pull/798 by @idstein
* Changed: LLT/JBD BMS - Improved error handling and automatical driver restart in case of error. Should fix https://github.com/Louisvdw/dbus-serialbattery/issues/730, https://github.com/Louisvdw/dbus-serialbattery/issues/769 and https://github.com/Louisvdw/dbus-serialbattery/issues/777 by @mr-manuel
* Changed: LLT/JBD BMS - Improved error handling and automatical driver restart in case of error. Fixed https://github.com/Louisvdw/dbus-serialbattery/issues/777 by @mr-manuel
* Changed: LLT/JBD BMS - SOC different in Xiaoxiang app and dbus-serialbattery with https://github.com/Louisvdw/dbus-serialbattery/pull/760 by @idstein
* Changed: Make CCL and DCL limiting messages more clear by @mr-manuel
* Changed: Reduce the big inrush current if the CVL jumps from Bulk/Absorbtion to Float https://github.com/Louisvdw/dbus-serialbattery/issues/659 by @Rikkert-RS & @ogurevich
Expand All @@ -44,7 +57,7 @@

## v1.0.20230531

### ATTENTION: Breaking changes! The config is now done in the `config.ini`. All values from the `utils.py` gets lost. The changes in the `config.ini` will persists future updates.
### ATTENTION: Breaking changes! The config is now done in the `config.ini`. All values from the `utils.py` get lost. The changes in the `config.ini` will persists future updates.

* Added: `self.unique_identifier` to the battery class. Used to identify a BMS when multiple BMS are connected - planned for future use by @mr-manuel
* Added: Alert is triggered, when BMS communication is lost by @mr-manuel
Expand Down
104 changes: 53 additions & 51 deletions etc/dbus-serialbattery/battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ def init_values(self):
self.cells: List[Cell] = []
self.control_charging = None
self.control_voltage = None
self.bulk_requested = False
self.bulk_last_reached = 0
self.bulk_battery_voltage = None
self.soc_reset_requested = False
self.soc_reset_last_reached = 0
self.soc_reset_battery_voltage = None
self.max_battery_voltage = None
self.min_battery_voltage = None
self.allow_max_voltage = True
Expand Down Expand Up @@ -244,34 +244,36 @@ def manage_charge_voltage(self) -> None:
self.charge_mode = "Keep always max voltage"

def prepare_voltage_management(self) -> None:
bulk_last_reached_days_ago = (
soc_reset_last_reached_days_ago = (
0
if self.bulk_last_reached == 0
else (((int(time()) - self.bulk_last_reached) / 60 / 60 / 24))
if self.soc_reset_last_reached == 0
else (((int(time()) - self.soc_reset_last_reached) / 60 / 60 / 24))
)
# set bulk_requested to True, if the days are over
# set soc_reset_requested to True, if the days are over
# it gets set to False once the bulk voltage was reached once
if (
utils.BULK_AFTER_DAYS is not False
and self.bulk_requested is False
utils.SOC_RESET_AFTER_DAYS is not False
and self.soc_reset_requested is False
and self.allow_max_voltage
and (
self.bulk_last_reached == 0
or utils.BULK_AFTER_DAYS < bulk_last_reached_days_ago
self.soc_reset_last_reached == 0
or utils.SOC_RESET_AFTER_DAYS < soc_reset_last_reached_days_ago
)
):
"""
logger.info(
f"set bulk_requested to True: first time (0) or {utils.BULK_AFTER_DAYS}"
+ f" < {round(bulk_last_reached_days_ago, 2)}"
f"set soc_reset_requested to True: first time (0) or {utils.SOC_RESET_AFTER_DAYS}"
+ f" < {round(soc_reset_last_reached_days_ago, 2)}"
)
"""
self.bulk_requested = True
self.soc_reset_requested = True

self.bulk_battery_voltage = round(utils.BULK_CELL_VOLTAGE * self.cell_count, 2)
self.soc_reset_battery_voltage = round(
utils.SOC_RESET_VOLTAGE * self.cell_count, 2
)

if self.bulk_requested:
self.max_battery_voltage = self.bulk_battery_voltage
if self.soc_reset_requested:
self.max_battery_voltage = self.soc_reset_battery_voltage
else:
self.max_battery_voltage = round(
utils.MAX_CELL_VOLTAGE * self.cell_count, 2
Expand Down Expand Up @@ -302,26 +304,26 @@ def manage_charge_voltage_linear(self) -> None:

# calculate penalty sum to prevent single cell overcharge by using current cell voltage
if (
self.max_battery_voltage != self.bulk_battery_voltage
self.max_battery_voltage != self.soc_reset_battery_voltage
and voltage > utils.MAX_CELL_VOLTAGE
):
# foundHighCellVoltage: reset to False is not needed, since it is recalculated every second
foundHighCellVoltage = True
penaltySum += voltage - utils.MAX_CELL_VOLTAGE
elif (
self.max_battery_voltage == self.bulk_battery_voltage
and voltage > utils.BULK_CELL_VOLTAGE
self.max_battery_voltage == self.soc_reset_battery_voltage
and voltage > utils.SOC_RESET_VOLTAGE
):
# foundHighCellVoltage: reset to False is not needed, since it is recalculated every second
foundHighCellVoltage = True
penaltySum += voltage - utils.BULK_CELL_VOLTAGE
penaltySum += voltage - utils.SOC_RESET_VOLTAGE

voltageDiff = self.get_max_cell_voltage() - self.get_min_cell_voltage()

if self.max_voltage_start_time is None:
# start timer, if max voltage is reached and cells are balanced
if (
self.max_battery_voltage - utils.VOLTAGE_DROP <= voltageSum
self.max_battery_voltage <= voltageSum
and voltageDiff <= utils.CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL
and self.allow_max_voltage
):
Expand Down Expand Up @@ -355,9 +357,7 @@ def manage_charge_voltage_linear(self) -> None:
# regardless of whether we were in absorption mode or not
if (
voltageSum
< self.max_battery_voltage
- utils.VOLTAGE_DROP
- measurementToleranceVariation
< self.max_battery_voltage - measurementToleranceVariation
):
self.max_voltage_start_time = None

Expand All @@ -379,30 +379,32 @@ def manage_charge_voltage_linear(self) -> None:

self.charge_mode = (
"Bulk dynamic"
# if self.max_voltage_start_time is None # remove this line after testing
if self.max_battery_voltage == self.bulk_battery_voltage
if self.max_voltage_start_time is None
else "Absorption dynamic"
)

if self.max_battery_voltage == self.soc_reset_battery_voltage:
self.charge_mode += " & SoC Reset"

elif self.allow_max_voltage:
self.control_voltage = round(self.max_battery_voltage, 3)
self.charge_mode = (
"Bulk"
# if self.max_voltage_start_time is None # remove this line after testing
if self.max_battery_voltage == self.bulk_battery_voltage
else "Absorption"
"Bulk" if self.max_voltage_start_time is None else "Absorption"
)

if self.max_battery_voltage == self.soc_reset_battery_voltage:
self.charge_mode += " & SoC Reset"

else:
floatVoltage = round((utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3)
chargeMode = "Float"
# reset bulk when going into float
if self.bulk_requested:
# logger.info("set bulk_requested to False")
self.bulk_requested = False
# IDEA: Save "bulk_last_reached" in the dbus path com.victronenergy.settings
if self.soc_reset_requested:
# logger.info("set soc_reset_requested to False")
self.soc_reset_requested = False
# IDEA: Save "soc_reset_last_reached" in the dbus path com.victronenergy.settings
# to make it restart persistent
self.bulk_last_reached = current_time
self.soc_reset_last_reached = current_time
if self.control_voltage:
# check if battery changed from bulk/absoprtion to float
if not self.charge_mode.startswith("Float"):
Expand Down Expand Up @@ -467,16 +469,16 @@ def manage_charge_voltage_linear(self) -> None:
self.charge_mode_debug += (
f"\nlinear_cvl_last_set: {self.linear_cvl_last_set}"
)
bulk_days_ago = round(
(current_time - self.bulk_last_reached) / 60 / 60 / 24, 2
soc_reset_days_ago = round(
(current_time - self.soc_reset_last_reached) / 60 / 60 / 24, 2
)
bulk_in_days = round(utils.BULK_AFTER_DAYS - bulk_days_ago, 2)
self.charge_mode_debug += "\nbulk_last_reached: " + str(
soc_reset_in_days = round(utils.SOC_RESET_AFTER_DAYS - soc_reset_days_ago, 2)
self.charge_mode_debug += "\nsoc_reset_last_reached: " + str(
"Never"
if self.bulk_last_reached == 0
else str(bulk_days_ago)
if self.soc_reset_last_reached == 0
else str(soc_reset_days_ago)
+ " days ago - next in "
+ str(bulk_in_days)
+ str(soc_reset_in_days)
+ "days"
)
# """
Expand Down Expand Up @@ -515,10 +517,7 @@ def manage_charge_voltage_step(self) -> None:

if self.max_voltage_start_time is None:
# check if max voltage is reached and start timer to keep max voltage
if (
self.max_battery_voltage - utils.VOLTAGE_DROP <= voltageSum
and self.allow_max_voltage
):
if self.max_battery_voltage <= voltageSum and self.allow_max_voltage:
# example 2
self.max_voltage_start_time = current_time

Expand Down Expand Up @@ -550,6 +549,9 @@ def manage_charge_voltage_step(self) -> None:
"Bulk" if self.max_voltage_start_time is None else "Absorption"
)

if self.max_battery_voltage == self.soc_reset_battery_voltage:
self.charge_mode += " & SoC Reset"

else:
# check if battery changed from bulk/absoprtion to float
if not self.charge_mode.startswith("Float"):
Expand All @@ -558,10 +560,10 @@ def manage_charge_voltage_step(self) -> None:
self.control_voltage = utils.FLOAT_CELL_VOLTAGE * self.cell_count
self.charge_mode = "Float"
# reset bulk when going into float
if self.bulk_requested:
# logger.info("set bulk_requested to False")
self.bulk_requested = False
self.bulk_last_reached = current_time
if self.soc_reset_requested:
# logger.info("set soc_reset_requested to False")
self.soc_reset_requested = False
self.soc_reset_last_reached = current_time

self.charge_mode += " (Step Mode)"

Expand Down
17 changes: 10 additions & 7 deletions etc/dbus-serialbattery/bms/jkbms.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,14 @@ def read_status_data(self):
self.custom_field = tmp if tmp != "Input Us" else None

# production date
offset = cellbyte_count + 164
tmp = unpack_from(">4s", self.get_data(status_data, b"\xB5", offset, 4))[
0
].decode()
self.production = "20" + tmp + "01" if tmp and tmp != "" else None
try:
offset = cellbyte_count + 164
tmp = unpack_from(">4s", self.get_data(status_data, b"\xB5", offset, 4))[
0
].decode()
self.production = "20" + tmp + "01" if tmp and tmp != "" else None
except UnicodeDecodeError:
self.production = None

offset = cellbyte_count + 174
self.version = unpack_from(
Expand Down Expand Up @@ -281,8 +284,8 @@ def to_protection_bits(self, byte_data):
# MOSFET temperature alarm
self.protection.temp_high_internal = 2 if is_bit_set(tmp[pos - 1]) else 0
# charge over voltage alarm
# TODO: check if "self.bulk_requested is False" works,
# else use "self.bulk_last_reached < int(time()) - (60 * 60)"
# TODO: check if "self.soc_reset_requested is False" works,
# else use "self.soc_reset_last_reached < int(time()) - (60 * 60)"
self.protection.voltage_high = 2 if is_bit_set(tmp[pos - 2]) else 0
# discharge under voltage alarm
self.protection.voltage_low = 2 if is_bit_set(tmp[pos - 3]) else 0
Expand Down
12 changes: 12 additions & 0 deletions etc/dbus-serialbattery/bms/jkbms_brn.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ def decode_cellinfo_jk02(self):
for t in TRANSLATE_CELL_INFO:
self.translate(fb, t, self.bms_status, f32s=has32s)
self.decode_warnings(fb)
logging.debug("decode_cellinfo_jk02(): self.frame_buffer")
logging.debug(self.frame_buffer)
logging.debug(self.bms_status)

def decode_settings_jk02(self):
Expand Down Expand Up @@ -255,6 +257,10 @@ def set_callback(self, callback):
self._new_data_callback = callback

def assemble_frame(self, data: bytearray):
logging.debug(
f"--> assemble_frame() -> self.frame_buffer (before extend) -> lenght: {len(self.frame_buffer)}"
)
logging.debug(self.frame_buffer)
if len(self.frame_buffer) > MAX_RESPONSE_SIZE:
logging.info(
"data dropped because it alone was longer than max frame length"
Expand All @@ -267,6 +273,10 @@ def assemble_frame(self, data: bytearray):

self.frame_buffer.extend(data)

logging.debug(
f"--> assemble_frame() -> self.frame_buffer (after extend) -> lenght: {len(self.frame_buffer)}"
)
logging.debug(self.frame_buffer)
if len(self.frame_buffer) >= MIN_RESPONSE_SIZE:
# check crc; always at position 300, independent of
# actual frame-lentgh, so crc up to 299
Expand All @@ -282,6 +292,8 @@ def assemble_frame(self, data: bytearray):

def ncallback(self, sender: int, data: bytearray):
logging.debug(f"--> NEW PACKAGE! lenght: {len(data)}")
logging.debug("ncallback(): data")
logging.debug(data)
self.assemble_frame(data)

def crc(self, arr: bytearray, length: int) -> int:
Expand Down
Loading

0 comments on commit f603dfb

Please sign in to comment.