Skip to content

Commit

Permalink
JKBMS Bluetooth - added battery details (#454)
Browse files Browse the repository at this point in the history
* added battery details
- added: capacity
- added: MOS temperature
- added: charging switch status
- added: discharging switch status
- added: balancing switch status
- added: show if balancing is active and which cells are balanced
- added: cell imbalance alert

* changed cell imbalance thresholds

* added battery details
- added: MOS temperature
- added: Balancing switch status
  • Loading branch information
mr-manuel committed Feb 13, 2023
1 parent 183910c commit a668a24
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 3 deletions.
10 changes: 10 additions & 0 deletions etc/dbus-serialbattery/battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ def __init__(self, port, baud):
self.soc = None
self.charge_fet = None
self.discharge_fet = None
self.balance_fet = None
self.cell_count = None
self.temp_sensors = None
self.temp1 = None
self.temp2 = None
self.temp_mos = None
self.cells = []
self.control_charging = None
self.control_voltage = None
Expand Down Expand Up @@ -101,6 +103,8 @@ def to_temp(self, sensor, value):
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):
if LINEAR_LIMITATION_ENABLE:
Expand Down Expand Up @@ -509,6 +513,12 @@ def get_max_temp(self):
else:
return None

def get_mos_temp(self):
if self.temp_mos is not None:
return self.temp_mos
else:
return None

