Skip to content

Commit

Permalink
Merge branch 'jkbms_ble' into master (#616)
Browse files Browse the repository at this point in the history
* changed cell imbalance thresholds

* added battery details
- added: MOS temperature
- added: Balancing switch status

* JKBMS Bluetooth - added battery details (#454)

* 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

* added support for the newer 32s systems, that have slightly different frame-structure

* added stop of scraping if detection of jkbms fails

* lint

* indentation was wrong

* adding fix for Venus OS >= v3.00~14 QML files

* merge with baranator:dbus-serialbattery:master

* BLE JK BMS model recognition
Pull request from @tcrichton
#462

* Small fixes
- Added: Setup cronjob when installing driver
- Added: Information how to pair JKBMS changed default pin
- Changed: Bump version to v0.15.0-ble

* Added driver restart script

* added informations to run the driver

* pasted code in wrong function

* fixed cell balancing color

* BLE JK BMS model recognition (#462)

* * Other JK BMS’ have an underscore in their vendor_id
* Don’t forget to include your new PageLynxIonIo in the build lis

* build list is removed upstream

---------

Co-authored-by: Tristan Crichton <[email protected]>

* install BLE battery as local independent service

* add disconnect before starting the service

* unique id per BLE device

* enable logging and improve error messages

* Update jkbms_ble from master

* fixes

* moved ble part to installble.sh

* added script to install directly from repository

* added QML

* create empty config.ini for easier user usage

* optimize installation scripts

* changed bash output

* added post install notes

* corrected errors

* fix black lint errors

* fix black lint errors

* fix black lint errors

* bump version

* fixed serial-starter.d
#520

* fix errors, move install notes to show always

* Update install-nightly.sh

* Update install-nightly.sh

* make use of bluetooth configurable

* remove modules with uninstall script

* fix typo

* Added install notes

* fix #351

* fix System Switch in IO page

* fix #239
by @TimGFoley

* fix black lint errors

* run code analyse only on code change

* fix #311

* Separate Time-To-Go and Time-To-SoC activation

* fix UnicodeDecodeError
#485 (comment)

* fix UnicodeDecodeError
#485 (comment)

* optimize uninstall script

* fix typo in log_settings

* add charge mode display

* Added: Temp name for sensor 1 & 2
Added the possibility to give a name to temperature sensor 1 & 2
This allows to see which sensor is low and high

* Added: Choose how battery temperature is assembled

* Added/Changed alarms for JKBMS
* Added: HighInternalTemperature alarm for JKBMS
* Changed: Temperature alarm to not trigger all in the same condition

* Removed Alarms/HighCellVoltage
It does not exist on the dbus

* Changed: Moved charge mode
from IO page to Paramaters page

* Added: Show (dis)charge current limitation reason

* added changelog

* corrected file permissions

* fix dbus-daemon memory leak

`poll_battery` is already called from the dbus mainloop in a separate thread. Opening a new thread here (and especially never closing it) will accumulate threads and leads to problems like `dbus-daemon` memory usage increasing over time, leading to eventual reboot

* fix typo

* Update readme

* Remove bluetooth option from the config file
Bluetooth classes are now imported automatically, if it's a Bluetooth port

* Small word case changes

* Added: Show specific TimeToSoC points in GUI
Only if 0%, 10%, 20%, 80%, 90% and/or 100% are selected

* fix black lint error

* Improved JBD BMS soc calculation
#439

* Fix for #397
#484

* small fixes

* sort bms imports

* Add support for HLPdata BMS4S
#505

* Add support for Seplos BMS
#530

* change flake8 settings

* fix black lint errors

* removed wildcard imports

* fixed black lint errors

* change flake8 settings

* remove wildcard import and fix black lint errors

* removed wildcard import

* fixed black lint check

* config changes

* remove old log message in handle_changed_setting()

* simplified condition for Time-To-Go/Soc

* fix renogy import

* added BMS info and cleanup
* MNB
* Revov
* Sinowealth

* moved BMS to subfolder

* moved BMS to subfolder

* corrected installble to run correct script

* Added self.unique_identifier to the battery class
Used to identify a BMS when multiple BMS are connected
planned for future use

* changed ble service name
from `dbus-blebattery-$1` to `dbus-blebattery.$1` like the non ble service

* read installed capacity at startup

* disable ANT BMS by default
#479

* fix cell voltage header parser

* rework daly receive routine

* improve read cell voltages - only work on sufficient data, drop only the bad package on checksum error, not the complete transmission

* allow read_soc to also retry serial transmission

* add daly cell balance state info. cells are red only if unbalanced now

* bump version

* typo

* moved read_serialport_data() to daly.py

* revert read_serialport_data() to the state before my changes

* fix connection log startup message.
now voltage/current/soc are displayed correctly

* black reformatting

* flake config change

* added linear voltage recalculation interval
In the config file can now be defined how often CVL, CCL and DCL is recalculated

* replaced penalty voltage calculation
with automatically calculated penalty voltages to simplify config
max voltage is kept until batteries are balanced

* fix black lint errors

* updated config.default.ini

* update nightly install script

* Removed line

* fixed error in HLPdataBMS4S

* fixed wrong variable assignment
`str` instead of `int`

* updated battery template

* Fix for #450
#450

* Read charge/discharge limit JKBMS
#4

* updated battery template

* Progress with config limits reason

* updated CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT default value

* added SoC round for LLT/JBD

* fixed log typo

* reworked installation procedure
Bluetooth BMS is now also fetched from config.ini

* Merge branch 'master' into jkbms_ble

* Merge branch 'master' of into jkbms_ble

* GitHub pages config change

* cleanup

* Renamed scripts for better reading #532

* update docusaurus dependencies

* change sh with bash

* limitation reason cleanup

* changed default config settings
FLOAT_CELL_VOLTAGE from 3.350V to 3.375V
LINEAR_LIMITATION_ENABLE from False to True

* removed testing line

* Cleanup duplicated files

Files were moved and not deleted

* Cleanup

* MOSFET temperature was displayed twice after merge

* updated changelog

* Small fixes

* fix disconnection behaviour: on disconnect, show '---' after 10s and 'not connected' after 60s

* fix flake errors

* small fix

* Added: apply max voltage if CVCM_ENABLE is False
before float voltage was applied

* fixed type error

* Added: BMS disconnect behaviour
* Choose to block charge/discharge on disconnect
* Trigger Venus OS alarm

* Changed: Remove wildcard import from dbushelper.py

* small fixes

* flake8 changes

* copied lltjbd_ble from idstein:jdb_ble

* Added and adapted LltJbd_Ble
ATTENTION: Currently it's untested

* small changes

* read production date and append to hardware version

* Set SOC nightly. Button is working, next is send command to bms

* Added: Show additional data in device page
* show self.unique_identifier as serial number
* show self.production as device name

* Added: JKBMS unique identifier & fixed data length

* Added: JKBMS BLE unique identifier

* Added: Jkbms_Ble connection_name()

* Added: Daly unique identifier

* Added: JKBMS BLE serial number, user defined field

* read out daly battery code and use as unique id

* moved production date and added custom field

* clean battery code
strip whitespace and replace one or multiple spaces with one underline
if no battery code generate unique field

* Daly read_capacity change
Read capacity from config file, if no value provided by BMS

* Daly try to fix no reply

* Improvements by @transistorgit

* changed value

* set SOC (and date time) on button press.

* fix battery code parser

* format fix

* format fix

* fix extra long serial timeouts
by calculating max time instead of loop counts

* Small fixes

* updated config file

* updated changelog

* debug daly

* changed release workflow

* Updated from master

* fix blank screen, debug daly

* make Reset SoC a spin box

* fix possible read_capacity problem

* Daly read_balance_state() add missing return

* Daly advanced troubleshooting

* Changed: Improved Daily stability by a lot

* fixes for disable and uninstall
service was not removed and if removed, it was recreated by the serial starter

* optimized USB install method

* updated changelog

* added missing qml to restore-gui.sh

* Improved uninstall

* make executable

* ready to merge with master

---------

Co-authored-by: Eike Baran <[email protected]>
Co-authored-by: Eike Baran <[email protected]>
Co-authored-by: Tristan Crichton <[email protected]>
Co-authored-by: Tristan Crichton <[email protected]>
Co-authored-by: Stefan Seidel <[email protected]>
Co-authored-by: seidler2547 <[email protected]>
Co-authored-by: Bernd Stahlbock <[email protected]>
Co-authored-by: Bernd <[email protected]>
  • Loading branch information
9 people authored May 8, 2023
1 parent 25e6fd4 commit 88e5eea
Show file tree
Hide file tree
Showing 9 changed files with 1,050 additions and 17 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) by @mr-manuel
* Added: Config file by @ppuetsch
* Added: Create empty `config.ini` for easier user usage by @mr-manuel
* Added: Cronjob to restart Bluetooth service every 12 hours by @mr-manuel
* Added: Daly BMS - Read capacity https://github.com/Louisvdw/dbus-serialbattery/pull/594 by @transistorgit
* Added: Daly BMS - Read production date and build unique identifier by @transistorgit
* Added: Daly BMS - Set SoC by @transistorgit
Expand All @@ -24,8 +25,18 @@
* Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 by @mr-manuel
* Added: HighInternalTemperature alarm (MOSFET) for JKBMS by @mr-manuel
* Added: Improved maintainability (flake8, black lint), introduced code checks and automate release build https://github.com/Louisvdw/dbus-serialbattery/pull/386 by @ppuetsch
* Added: Install needed Bluetooth components automatically after a Venus OS upgrade by @mr-manuel
* Added: JKBMS - MOS temperature https://github.com/Louisvdw/dbus-serialbattery/pull/440 by @baphomett
* Added: JKBMS - Uniqie identifier and show "User Private Data" field that can be set in the JKBMS App to identify the BMS in a multi battery environment by @mr-manuel
* Added: JKBMS BLE - Balancing switch status by @mr-manuel
* Added: JKBMS BLE - Capacity by @mr-manuel
* Added: JKBMS BLE - Cell imbalance alert by @mr-manuel
* Added: JKBMS BLE - Charging switch status by @mr-manuel
* Added: JKBMS BLE - Discharging switch status by @mr-manuel
* Added: JKBMS BLE - MOS temperature by @mr-manuel
* Added: JKBMS BLE - Show if balancing is active and which cells are balancing by @mr-manuel
* Added: JKBMS BLE - Show serial number and "User Private Data" field that can be set in the JKBMS App to identify the BMS in a multi battery environment by @mr-manuel
* Added: JKBMS BLE driver by @baranator
* Added: Possibility to add `config.ini` to the root of a USB flash drive on install via the USB method by @mr-manuel
* Added: Post install notes by @mr-manuel
* Added: Read charge/discharge limits from JKBMS by @mr-manuel
Expand Down Expand Up @@ -55,6 +66,7 @@
* Changed: Default FLOAT_CELL_VOLTAGE from 3.350 V to 3.375 V by @mr-manuel
* Changed: Default LINEAR_LIMITATION_ENABLE from False to True by @mr-manuel
* Changed: Disabled ANT BMS by default https://github.com/Louisvdw/dbus-serialbattery/issues/479 by @mr-manuel
* Changed: Driver can now also start without serial adapter attached for Bluetooth BMS by @seidler2547
* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/239 by @mr-manuel
* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/311 by @mr-manuel
* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 by @mr-manuel
Expand All @@ -68,6 +80,7 @@
* Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 by @aaronreek
* Changed: Logging to get relevant data by @mr-manuel
* Changed: Many code improvements https://github.com/Louisvdw/dbus-serialbattery/pull/393 by @ppuetsch
* Changed: Moved Bluetooth part to `reinstall-local.sh` by @mr-manuel
* Changed: Moved BMS scripts to subfolder by @mr-manuel
* Changed: Removed all wildcard imports and fixed black lint errors by @mr-manuel
* Changed: Removed cell voltage penalty. Replaced by automatic voltage calculation. Max voltage is kept until cells are balanced and reset when cells are inbalanced by @mr-manuel
Expand All @@ -78,3 +91,4 @@
* Changed: Temperature alarm changed in order to not trigger all in the same condition for JKBMS by @mr-manuel
* Changed: Time-To-Soc repetition from cycles to seconds. Minimum value is every 5 seconds. This prevents CPU overload and ensures system stability. Renamed `TIME_TO_SOC_LOOP_CYCLES` to `TIME_TO_SOC_RECALCULATE_EVERY` by @mr-manuel
* Changed: Time-To-Soc string from `days, HR:MN:SC` to `<days>d <hours>h <minutes>m <seconds>s` (same as Time-To-Go) by @mr-manuel
* Changed: Uninstall also installed Bluetooth modules on uninstall by @mr-manuel
255 changes: 255 additions & 0 deletions etc/dbus-serialbattery/bms/jkbms_ble.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
# -*- coding: utf-8 -*-
from battery import Battery, Cell
from utils import logger
from bms.jkbms_brn import Jkbms_Brn
from bleak import BleakScanner, BleakError
import asyncio
import time
import os


class Jkbms_Ble(Battery):
BATTERYTYPE = "Jkbms_Ble"
resetting = False

def __init__(self, port, baud, address):
super(Jkbms_Ble, self).__init__(address.replace(":", "").lower(), baud, address)
self.type = self.BATTERYTYPE
self.jk = Jkbms_Brn(address)

logger.info("Init of Jkbms_Ble at " + address)

def connection_name(self) -> str:
return "BLE " + self.address

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 Jkbms_Ble at " + self.jk.address)
try:
loop = asyncio.get_event_loop()
t = loop.create_task(BleakScanner.discover())
devices = loop.run_until_complete(t)
except BleakError as err:
logger.error(str(err))
return False
except Exception as err:
logger.error(f"Unexpected {err=}, {type(err)=}")
return False

found = False
for d in devices:
if d.address == self.jk.address:
found = True
if not found:
logger.error("No Jkbms_Ble found at " + self.jk.address)
return False

"""
# before indipended service, has to be checked
logger.info("test of jkbmsble")
tries = 0
while True:
try:
loop = asyncio.get_event_loop()
t = loop.create_task(
BleakScanner.find_device_by_address(self.jk.address)
)
device = loop.run_until_complete(t)
if device is None:
logger.info("jkbmsble not found")
if tries > 2:
return False
else:
# device found, exit loop and continue test
break
except BleakError as e:
if tries > 2:
return False
# recover from error if tries left
logger.error(str(e))
self.reset_bluetooth()
tries += 1
"""

# 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:
self.jk.stop_scraping()
return False

if not status["device_info"]["vendor_id"].startswith(("JK-", "JK_")):
self.jk.stop_scraping()
return False

logger.info("Jkbms_Ble found!")

# get first data to show in startup log
self.get_settings()
self.refresh_data()

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

# "User Private Data" field in APP
tmp = self.jk.get_status()["device_info"]["production"]
self.custom_field = tmp if tmp != "Input Us" else None

tmp = self.jk.get_status()["device_info"]["manufacturing_date"]
self.production = "20" + tmp if tmp and tmp != "" else None

self.unique_identifier = self.jk.get_status()["device_info"]["serial_number"]

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"]
+ " "
+ str(self.cell_count)
+ " cells"
+ (" (" + self.production + ")" if self.production else "")
)
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
logger.info("Jkbms_Ble: Bluetooth died")

# if the thread is still alive but data too old there is sth
# wrong with the bt-connection; restart whole stack
if not self.resetting:
self.reset_bluetooth()
self.jk.start_scraping()
time.sleep(2)

return False
else:
self.resetting = False

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.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"]

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"]

# show wich cells are balancing
for c in range(self.cell_count):
if self.balancing and (
st["cell_info"]["min_voltage_cell"] == c
or st["cell_info"]["max_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

# 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

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

def reset_bluetooth(self):
logger.info("Reset of Bluetooth triggered")
self.resetting = True
# if self.jk.is_running():
# self.jk.stop_scraping()
logger.info("Scraping ended, issuing sys-commands")
# process kill is needed, since the service/bluetooth driver is probably freezed
os.system('pkill -f "bluetoothd"')
# stop will not work, if service/bluetooth driver is stuck
# os.system("/etc/init.d/bluetooth stop")
time.sleep(2)
os.system("rfkill block bluetooth")
os.system("rfkill unblock bluetooth")
os.system("/etc/init.d/bluetooth start")
logger.info("Bluetooth should have been restarted")

def get_balancing(self):
return 1 if self.balancing else 0
Loading

0 comments on commit 88e5eea

Please sign in to comment.