I've been using my dbus-serialbattery for a while now with my Daly BMS. works great till about two weeks ago. Almost every night a random ANT BMS shows up and sets off the low voltage alarm in the Cerbo.. When I restart my Cerbo the ANT disappears again for about a day... Any idea whats going on? just to be clear. I have no ANT BMS hooked up to my cerbo or even in my system. All I have (connected) is a daly bms, multi, mppt and a gps mouse. Daly works fine.
many thanks for taking a look at this.
regards,
Rob
Driver version
0.14.3
Venus OS device type
Cerbo GX
Venus OS version
3.10
BMS type
Daly Smart BMS
Cell count
4
Connection type
Serial USB adapter to RS485
Config file
# -*- coding: utf-8 -*-
import logging
import serial
from time import sleep
from struct import *
import bisect
# Logging
logging.basicConfig()
logger = logging.getLogger("SerialBattery")
logger.setLevel(logging.INFO)
# battery types
# if not specified: baud = 9600
battery_types = [
{'bms' : "LltJbd"},
{'bms' : "Ant", "baud" : 19200},
{"bms" : "Daly", "address" : b"\x40"},
{"bms" : "Daly", "address" : b"\x80"},
{"bms" : "Jkbms", "baud" : 115200},
# {"bms" : "Sinowealth"},
{"bms" : "Lifepower"},
{"bms" : "Renogy", "address": b"\x30"},
{"bms" : "Renogy", "address": b"\xF7"},
{"bms" : "Ecs", "baud" : 19200},
# {"bms" : "MNB"},
]
# Constants - Need to dynamically get them in future
DRIVER_VERSION = 0.14
DRIVER_SUBVERSION = '.3'
zero_char = chr(48)
degree_sign = u'\N{DEGREE SIGN}'
# Choose the mode for voltage / current limitations (True / False)
# False is a Step mode. This is the default with limitations on hard boundary steps
# True "Linear" # New linear limitations by WaldemarFech for smoother values
LINEAR_LIMITATION_ENABLE = False
######### Cell Voltage limitation #########
# Description:
# Maximal charge / discharge current will be in-/decreased depending on min- and max-cell-voltages
# Example: 18cells * 3.55V/cell = 63.9V max charge voltage. 18 * 2.7V = 48,6V min discharge voltage
# ... but the (dis)charge current will be (in-/)decreased, if even ONE SINGLE BATTERY CELL reaches the limits
# Charge current control management referring to cell-voltage enable (True/False).
CCCM_CV_ENABLE = True
# Discharge current control management referring to cell-voltage enable (True/False).
DCCM_CV_ENABLE = True
# Set Steps to reduce battery current. The current will be changed linear between those steps
CELL_VOLTAGES_WHILE_CHARGING = [3.55, 3.50, 3.45, 3.30]
MAX_CHARGE_CURRENT_CV = [ 0, 2, 30, 100]
CELL_VOLTAGES_WHILE_DISCHARGING = [2.70, 2.80, 2.90, 3.10]
MAX_DISCHARGE_CURRENT_CV = [ 0, 5, 30, 60]
######### Temperature limitation #########
# Description:
# Maximal charge / discharge current will be in-/decreased depending on temperature
# Example: The temperature limit will be monitored to control the currents. If there are two temperature senors,
# then the worst case will be calculated and the more secure lower current will be set.
# Charge current control management referring to temperature enable (True/False).
CCCM_T_ENABLE = True
# Charge current control management referring to temperature enable (True/False).
DCCM_T_ENABLE = True
# Set Steps to reduce battery current. The current will be changed linear between those steps
TEMPERATURE_LIMITS_WHILE_CHARGING = [55, 40, 35, 5, 2, 0]
MAX_CHARGE_CURRENT_T = [ 0, 28, 60, 60, 28, 0]
TEMPERATURE_LIMITS_WHILE_DISCHARGING = [55, 40, 35, 5, 0, -20]
MAX_DISCHARGE_CURRENT_T = [ 0, 28, 60, 60, 28, 0]
# if the cell voltage reaches 3.55V, then reduce current battery-voltage by 0.01V
# if the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded
# there will be a sum of all penalties for each cell, which exceeds the limits
PENALTY_AT_CELL_VOLTAGE = [3.45, 3.55, 3.6]
PENALTY_BATTERY_VOLTAGE = [0.01, 1.0, 2.0] # this voltage will be subtracted
######### SOC limitation #########
# Description:
# Maximal charge / discharge current will be increased / decreased depending on State of Charge, see CC_SOC_LIMIT1 etc.
# The State of Charge (SoC) charge / discharge current will be in-/decreased depending on SOC.
# Example: 16cells * 3.45V/cell = 55,2V max charge voltage. 16*2.9V = 46,4V min discharge voltage
# Cell min/max voltages - used with the cell count to get the min/max battery voltage
MIN_CELL_VOLTAGE = 2.75
MAX_CELL_VOLTAGE = 3.45
FLOAT_CELL_VOLTAGE = 3.40
MAX_VOLTAGE_TIME_SEC = 15*60
SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = 90
# battery Current limits
MAX_BATTERY_CHARGE_CURRENT = 100.0
MAX_BATTERY_DISCHARGE_CURRENT = 250.0
# Charge current control management enable (True/False).
CCCM_SOC_ENABLE = True
# Discharge current control management enable (True/False).
DCCM_SOC_ENABLE = True
#charge current soc limits
CC_SOC_LIMIT1 = 98
CC_SOC_LIMIT2 = 95
CC_SOC_LIMIT3 = 91
#charge current limits
CC_CURRENT_LIMIT1 = 100
CC_CURRENT_LIMIT2 = MAX_BATTERY_CHARGE_CURRENT/4
CC_CURRENT_LIMIT3 = MAX_BATTERY_CHARGE_CURRENT/2
#discharge current soc limits
DC_SOC_LIMIT1 = 10
DC_SOC_LIMIT2 = 20
DC_SOC_LIMIT3 = 30
#discharge current limits
DC_CURRENT_LIMIT1 = 150
DC_CURRENT_LIMIT2 = MAX_BATTERY_DISCHARGE_CURRENT/4
DC_CURRENT_LIMIT3 = MAX_BATTERY_DISCHARGE_CURRENT/2
# Charge voltage control management enable (True/False).
CVCM_ENABLE = False
# Simulate Midpoint graph (True/False).
MIDPOINT_ENABLE = False
#soc low levels
SOC_LOW_WARNING = 20
SOC_LOW_ALARM = 10
# Daly settings
# Battery capacity (amps) if the BMS does not support reading it
BATTERY_CAPACITY = 304
# Invert Battery Current. Default non-inverted. Set to -1 to invert
INVERT_CURRENT_MEASUREMENT = -1
# TIME TO SOC settings [Valid values 0-100, but I don't recommend more that 20 intervals]
# Set of SoC percentages to report on dbus. The more you specify the more it will impact system performance.
# TIME_TO_SOC_POINTS = [100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0] # Every 5% SoC
# TIME_TO_SOC_POINTS = [100, 95, 90, 85, 75, 50, 25, 20, 10, 0]
TIME_TO_SOC_POINTS = [] # No data set to disable
# Specify TimeToSoc value type: [Valid values 1,2,3]
# TIME_TO_SOC_VALUE_TYPE = 1 # Seconds
# TIME_TO_SOC_VALUE_TYPE = 2 # Time string HH:MN:SC
TIME_TO_SOC_VALUE_TYPE = 3 # Both Seconds and time str "<seconds> [days, HR:MN:SC]"
# Specify how many loop cycles between each TimeToSoc updates
TIME_TO_SOC_LOOP_CYCLES = 5
# Include TimeToSoC points when moving away from the SoC point. [Valid values True,False]
# These will be as negative time. Disabling this improves performance slightly.
TIME_TO_SOC_INC_FROM = False
# Select the format of cell data presented on dbus. [Valid values 0,1,2,3]
# 0 Do not publish all the cells (only the min/max cell data as used by the default GX)
# 1 Format: /Voltages/Cell# (also available for display on Remote Console)
# 2 Format: /Cell/#/Volts
# 3 Both formats 1 and 2
BATTERY_CELL_DATA_FORMAT = 1
# Settings for ESC GreenMeter and Lipro devices
GREENMETER_ADDRESS = 1
LIPRO_START_ADDRESS = 2
LIPRO_END_ADDRESS = 4
LIPRO_CELL_COUNT = 15
def constrain(val, min_val, max_val):
if min_val > max_val:
min_val, max_val = max_val, min_val
return min(max_val, max(min_val, val))
def mapRange(inValue, inMin, inMax, outMin, outMax):
return outMin + (((inValue - inMin) / (inMax - inMin)) * (outMax - outMin))
def mapRangeConstrain(inValue, inMin, inMax, outMin, outMax):
return constrain(mapRange(inValue, inMin, inMax, outMin, outMax), outMin, outMax)
def calcLinearRelationship(inValue, inArray, outArray):
if inArray[0] > inArray[-1]: # change compare-direction in array
return calcLinearRelationship(inValue, inArray[::-1], outArray[::-1])
else:
# Handle out of bounds
if inValue <= inArray[0]:
return outArray[0]
if inValue >= inArray[-1]:
return outArray[-1]
# else calculate linear current between the setpoints
idx = bisect.bisect(inArray, inValue)
upperIN = inArray[idx - 1] # begin with idx 0 as max value
upperOUT = outArray[idx - 1]
lowerIN = inArray[idx]
lowerOUT = outArray[idx]
return mapRangeConstrain(inValue, lowerIN, upperIN, lowerOUT, upperOUT)
def calcStepRelationship(inValue, inArray, outArray, returnLower):
if inArray[0] > inArray[-1]: # change compare-direction in array
return calcStepRelationship(inValue, inArray[::-1], outArray[::-1], returnLower)
# Handle out of bounds
if inValue <= inArray[0]:
return outArray[0]
if inValue >= inArray[-1]:
return outArray[-1]
# else get index between the setpoints
idx = bisect.bisect(inArray, inValue)
return outArray[idx] if returnLower else outArray[idx-1]
def is_bit_set(tmp):
return False if tmp == zero_char else True
def kelvin_to_celsius(kelvin_temp):
return kelvin_temp - 273.1
def format_value(value, prefix, suffix):
return None if value is None else ('' if prefix is None else prefix) + \
str(value) + \
('' if suffix is None else suffix)
def read_serial_data(command, port, baud, length_pos, length_check, length_fixed=None, length_size=None):
try:
with serial.Serial(port, baudrate=baud, timeout=0.1) as ser:
return read_serialport_data(ser, command, length_pos, length_check, length_fixed, length_size)
except serial.SerialException as e:
logger.error(e)
return False
# Open the serial port
# Return variable for the openned port
def open_serial_port(port, baud):
ser = None
tries = 3
while tries > 0:
try:
ser = serial.Serial(port, baudrate=baud, timeout=0.1)
tries = 0
except serial.SerialException as e:
logger.error(e)
tries -= 1
return ser
# Read data from previously openned serial port
def read_serialport_data(ser, command, length_pos, length_check, length_fixed=None, length_size=None):
try:
ser.flushOutput()
ser.flushInput()
ser.write(command)
length_byte_size = 1
if length_size is not None:
if length_size.upper() == 'H':
length_byte_size = 2
elif length_size.upper() == 'I' or length_size.upper() == 'L':
length_byte_size = 4
count = 0
toread = ser.inWaiting()
while toread < (length_pos+length_byte_size):
sleep(0.005)
toread = ser.inWaiting()
count += 1
if count > 50:
logger.error(">>> ERROR: No reply - returning")
return False
#logger.info('serial data toread ' + str(toread))
res = ser.read(toread)
if length_fixed is not None:
length = length_fixed
else:
if len(res) < (length_pos+length_byte_size):
logger.error(">>> ERROR: No reply - returning [len:" + str(len(res)) + "]")
return False
length_size = length_size if length_size is not None else 'B'
length = unpack_from('>'+length_size, res,length_pos)[0]
#logger.info('serial data length ' + str(length))
count = 0
data = bytearray(res)
while len(data) <= length + length_check:
res = ser.read(length + length_check)
data.extend(res)
#logger.info('serial data length ' + str(len(data)))
sleep(0.005)
count += 1
if count > 150:
logger.error(">>> ERROR: No reply - returning [len:" + str(len(data)) + "/" + str(length + length_check) + "]")
return False
return data
except serial.SerialException as e:
logger.error(e)
return False
Relevant log output
bit lost here but according to my cerbo the ANT should be on ttyUSB2..
root@cerbosgx:~# tail -F -n 100 /data/log/dbus-serialbattery.ttyUSB2/current | tai64nlocal
2023-09-21 19:31:42.098501500 ERROR:SerialBattery:>>> ERROR: No reply - returning
2023-09-21 19:31:42.105399500 ERROR:SerialBattery:>>> ERROR: Incorrect Data
Describe the problem
Hi All,
I've been using my dbus-serialbattery for a while now with my Daly BMS. works great till about two weeks ago. Almost every night a random ANT BMS shows up and sets off the low voltage alarm in the Cerbo.. When I restart my Cerbo the ANT disappears again for about a day... Any idea whats going on? just to be clear. I have no ANT BMS hooked up to my cerbo or even in my system. All I have (connected) is a daly bms, multi, mppt and a gps mouse. Daly works fine.
many thanks for taking a look at this.
regards, Rob
Driver version
0.14.3
Venus OS device type
Cerbo GX
Venus OS version
3.10
BMS type
Daly Smart BMS
Cell count
4
Connection type
Serial USB adapter to RS485
Config file
Relevant log output
Any other information that may be helpful
No response