Skip to content

Commit

Permalink
Bluetooth-Support for JK BMS (#372)
Browse files Browse the repository at this point in the history
* jkbms ble integration

* implemented most warnings except undercurrent(?), imbalance

* getting things together, removed most obvious mistakes; connection test is executed, further work required

* give the bluetooth-jkbms a dummy-serialport that will fail to open, so that the bt-connection can be tested

* added installation dependencies to installlocal; added error-handling if no bt-device is available during test_connection

* cleanup imports & None-check

Co-authored-by: Eike Baran <[email protected]>
  • Loading branch information
baranator and Eike Baran authored Jan 9, 2023
1 parent 43140c9 commit bdc777c
Show file tree
Hide file tree
Showing 6 changed files with 519 additions and 2 deletions.
4 changes: 3 additions & 1 deletion buildfiles.lst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ etc/dbus-serialbattery/ant.py
etc/dbus-serialbattery/util_max17853.py
etc/dbus-serialbattery/mnb.py
etc/dbus-serialbattery/jkbms.py
etc/dbus-serialbattery/jkbms_ble.py
etc/dbus-serialbattery/jkbms_brn.py
etc/dbus-serialbattery/sinowealth.py
etc/dbus-serialbattery/renogy.py
etc/dbus-serialbattery/ecs.py
etc/dbus-serialbattery/lifepower.py
etc/dbus-serialbattery/lifepower.py
1 change: 1 addition & 0 deletions etc/dbus-serialbattery/dbus-serialbattery.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from daly import Daly
from ant import Ant
from jkbms import Jkbms
from jkbms_ble import Jkbms_Ble
from sinowealth import Sinowealth
from renogy import Renogy
from ecs import Ecs
Expand Down
7 changes: 6 additions & 1 deletion etc/dbus-serialbattery/installlocal.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#!/bin/sh
opkg update
opkg install python3-misc python3-pip
pip3 install bleak
tar -zxf ./venus-data.tar.gz -C /data
sh /data/etc/dbus-serialbattery/reinstalllocal.sh
sh /data/etc/dbus-serialbattery/reinstalllocal.sh
echo "make sure to disable Settings/Bluetooth in the Remote-Console to prevent reconnects every minute. In case of crash after ~12-16 hours disable raspberry pi 3 internal bluetooth via dtoverlay and use an external usb bluetooth-dongle"

135 changes: 135 additions & 0 deletions etc/dbus-serialbattery/jkbms_ble.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
from battery import Battery, Cell
from utils import logger
from jkbms_brn import JkBmsBle
from bleak import BleakScanner, BleakError
import asyncio
import time


class Jkbms_Ble(Battery):
BATTERYTYPE = "Jkbms BLE"

def __init__(self, port, baud, address):
super(Jkbms_Ble, self).__init__("zero", baud)
self.type = self.BATTERYTYPE
self.jk = JkBmsBle(address)

logger.error("init of jkbmsble")

def test_connection(self):
# call a function that will connect to the battery, send a command and retrieve the result.
# The result or call should be unique to this BMS. Battery name or version, etc.
# Return True if success, False for failure

# check if device with given mac is found, otherwise abort

logger.info("test of jkbmsble")
try:
loop = asyncio.get_event_loop()
t = loop.create_task(BleakScanner.discover())
devices = loop.run_until_complete(t)
except BleakError as e:
logger.error(str(e))
return False

found = False
for d in devices:
if d.address == self.jk.address:
found = True
if not found:
return False

# device was found, presumeably a jkbms so start scraping
self.jk.start_scraping()
tries = 1

while self.jk.get_status() is None and tries < 20:
time.sleep(0.5)
tries += 1

# load initial data, from here on get_status has valid values to be served to the dbus
status = self.jk.get_status()
if status is None:
return False

if not status["device_info"]["vendor_id"].startswith("JK-"):
return False

logger.info("JK BMS found!")
return True

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
st = self.jk.get_status()["settings"]
self.cell_count = st["cell_count"]
self.max_battery_charge_current = st["max_charge_current"]
self.max_battery_discharge_current = st["max_discharge_current"]
self.max_battery_voltage = st["cell_ovp"] * self.cell_count
self.min_battery_voltage = st["cell_uvp"] * self.cell_count

for c in range(self.cell_count):
self.cells.append(Cell(False))

self.hardware_version = (
"JKBMS "
+ self.jk.get_status()["device_info"]["hw_rev"]
+ " "
+ str(self.cell_count)
+ " cells"
)
logger.info("BAT: " + self.hardware_version)
return True

def refresh_data(self):
# call all functions that will refresh the battery data.
# This will be called for every iteration (1 second)
# Return True if success, False for failure

# result = self.read_soc_data()
# TODO: check for errors
st = self.jk.get_status()
if st is None:
return False
if time.time() - st["last_update"] > 30:
# if data not updated for more than 30s, sth is wrong, then fail
return False

for c in range(self.cell_count):
self.cells[c].voltage = st["cell_info"]["voltages"][c]

self.to_temp(1, st["cell_info"]["temperature_sensor_1"])
self.to_temp(2, st["cell_info"]["temperature_sensor_2"])
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"]

# 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

self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0
self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0

self.protection.current_over = (
2
if (
st["warnings"]["charge_overcurrent"]
or st["warnings"]["discharge_overcurrent"]
)
else 0
)
self.protection.set_IC_inspection = (
2 if st["cell_info"]["temperature_mos"] > 80 else 0
)
self.protection.temp_high_charge = 2 if st["warnings"]["charge_overtemp"] else 0
self.protection.temp_low_charge = 2 if st["warnings"]["charge_undertemp"] else 0
self.protection.temp_high_discharge = (
2 if st["warnings"]["discharge_overtemp"] else 0
)
return True
Loading

0 comments on commit bdc777c

Please sign in to comment.