def log_cell_data(self):
if logger.getEffectiveLevel() > logging.INFO and len(self.cells) == 0:
return False
Expand Down
8 changes: 7 additions & 1 deletion etc/dbus-serialbattery/dbushelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ def setup_vedbus(self):
# Create battery extras
self._dbusservice.add_path("/System/MinCellTemperature", None, writeable=True)
self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True)
self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True)
self._dbusservice.add_path(
"/System/MaxCellVoltage",
None,
Expand All @@ -243,7 +244,8 @@ def setup_vedbus(self):
self._dbusservice.add_path("/Balancing", None, writeable=True)
self._dbusservice.add_path("/Io/AllowToCharge", 0, writeable=True)
self._dbusservice.add_path("/Io/AllowToDischarge", 0, writeable=True)
# self._dbusservice.add_path('/SystemSwitch',1,writeable=True)
self._dbusservice.add_path("/Io/AllowToBalance", 0, writeable=True)
# self._dbusservice.add_path('/SystemSwitch', 1, writeable=True)

# Create the alarms
self._dbusservice.add_path("/Alarms/LowVoltage", None, writeable=True)
Expand Down Expand Up @@ -363,6 +365,9 @@ def publish_dbus(self):
self._dbusservice["/Io/AllowToDischarge"] = (
1 if self.battery.discharge_fet else 0
)
self._dbusservice["/Io/AllowToBalance"] = (
1 if self.battery.balance_fet else 0
)
self._dbusservice["/System/NrOfModulesBlockingCharge"] = (
0
if self.battery.charge_fet is None
Expand All @@ -378,6 +383,7 @@ def publish_dbus(self):
)
self._dbusservice["/System/MinCellTemperature"] = self.battery.get_min_temp()
self._dbusservice["/System/MaxCellTemperature"] = self.battery.get_max_temp()
self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp()

# Charge control
self._dbusservice[
Expand Down
5 changes: 5 additions & 0 deletions etc/dbus-serialbattery/installqml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
if [ ! -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then
cp /opt/victronenergy/gui/qml/PageBattery.qml /opt/victronenergy/gui/qml/PageBattery.qml.backup
fi
if [ ! -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then
cp /opt/victronenergy/gui/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup
fi
#copy new PageBattery.qml
cp /data/etc/dbus-serialbattery/qml/PageBattery.qml /opt/victronenergy/gui/qml/
#copy new PageLynxIonIo.qml
cp /data/etc/dbus-serialbattery/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/
#copy new PageBatteryCellVoltages
cp /data/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml /opt/victronenergy/gui/qml/
cp /data/etc/dbus-serialbattery/qml/PageBatterySetup.qml /opt/victronenergy/gui/qml/
Expand Down
5 changes: 5 additions & 0 deletions etc/dbus-serialbattery/jkbms.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ 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
Expand Down
31 changes: 29 additions & 2 deletions etc/dbus-serialbattery/jkbms_ble.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def get_settings(self):
for c in range(self.cell_count):
self.cells.append(Cell(False))

self.capacity = self.jk.get_status()["cell_info"]["capacity_nominal"]

self.hardware_version = (
"JKBMS "
+ self.jk.get_status()["device_info"]["hw_rev"]
Expand Down Expand Up @@ -102,16 +104,37 @@ def refresh_data(self):

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.soc = st["cell_info"]["battery_soc"]
self.cycles = st["cell_info"]["cycle_count"]
self.capacity = st["cell_info"]["capacity_nominal"]

self.charge_fet = st["settings"]["charging_switch"]
self.discharge_fet = st["settings"]["discharging_switch"]
self.balance_fet = st["settings"]["balancing_switch"]

self.balancing = False if st["cell_info"]["balancing_action"] == 0.000 else True
self.balancing_current = st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1
self.balancing_action = st["cell_info"]["balancing_action"]

for c in range(self.cell_count):
if self.balancing and (st["cell_info"]["max_voltage_cell"] == c or st["cell_info"]["min_voltage_cell"] == c ):
self.cells[c].balance = True
else:
self.cells[c].balance = False

# protection bits
# self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0
# self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0

# trigger cell imbalance warning when delta is to great
if st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.05, 0.200):
self.protection.cell_imbalance = 2
elif st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.03, 0.120):
self.protection.cell_imbalance = 1
else:
self.protection.cell_imbalance = 0

self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0
self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0
Expand All @@ -133,3 +156,7 @@ def refresh_data(self):
2 if st["warnings"]["discharge_overtemp"] else 0
)
return True


def get_balancing(self):
return 1 if self.balancing else 0
9 changes: 9 additions & 0 deletions etc/dbus-serialbattery/qml/PageBattery.qml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ MbPage {
}
}

MbItemValue {
description: qsTr("MOSFET temperature")
show: item.valid
item {
bind: service.path("/System/MOSTemperature")
displayUnit: user.temperatureUnit
}
}

MbItemValue {
description: qsTr("Air temperature")
item {
Expand Down
73 changes: 73 additions & 0 deletions etc/dbus-serialbattery/qml/PageLynxIonIo.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import QtQuick 1.1
import "utils.js" as Utils

MbPage {
id: root
property string bindPrefix

model: VisualItemModel {
MbItemOptions {
id: systemSwitch
description: qsTr("System Switch")
bind: Utils.path(bindPrefix, "/SystemSwitch")
readonly: true
possibleValues:[
MbOption{description: qsTr("Disabled"); value: 0},
MbOption{description: qsTr("Enabled"); value: 1}
]
}

MbItemOptions {
description: qsTr("Allow to charge")
bind: Utils.path(bindPrefix, "/Io/AllowToCharge")
readonly: true
possibleValues:[
MbOption{description: qsTr("No"); value: 0},
MbOption{description: qsTr("Yes"); value: 1}
]
}

MbItemOptions {
description: qsTr("Allow to discharge")
bind: Utils.path(bindPrefix, "/Io/AllowToDischarge")
readonly: true
possibleValues:[
MbOption{description: qsTr("No"); value: 0},
MbOption{description: qsTr("Yes"); value: 1}
]
}

MbItemOptions {
description: qsTr("Allow to balance")
bind: service.path("/Io/AllowToBalance")
readonly: true
show: item.valid
possibleValues:[
MbOption{description: qsTr("No"); value: 0},
MbOption{description: qsTr("Yes"); value: 1}
]
}

MbItemOptions {
description: qsTr("External relay")
bind: Utils.path(bindPrefix, "/Io/ExternalRelay")
readonly: true
show: item.valid
possibleValues:[
MbOption{description: qsTr("Inactive"); value: 0},
MbOption{description: qsTr("Active"); value: 1}
]
}

MbItemOptions {
description: qsTr("Programmable Contact")
bind: Utils.path(bindPrefix, "/Io/ProgrammableContact")
readonly: true
show: item.valid
possibleValues:[
MbOption{description: qsTr("Inactive"); value: 0},
MbOption{description: qsTr("Active"); value: 1}
]
}
}
}

0 comments on commit a668a24

Please sign in to comment.