mpp-solar: Cannot connect to JK BMS/Active Balancer

Tried both JK02/04. I am sure its something simple I am missing here. Using RPI4 4gb

jkbms -p C8:47:8C:F3:75:92 -P JK02 -D

2021-10-23 17:17:55,859:INFO:__init__:main@195: Solar Device Command Utility, version: 0.7.83, add missing libs
MqttBroker(name='localhost', port=1883, username=None, password=None, results_topic='jkbms')
2021-10-23 17:17:55,859:INFO:__init__:main@299: Creating device "unnamed" (type: "jkbms") on port "C8:47:8C:F3:75:92 (porttype=None)" using protocol "JK02"
2021-10-23 17:17:55,867:DEBUG:__init__:main@302: device_class <class 'mppsolar.devices.jkbms.jkbms'>
2021-10-23 17:17:55,867:DEBUG:device:__init__@31: __init__ args ()
2021-10-23 17:17:55,867:DEBUG:device:__init__@32: __init__ kwargs {'name': 'unnamed', 'port': 'C8:47:8C:F3:75:92', 'protocol': 'JK02', 'baud': 2400, 'porttype': None, 'mqtt_broker': MqttBroker(name='localhost', port=1883, username=None, password=None, results_topic='jkbms')}
2021-10-23 17:17:55,868:DEBUG:device:get_port_type@71: port matches jkble ':'
2021-10-23 17:17:55,868:INFO:device:set_port@154: Using jkbleio for communications
2021-10-23 17:17:55,902:DEBUG:device:set_protocol@96: Protocol JK02
2021-10-23 17:17:55,923:DEBUG:device:__init__@36: __init__ name unnamed, port <mppsolar.io.jkbleio.JkBleIO object at 0xb5d945b0>, protocol <mppsolar.protocols.jk02.jk02 object at 0xb5d7be70>
2021-10-23 17:17:55,923:DEBUG:__init__:main@349: Commands [(<mppsolar.devices.jkbms.jkbms object at 0xb5d7bed0>, '', '', 'screen', None, None)]
2021-10-23 17:17:55,923:INFO:__init__:main@354: Looping 1 commands
2021-10-23 17:17:55,924:INFO:__init__:main@365: Getting results from device: jkbms device - name: unnamed, port: <mppsolar.io.jkbleio.JkBleIO object at 0xb5d945b0>, protocol: <mppsolar.protocols.jk02.jk02 object at 0xb5d7be70> for command: , tag: , outputs: screen
2021-10-23 17:17:55,924:INFO:device:run_command@268: Running command
2021-10-23 17:17:55,924:INFO:jkabstractprotocol:get_full_command@71: Using protocol b'JK02' with 3 commands
2021-10-23 17:17:55,924:DEBUG:jkabstractprotocol:get_command_defn@102: get_command_defn for: getCellData
2021-10-23 17:17:55,924:DEBUG:abstractprotocol:get_command_defn@43: Processing command 'getCellData'
2021-10-23 17:17:55,924:DEBUG:abstractprotocol:get_command_defn@45: Found command getCellData in protocol b'JK02'
2021-10-23 17:17:55,924:DEBUG:jkabstractprotocol:get_full_command@76: self._command = getCellData
2021-10-23 17:17:55,924:DEBUG:jkabstractprotocol:get_full_command@87: cmd with SOR: bytearray(b'\xaaU\x90\xeb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
2021-10-23 17:17:55,925:DEBUG:jkabstractprotocol:get_full_command@95: cmd with command code: bytearray(b'\xaaU\x90\xeb\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
2021-10-23 17:17:55,925:DEBUG:jkabstractprotocol:get_full_command@97: cmd with crc: bytearray(b'\xaaU\x90\xeb\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10')
2021-10-23 17:17:55,925:INFO:device:run_command@294: full command bytearray(b'\xaaU\x90\xeb\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10') for command getCellData
2021-10-23 17:17:55,925:DEBUG:jkabstractprotocol:get_command_defn@102: get_command_defn for: getCellData
2021-10-23 17:17:55,925:DEBUG:abstractprotocol:get_command_defn@43: Processing command 'getCellData'
2021-10-23 17:17:55,925:DEBUG:abstractprotocol:get_command_defn@45: Found command getCellData in protocol b'JK02'
2021-10-23 17:17:55,925:INFO:jkabstractprotocol:get_full_command@71: Using protocol b'JK02' with 3 commands
2021-10-23 17:17:55,925:DEBUG:jkabstractprotocol:get_command_defn@102: get_command_defn for: getCellData
2021-10-23 17:17:55,926:DEBUG:abstractprotocol:get_command_defn@43: Processing command 'getCellData'
2021-10-23 17:17:55,926:DEBUG:abstractprotocol:get_command_defn@45: Found command getCellData in protocol b'JK02'
2021-10-23 17:17:55,926:DEBUG:jkabstractprotocol:get_full_command@76: self._command = getCellData
2021-10-23 17:17:55,926:DEBUG:jkabstractprotocol:get_full_command@87: cmd with SOR: bytearray(b'\xaaU\x90\xeb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
2021-10-23 17:17:55,926:DEBUG:jkabstractprotocol:get_full_command@95: cmd with command code: bytearray(b'\xaaU\x90\xeb\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
2021-10-23 17:17:55,926:DEBUG:jkabstractprotocol:get_full_command@97: cmd with crc: bytearray(b'\xaaU\x90\xeb\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10')
2021-10-23 17:17:55,926:INFO:jkbleio:send_and_receive@26: full command bytearray(b'\xaaU\x90\xeb\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10') for command getCellData
2021-10-23 17:17:55,926:DEBUG:jkabstractprotocol:get_command_defn@102: get_command_defn for: getCellData
2021-10-23 17:17:55,927:DEBUG:abstractprotocol:get_command_defn@43: Processing command 'getCellData'
2021-10-23 17:17:55,927:DEBUG:abstractprotocol:get_command_defn@45: Found command getCellData in protocol b'JK02'
2021-10-23 17:17:55,927:DEBUG:jkbleio:send_and_receive@30: expected record type 2 for command getCellData
2021-10-23 17:17:55,927:INFO:jkbleio:ble_connect@54: Attempting to connect to C8:47:8C:F3:75:92
2021-10-23 17:17:57,123:INFO:jkbleio:ble_get_data@82: Connected to b''
Traceback (most recent call last):
  File "/usr/local/bin/jkbms", line 33, in <module>
    sys.exit(load_entry_point('mppsolar', 'console_scripts', 'jkbms')())
  File "/home/pi/Python-3.6.9/src/mpp-solar/mppsolar/__init__.py", line 367, in main
    results = _device.run_command(command=_command)
  File "/home/pi/Python-3.6.9/src/mpp-solar/mppsolar/devices/device.py", line 311, in run_command
    command_defn=self._protocol.get_command_defn(command),
  File "/home/pi/Python-3.6.9/src/mpp-solar/mppsolar/io/jkbleio.py", line 34, in send_and_receive
    response = self.ble_get_data(full_command)
  File "/home/pi/Python-3.6.9/src/mpp-solar/mppsolar/io/jkbleio.py", line 91, in ble_get_data
    characteristicRead = serviceNotify.getCharacteristics(characteristicReadUuid)[0]
IndexError: list index out of range

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 40 (12 by maintainers)

Commits related to this issue

Most upvoted comments

if len(record) == 300 or len(record) == 320:
    # check the crc/checksum is correct for the record data
    crc = ord(record[299])
    calcCrc = crc8(record[:299])
    # print (crc, calcCrc)
    if crc == calcCrc:
        log.debug("Record CRC is valid")
        return True
return False

The checksum is always at position 300 even if the frame is 320 bytes long.

Thanks syssi. I got it to work by removing the ord() function from the line crc = ord(record[299])

I implemented a JKBMS Emulator based on a JDY-23 BLE module. The commands to change values are always based on a 20 byte frame with following format: Set Command:

aa5590eb 1d 04 01000000 000000000000000000 9c --> Set Charge ON (example)
|        |  |  |        |                  |
|        |  |  |        |                  +--> LSB of SUM
|        |  |  |        +---------------------> Dummy Data (usually random data)
|        |  |  +------------------------------> Value (little endian)
|        |  +---------------------------------> Size of value in byte
|        +------------------------------------> Register address to be modified
+---------------------------------------------> Header (fixed to 0xAA5590EB)

The register addresses (AD) are as follows:

AD  # VALUE
================
1d 04 01000000  Set Charge on (off = 00000000)
1e 04 01000000  Set Disharge on (off = 00000000)
1f 04 01000000  Set Balance on (off = 00000000)
06 04 03000000  Set balance trig voltage to 0.003
21 04 42d10000  Set cal voltage to 53.57
24 04 64000000  Set cal current to 0.100
1c 04 10000000  Set cell count to 16
20 04 f0ba0400  Set battery cap to 310
04 04 420e0000  Set Cell OVP to 3.65
05 04 b0040000  Set Cell OVPR to 1.2
03 04 b0040000  Set Cell UVPR to 1.2
02 04 b0040000  Set Cell UVP to 1.2
0b 04 b0040000  Set Power Off value to 1.2
9f 04 54657374  Set User Private Data (4 bytes) to TEST
9f 0d 30313233343536373839303132 c6  Set User Private Data (4 bytes) to 0123456789012
26 04 b0040000  Set Start Balance voltage to 1.2
13 04 2c010000  Set max Balance current to 0.3
0c 04 e8030000  Set Max charge Current to 1.0
0d 04 02000000  Set Charge OCP delay to 2
0e 04 02000000  Set Charge OCPR Time to 2
0f 04 e8030000  Set Max discharge Current to 1.0
10 04 02000000  Set Discharge OCP delay to 2
11 04 02000000  Set Discharge OCPR Time to 2
25 04 0a000000  Set to SCP Delay to 10.000
12 04 02000000  Set SCPR Time to 2
14 04 2c010000  Set Charge OTP to 30
15 04 2c010000  Set Charge OTPR to 30
16 04 2c010000  Set Discharge OTP to 30
17 04 2c010000  Set Discharge OTPR to 30
18 04 00000000  Set Charge UTP to 0
19 04 00000000  Set Charge UTPR to 0
27 04 00000000  Set Con. Wire Res.01 to 0.00
...
3e 04 00000000  Set Con. Wire Res.24 to 0.00

Btw, the first response on CMDINFO is a record type 0x01 that contains all setup data, then JKBMS continues periodically with record type 0x02 (Cell data). Based on this information one is able cover almost the whole functionality as the JKBMS App. Here the record structure (record type 0x01) with real data from my LiPoFe4 battery block:

Screenshot 2022-02-25 at 10 39 57

I could also find some more useful data in the Cell info frame (record type 0x02). At position 0x88 the possible 16 different System Alarms are shown and at 0x3E/3F the actual min and max cell number is distributed. Here the screenshot of the grammar:

JKBMS_Cell_Info

Finally the data analysis function I am using to convert the data on an ESP32: DATA_ANALYSIS.ino.zip

@bullar Do you like to share the code of your JK-BMS emulator? There is a new BMS model (B2A8S20P, hardware version 11) and some registers has been changed.

@LMMKnight This checksum should work for your device:

if len(record) == 300 or len(record) == 320:
    # check the crc/checksum is correct for the record data
    crc = ord(record[299])
    calcCrc = crc8(record[:299])
    # print (crc, calcCrc)
    if crc == calcCrc:
        log.debug("Record CRC is valid")
        return True
return False

The checksum is always at position 300 even if the frame is 320 bytes long.

To get things working on HW Ver 8.x and Software Ver 8.20G

I had to switch to a Raspberry Pi 4 with bluetooth 5.0 and change the following code:

def ble_connect(self, mac=None, protocol=None, record_type=0x02): “”" Connect to a BLE device with ‘mac’ address “”" self._device = None # Intialise BLE device self._device = btle.Peripheral(None) self._device.withDelegate(jkBleDelegate(self, protocol, record_type)) # Connect to BLE Device connected = False attempts = 0 log.info(f"Attempting to connect to {mac}“) while not connected: attempts += 1 if attempts > self.maxConnectionAttempts: log.warning(f"Cannot connect to mac {mac} - exceeded {attempts - 1} attempts”) return connected try: self._device.connect(mac) self._device.setMTU(244) #this must be added to receive more than 20 bytes in a single message 120, 120, 64 = 320 total connected = True except Exception: continue return connected

I also had to tweak the FFE3 to FFE1 and here’s the new format for the info:

SOR = bytes.fromhex(“55aaeb90”)

COMMANDS = { “getInfo”: { “name”: “getInfo”, “command_code”: “97”, “record_type”: “3”, “description”: “BLE Device Information inquiry”, “help”: " – queries the ble device information", “type”: “QUERY”, “response_type”: “POSITIONAL”, “response”: [ [“Hex2Str”, 4, “Header”, “”], [“Hex2Str”, 1, “Record Type”, “”], [“Hex2Int”, 1, “Record Counter”, “”], [“Hex2Ascii”, 12, “Device Model”, “”], [“Hex2Ascii”, 10, “Hardware Version”, “”], [“Hex2Ascii”, 10, “Software Version”, “”], [“uptime”, 3, “Up Time”, “”], [“Hex2Str”, 2, “Power-on Times”, “”], [“Hex2Ascii”, 13, “Device Name”, “”], [“Hex2Ascii”, 22, “Device Passcode”, “”], [“Hex2Ascii”, 6, “Manufacturing Date”, “”], [“Hex2Ascii”, 12, “Serial Number”, “”], [“discard”, 5, “”, “”], [“Hex2Ascii”, 17, “User Data”, “”], [“Hex2Ascii”, 16, “Settings Passcode?”, “”], [“discard”, 672, “unknown”, “”], ], “test_responses”: [ bytes.fromhex( “55aaeb9003f14a4b2d42324132345300000000000000332e300000000000332e322e330000000876450004000000506f7765722057616c6c203100000000313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2” ), bytes.fromhex( “55aaeb9003b54a4b2d42443641323053313050000000342e300000000000342e312e37000000541d1600040000004e6f7468696e67204a4b31000000000031323334000000000000000000000000323030373038000032303036323834303735000000000000496e707574205573657264617461000031323334353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4” ), ], }, }

Also the crc is broken so I skipped those lines. log.debug(f"Record Length:{len(record)}“) if len(record) == 300 or len(record) == 320: # check the crc/checksum is correct for the record data crc = ord(record[-1:]) calcCrc = crc8(record[:-1]) log.debug(f"crc {crc} calcCrc {calcCrc}”)# print (crc, calcCrc) return True if crc == calcCrc: log.debug(“Record CRC is valid”) return True return False

Another implementation using FFE0 and char FFE1: https://github.com/syssi/esphome-jk-bms/pull/10/files