Benachrichtigungen
Alles löschen

JK_B2A8S20P => TTL UART => Python

1 Beiträge
1 Benutzer
0 Likes
581 Ansichten
nwmichl
(@nwmichl)
Newbie
Beigetreten: Vor 2 Jahren
Beiträge: 1
Themenstarter  

Hi there,

bisher stiller Mitleser, der gerade einen kleinen 12V Grundlastakku-PoC zusammenschraubt, die große PV-Eigenverbrauchssenke steht eh in der Garage...

Die direkte Anbindung des neuen/kleinen JK-BMS scheint ja wegen des cell count etwas tricky zu sein, out-of-the-box geht wohl nur dbus/serial-battery und das ESP Projekt aus dem Forum hier.
Der Umweg über VenusOS ist etwas umständlich für Leute, die vielleicht ihre eigene control loop schreiben möchten oder sogar außerhalb des Victronversums bleiben möchten.
Glücklicherweise bin ich heute über dieses Repo https://github.com/PurpleAlien/jk-bms_grafana gestolpert, habe das Python Script fix etwas für generische Aufrufe angepasst und möchte es euch zum weiteren Spielen überlassen.
Johan/PurpleAlien hat den code wirklich sehr schön kommentiert, das erleichtert die Nachvollziehbarkeit des data parsing und damit auch allfällige Erweiterungen ungemein.

Aktuell lässt meine Version unten die BMS-Daten einfach auf den Bildschirm raus, ist aber leicht an die Schnittstelle der Wahl anzupassen. Bei mir laufen die Metriken z.B. zusätzlich per rest call und line protocol in eine InfluxDB.

(...)
[3.3, 3.303, 3.283, 3.279] 75% 13.16V -29.09A -383.0W 22.0 21°C
[3.303, 3.3, 3.284, 3.279] 75% 13.16V -28.91A -380.0W 22.0 21°C
[3.3, 3.303, 3.299, 3.278] 75% 13.16V -29.82A -392.0W 22.0 21°C
[3.304, 3.3, 3.285, 3.298] 75% 13.17V -29.46A -388.0W 22.0 21°C
[3.299, 3.304, 3.299, 3.296] 75% 13.18V -29.28A -386.0W 22.0 21°C
[3.299, 3.295, 3.285, 3.289] 75% 13.19V -28.73A -379.0W 22.0 21°C
(...)


# This script reads the data from a JB BMS over RS-485 and formats
# it for use with https://github.com/BarkinSpider/SolarShed/

import time
import sys, os, io
import struct

# Plain serial... Modbus would have been nice, but oh well.
import serial

sleepTime = 10

try:
bms = serial.Serial('/dev/ttyUSB0')
bms.baudrate = 115200
bms.timeout = 0.2
except:
print("BMS not found.")

# The hex string composing the command, including CRC check etc.
# See also:
# - https://github.com/syssi/esphome-jk-bms
# - https://github.com/NEEY-electronic/JK/tree/JK-BMS
# - https://github.com/Louisvdw/dbus-serialbattery

def sendBMSCommand(cmd_string):
cmd_bytes = bytearray.fromhex(cmd_string)
for cmd_byte in cmd_bytes:
hex_byte = ("{0:02x}".format(cmd_byte))
bms.write(bytearray.fromhex(hex_byte))
return

# This could be much better, but it works.
def readBMS():

try:
# Read all command
sendBMSCommand('4E 57 00 13 00 00 00 00 06 03 00 00 00 00 00 00 68 00 00 01 29')

time.sleep(.1)

if bms.inWaiting() >= 4 :
if bms.read(1).hex() == '4e' : # header byte 1
if bms.read(1).hex() == '57' : # header byte 2
# next two bytes is the length of the data package, including the two length bytes
length = int.from_bytes(bms.read(2),byteorder='big')
length -= 2 # Remaining after length bytes

# Lets wait until all the data that should be there, really is present.
# If not, something went wrong. Flush and exit
available = bms.inWaiting()
if available != length :
time.sleep(0.1)
available = bms.inWaiting()
# if it's not here by now, exit
if available != length :
bms.reset_input_buffer()
raise Exception("Something went wrong reading the data...")

# Reconstruct the header and length field
b = bytearray.fromhex("4e57")
b += (length+2).to_bytes(2, byteorder='big')

# Read all the data
data = bytearray(bms.read(available))
# And re-attach the header (needed for CRC calculation)
data = b + data

# Calculate the CRC sum
crc_calc = sum(data[0:-4])
# Extract the CRC value from the data
crc_lo = struct.unpack_from('>H', data[-2:])[0]

# Exit if CRC doesn't match
if crc_calc != crc_lo :
bms.reset_input_buffer()
raise Exception("CRC Wrong")

# The actual data we need
data = data[11:length-19] # at location 0 we have 0x79

# The byte at location 1 is the length count for the cell data bytes
# Each cell has 3 bytes representing the voltage per cell in mV
bytecount = data[1]

# We can use this number to determine the total amount of cells we have
cellcount = int(bytecount/3)

# Voltages start at index 2, in groups of 3
cell =[]
for i in range(cellcount) :
voltage = struct.unpack_from('>xH', data, i * 3 + 2)[0]/1000
cell.append(voltage)

# Temperatures are in the next nine bytes (MOSFET, Probe 1 and Probe 2), register id + two bytes each for data
# Anything over 100 is negative, so 110 == -10
temp_fet = struct.unpack_from('>H', data, bytecount + 3)[0]
if temp_fet > 100 :
temp_fet = -(temp_fet - 100)
temp_1 = struct.unpack_from('>H', data, bytecount + 6)[0]
if temp_1 > 100 :
temp_1 = -(temp_1 - 100)
temp_2 = struct.unpack_from('>H', data, bytecount + 9)[0]
if temp_2 > 100 :
temp_2 = -(temp_2 - 100)

temp_bat = (temp_1 + temp_2) / 2

# Battery Voltage
voltage = struct.unpack_from('>H', data, bytecount + 12)[0]/100

# Battery Current
current = struct.unpack_from('>H', data, bytecount + 15)[0]/100
if current > 327.68:
current = round((current - 327.68), 2)
else:
current = round((current * -1), 2)

# Remaining capacity, SOC%
soc = struct.unpack_from('>B', data, bytecount + 18)[0]

# Calculate Battery Power
power = round((voltage * current), 0)

bms.reset_input_buffer()

except Exception as e :
print(e)

return cell, soc, voltage, current, power, temp_bat, temp_fet

while True:

cell, soc, voltage, current, power, temp_bat, temp_fet = readBMS()
print(f"{cell} {soc}% {voltage}V {current}A {power}W {temp_bat} {temp_fet}°C")

time.sleep(sleepTime)

Viele Grüße
Michael


   
Zitat
Teilen: