# BullTron BLE Live Capture Status: confirmed from Android Bluetooth HCI snoop log captured with the BullTron app connected to a BullTron battery. The raw `btsnoop_hci.log` is not committed because it contains unrelated nearby Bluetooth advertisements and device addresses. This file contains the protocol facts extracted from the capture. ## Capture Summary - Format: BTSnoop version 1, HCI UART (H4) - Tool used for inspection: `btmon` - Connected LE device: BullTron/Daly-style BLE peripheral - Connection handle in capture: `65` - Remote controller manufacturer: Hong Kong Bouffalo Lab Limited - Remote controller Bluetooth version: Bluetooth 5.0 - App behavior: reconnects automatically, discovers services, negotiates MTU, subscribes to notifications, then polls BMS registers. ## Discovery And Setup The capture confirms the app-discovered main BMS service: | Handle range | UUID | Meaning | | --- | --- | --- | | `0x0010`-`0x001c` | `0000fff0-0000-1000-8000-00805f9b34fb` | Main BMS service | Characteristics under that service: | Declaration handle | Value handle | UUID | Properties | | ---: | ---: | --- | --- | | `0x0011` | `0x0012` | `0000fff1-0000-1000-8000-00805f9b34fb` | read, notify | | `0x0014` | `0x0015` | `0000fff2-0000-1000-8000-00805f9b34fb` | read, write, write without response | | `0x001a` | `0x001b` | `0000fff3-0000-1000-8000-00805f9b34fb` | read, write, notify | Setup observed: ```text ATT Exchange MTU Request: client RX MTU 517 ATT Exchange MTU Response: server RX MTU 247 LE Set Data Length: TX octets 251 ``` The app enables notifications by writing `0100` to a Client Characteristic Configuration descriptor. The capture view labels that descriptor as handle `0x0013`. ## Confirmed Main Data Flow Normal BMS command frames are written with ATT Write Command to handle `0x0015` (`fff2`). BMS responses arrive as Handle Value Notifications on handle `0x0012` (`fff1`). The live telemetry command is repeatedly sent: ```text TX handle 0x0015 / fff2: D2030000003ED7B9 ``` The battery replies with a `D203` frame with byte count `0x7C`: ```text RX handle 0x0012 / fff1: D2037C <124-byte payload> ``` In this capture, 28 live-data `D2037C` frames were observed and all had valid Modbus CRC16 using the same swapped CRC format implemented in `tools/bulltron_frame.py`. ## Confirmed Read Commands These `D203` read commands were observed on `fff2` with valid CRCs: | Payload | Start register | Count | Observed use | | --- | ---: | ---: | --- | | `D2030000003ED7B9` | `0x0000` | `0x003E` | Main live telemetry block | | `D203003E0009F7A3` | `0x003E` | `0x0009` | Extra cell/pack data block | | `D2030057000CE7BC` | `0x0057` | `0x000C` | Additional info/status block | | `D20300640001D676` | `0x0064` | `0x0001` | Residue time / heating flag | | `D20300800029965F` | `0x0080` | `0x0029` | Settings/system block | | `D20300A900208791` | `0x00A9` | `0x0020` | System/settings block | | `D20300A900254792` | `0x00A9` | `0x0025` | Extended system/settings block | | `D20300C90003C656` | `0x00C9` | `0x0003` | Password/control-code field | | `D20300CC00021797` | `0x00CC` | `0x0002` | Small settings/status field | | `D20300D900054791` | `0x00D9` | `0x0005` | Small settings/status field | The app also performs a phone-time sync immediately after setup: ```text TX handle 0x0015 / fff2: D21000D400031A070A122E2C9FE4 RX handle 0x0012 / fff1: D21000D40003D393 ``` The timestamp payload decodes as: ```text 1A 07 0A 12 2E 2C YY MM DD HH MM SS ``` ## Confirmed Response Byte Counts Observed `D203` response byte-count dispatch values: | Byte count | Count in capture | Meaning | | ---: | ---: | --- | | `0x7C` | 28 | Main 62-word live telemetry block | | `0x52` | 28 | Settings info block | | `0x40` | 18 | Version/device info block | | `0x18` | 38 | Serial number / identifier block | | `0x12` | 42 | Small status block | | `0x06` | 18 | Password/control-code field | | `0x04` | 34 | Small status/ack-style block | | `0x02` | 17 | Residue time / heating flag | | `0x0A` | 1 | Small status block | Some notifications contain more than one `D203` frame concatenated. A parser must split by `D203`, use the response byte count to determine frame length, and then validate CRC per frame. ## Decoded Live Telemetry Example One live `D2037C` frame from the capture decodes to: | Field | Register | Decoded value | | --- | ---: | --- | | Cell 1 | `0x0000` | `3.306 V` | | Cell 2 | `0x0001` | `3.312 V` | | Cell 3 | `0x0002` | `3.308 V` | | Cell 4 | `0x0003` | `3.311 V` | | Pack voltage | `0x0028` | `13.2 V` | | Current raw | `0x0029` | `29925` | | Current | `0x0029` | `-7.5 A` using `(raw - 30000) * 0.1` | | SOC | `0x002A` | `65.2 %` | | State | `0x002F` | `2`, discharging | | Remaining capacity | `0x0030` | `107.5 Ah` | | Temperature sensors | `0x0032` | `2` | | Cycle count | `0x0033` | `3` | | Charge MOS | `0x0035` | `1`, on | | Discharge MOS | `0x0036` | `1`, on | | Cell voltage delta | `0x0038` | `0.006 V` | | Alarm/status words | `0x003A`-`0x003D` | `0000 0000 0000 0010` | Later frames show the same battery around: ```text pack voltage: 13.2 V current: -7.4 A SOC: 65.0-65.2 % remaining capacity: 107.2-107.5 Ah state: 2 / discharging ``` This confirms the decompiled formulas for voltage, current, SOC, remaining capacity, MOS state, cell delta, and alarm bitmaps. ## System Screen Observations After entering the system code `000000`, the capture shows reads of: ```text D20300C90003C656 ``` with a corresponding response: ```text D20306303030303030F6AF ``` The payload is ASCII `000000`, confirming register `0x00C9` length `3` as the password/control-code field. The app also writes `AT+PRODUCTINFO=?` to handle `0x001B` (`fff3`) during the system/settings path: ```text 41542B50524F44554354494E464F3D3F ``` ASCII: ```text AT+PRODUCTINFO=? ``` ## Pairing / Bonding Result This capture supports the earlier code-derived conclusion: normal telemetry did not require an Android pairing dialog or explicit bond setup. The app connected, discovered services, negotiated MTU, enabled notifications, and exchanged BMS frames directly.