Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes 2023.09.27 #833

Merged
merged 6 commits into from
Oct 6, 2023
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
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