This protocol enables communication between a client device and a Battery Management System (BMS) over Bluetooth Low Energy. It supports real-time monitoring of battery pack parameters including voltage, current, temperature, and protection states.
Before standard commands (0xDD) are accepted, a handshake must be performed using Auth Packets (0xFF).
| Component | UUID | Properties |
|---|---|---|
| Service | 0000ff00-0000-1000-8000-00805f9b34fb |
Primary Service |
| R/W Characteristic | 0000ff02-0000-1000-8000-00805f9b34fb |
Read, Write, Notify |
βββββββββββ¬ββββββββββ¬βββββββββββ¬βββββββββ¬βββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββ β Header β Action β Function β Length β Data β Checksum_H β Checksum_L β Tail β βββββββββββΌββββββββββΌβββββββββββΌβββββββββΌβββββββββββΌββββββββββββββΌββββββββββββββΌββββββββββ€ β 0xDD β 0xA5/5A β 1 byte β 1 byte β N bytes β 1 byte β 1 byte β 0x77 β βββββββββββ΄ββββββββββ΄βββββββββββ΄βββββββββ΄βββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββ Byte 0 Byte 1 Byte 2 Byte 3 Bytes 4-N Byte N+4 Byte N+5 Byte N+6 Total Length = Data Length + 7 bytes
βββββββββββ¬ββββββββββ¬βββββββββ¬βββββββββ¬βββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββ β Header βResponse β Status β Length β Data β Checksum_H β Checksum_L β Tail β βββββββββββΌββββββββββΌβββββββββΌβββββββββΌβββββββββββΌββββββββββββββΌββββββββββββββΌββββββββββ€ β 0xDD β 1 byte β 1 byte β1 byte β N bytes β 1 byte β 1 byte β 0x77 β βββββββββββ΄ββββββββββ΄βββββββββ΄βββββββββ΄βββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββ Byte 0 Byte 1 Byte 2 Byte 3 Bytes 4-N Byte N+4 Byte N+5 Byte N+6
| Field | Size | Description |
|---|---|---|
| Header | 1 byte | Fixed value 0xDD - packet start marker |
| Command | 1 byte | Command identifier |
| Mode | 1 byte | 0xA5 for read operations |
| Status | 1 byte | Response status code (0x00 = success) |
| Length | 1 byte | Number of data bytes (0-255) |
| Data | N bytes | Command-specific payload |
| Checksum_H | 1 byte | High byte of 16-bit CRC |
| Checksum_L | 1 byte | Low byte of 16-bit CRC |
| Tail | 1 byte | Fixed value 0x77 - packet end marker |
Client BMS Device
| |
|-- Write Request Packet ------------------>|
| |
| |-- Process Command
| |
|<-- Notify Response Packet ----------------|
| |
| Parameter | Value | Description |
|---|---|---|
| Command Timeout | 2000ms | Maximum wait time for response |
| Retry Interval | 50ms | Polling interval for command queue |
| Read Timeout | 5000ms | Maximum read operation duration |
| Recording Interval | 1000ms | Data collection frequency |
Request: DD 03 A5 00 ...
| Offset | Size | Field | Type | Unit |
|---|---|---|---|---|
| 0-1 | 2 | Total Voltage | uint16 | 0.1V |
| 2-3 | 2 | Current | int16 | 0.1A (signed) |
| 4-5 | 2 | Remaining Capacity | uint16 | 0.01Ah |
| 6 | 1 | Temperature Count (N) | uint8 | |
| 7-N | N | Temperature Array | int8[] | 1Β°C (signed, offset -40) |
| N+1 | 1 | RSOC | uint8 | 1% |
| N+2 | 1 | MOS Status | uint8 | Bit 0: Charge, Bit 1: Discharge |
| N+3-4 | 2 | Protection State | uint16 | Bit flags |
Request: DD 04 A5 00 ...
| Offset | Size | Field | Type | Unit |
|---|---|---|---|---|
| 0 | 1 | Cell Count (N) | uint8 | |
| 1-2N | 2N | Cell Voltage Array | uint16[] | 1mV per cell |
All multi-byte integers use Big-Endian (most significant byte first) encoding.
def calculate_request_checksum(function: int, length: int, data: bytes) -> tuple[int, int]:
"""
Calculate 16-bit checksum for REQUEST packets
Per protocol spec: 0x10000 - sum(function + length + data)
Returns: (high_byte, low_byte)
"""
checksum = function + length
for byte in data:
checksum += byte
# Protocol formula: 0x10000 - sum
checksum = (0x10000 - checksum) & 0xFFFF
high_byte = (checksum >> 8) & 0xFF
low_byte = checksum & 0xFF
return (high_byte, low_byte)
def calculate_response_checksum(length: int, data: bytes) -> tuple[int, int]:
"""
Calculate 16-bit checksum for validating RESPONSE packets
Checksum covers: length + data bytes only
"""
checksum = length
for byte in data:
checksum += byte
checksum = (0x10000 - checksum) & 0xFFFF
return ((checksum >> 8) & 0xFF, checksum & 0xFF)
# Example: Read Base Info request
# Function=0x03, Length=0x00, Data=[]
# Checksum = 0x10000 - 0x03 = 0xFFFD
# Packet: DD A5 03 00 FF FD 77
def calculate_auth_checksum(payload: bytes) -> int:
"""
Calculate 8-bit checksum for Auth packets (Header FF, AA)
Sum of payload bytes (Cmd + Len + Data)
"""
checksum = 0
for byte in payload:
checksum += byte
return checksum & 0xFF
| Code | Name | Description |
|---|---|---|
| 0x00 | SUCCESS | Command executed successfully |
| 0xFF | CHECKSUM_ERROR | Checksum validation failed |
| 0xFE | TIMEOUT | No response within timeout |
| 0xFD | INVALID_COMMAND | Unknown command byte |
When RSOC is between 45-90% during discharge transition:
For each cell i:
R[i] = (V_pre[i] - V_post[i]) / |I_discharge|
Where:
V_pre[i] = Cell voltage before discharge (PRE_DISCHARGE state)
V_post[i] = Cell voltage after discharge starts (DISCHARGE state)
I_discharge = Discharge current
class BMSProtocol:
HEADER = 0xDD
TAIL = 0x77
MODE_READ = 0xA5
def parse_base_info(self, data: bytes) -> dict:
"""Parse base information response"""
voltage = int.from_bytes(data[0:2], 'big') / 10.0 # 0.1V units
current = int.from_bytes(data[2:4], 'big', signed=True) / 10.0 # 0.1A
capacity = int.from_bytes(data[4:6], 'big') / 100.0 # 0.01Ah
temp_count = data[6]
temperatures = []
for i in range(temp_count):
temp_raw = data[7 + i]
temperatures.append(temp_raw - 40) # Offset by 40Β°C
return {
'voltage': voltage,
'current': current,
'capacity': capacity,
'temperatures': temperatures
}