Motivation
For reasons I shall not get into I like to take my self built electric Art car to a local Burning Man inspired event.

The car runs off 48V 30AH LiFePo4 battery packs with built in BMS:
The vehicle is light and has a really long range but as you can imagine it would really suck to be stuck somewhere.
The packs does have simple battery capacity displays that just read out the terminal voltage and translate this to a ballpark State Of Charge percentage but the readout does not seem to be very accurate.
The build in BMS offers monitoring via. Bluetooth and the seller supplies a couple of dodgy Android app that can provide a bit more information.
The apps were either very unstable and/or required you to sign up to some dodgy cloud service to use them. All in all not a very comforting experience.
I would really like if I was able to directly communicate with the BMS and integrate the battery monitoring directly into the dash of the Art car.
I had bought a spare BMS from the same vendor I got the battery packs from. This was a Jikong JK-B1A24S15P so I initially assumed this was the same BMS as in the packs.
I was able to infer from the apps that the packs actually contained a BMS from a company called Xiaoxiang and I started to investigate if there was any information about how to communicate with this BMS.
Starting point — existing community work
The investigation did not start from zero. An important initial reference was Sebastian Süss’ project, an open-source esphome-jk-bms implementation for communicating with JK battery-management systems. Its BLE implementation, test material and protocol notes provided a useful description of how a known family of commercial Bluetooth BMSs communicates.
The first useful result was that the reference didn’t fit
Rather than starting with a embedded ESP implementation I decided to first get the communication going via. Python from my workstation.
A discovery script let me scan for available BLE devices with associated MAC and BLE name.
Another script attempted to access information as if the pack was a Jikong BMS.
After some probing and prodding it turned out that the Xiaoxiang BMS used a slightly different but almost compatible protocol.
Based on that information, the first experimental implementation expected the JK BLE service/profile, including the familiar FFE0/FFE1 characteristics. Scanning and interrogating the actual batteries quickly showed something different: the packs exposed FF00/FF01/FF02 instead.
Armed with this information I could create another script that used the Xiaoxiang protocol to read Pack and cell voltages, temperature and State Of Charge (Note: this is a 12V pack):
python .\xiaoxiang_probe.py "A4:C1:37:42:6D:9E" [BLE] Connected: A4:C1:37:42:6D:9E [BLE] Notifications enabled on FF01 [BLE TX] DD A5 03 00 FF FD 77 [BLE TX] DD A5 04 00 FF FC 77 [BLE RX] 15 bytes: DD 04 00 08 0C FF 0C FF 0C FF 0D 00 FC CA 77 [FRAME] 15 bytes: DD 04 00 08 0C FF 0C FF 0C FF 0D 00 FC CA 77 -------------------------------- Cell count : 4 Cell 01 : 3.327 V Cell 02 : 3.327 V Cell 03 : 3.327 V Cell 04 : 3.328 V --------------------------------
This was really all I needed. I have several packs with the same BMS so I could now make the embedded version.
Result
I chose to make a stand alone device based on a ESP32 CheapYellowDevice board.
I could then stick this board on the dash of the car for constant monitoring:

I added a second screen to scan for BLE devices and let the user persistently select a BMS to connect to.
Project files available on GitHub
Future work
I have not yet started using the spare JK-B1A24S15P BMS I bought.
When I do I will probably make sure the device works with this BMS too. This should be easy enough since this is much better documented.