# Berger LFP BLE Flow This is the app flow used to obtain battery information such as discharge current, SOC, voltage, temperatures, cell voltages, MOS state, and estimated charge/discharge time. ## Evidence - App package: `com.jbd.berger` - Version: `1.0.3` - Main bundle: `assets/apps/__UNI__F61076D/www/app-service.js` - Source-map style log labels embedded in bundle: - `utils/BLE.ts` - `App.vue` - dashboard/control/parameter pages ## Connection Sequence 1. `uni.openBluetoothAdapter()` 2. `uni.startBluetoothDevicesDiscovery()` with service filter `ff00` 3. `uni.onBluetoothDeviceFound()` records devices and RSSI 4. `uni.createBLEConnection()` with 10 second timeout 5. `uni.getBLEDeviceServices()` 6. `uni.getBLEDeviceCharacteristics()` 7. `uni.notifyBLECharacteristicValueChange()` on `ff01` 8. `uni.onBLECharacteristicValueChange()` buffers incoming bytes 9. `uni.writeBLECharacteristicValue()` sends command chunks to `ff02` with `writeType: "writeNoResponse"` The app chunks outgoing buffers into 20-byte pieces before writing. ## Services And Characteristics - Service: `0000ff00-0000-1000-8000-00805f9b34fb` - Notify/read: `0000ff01-0000-1000-8000-00805f9b34fb` - Write: `0000ff02-0000-1000-8000-00805f9b34fb` ## Pairing / Bonding - Needs Android bond before normal telemetry: likely no. - Evidence: the app uses uni-app BLE APIs for connection, service discovery, notifications, and writes. I did not find a normal-path bond request in the app bundle. - Caveat: a peripheral firmware could still enforce encryption independently, but the app flow itself does not show pairing as a telemetry prerequisite. ## Getting Live Values The app sends: ```text DD A5 03 00 FF FD 77 ``` The response uses: ```text DD 03 77 ``` Important payload fields for command `0x03`: | Payload bytes | Formula | Unit | Field | | ---: | --- | --- | --- | | `0..1` | big-endian / 100 | V | pack voltage | | `2..3` | signed big-endian / 100 | A | current | | `4..5` | big-endian / 100 | Ah | remaining capacity | | `6..7` | big-endian / 100 | Ah | nominal capacity | | `8..9` | raw | cycles | cycle count | | `10..11` | JBD bitfield | date | production date | | `12..13` | nonzero bitmap | boolean | balancing | | `16..17` | nonzero bitmap | boolean | protection | | `18` | decimal digits | version | software version | | `19` | raw | % | SOC | | `20` | `1 = charge`, `2 = discharge`, `3 = both` | boolean | MOS state | | `22` | raw count | count | temperature sensor count | | `23..` | `(raw - 2731) / 10` | deg C | temperatures | Production date bitfield: ```text day = value & 0x1F month = (value >> 5) & 0x0F year = 2000 + (value >> 9) ``` Estimated time is computed locally: ```text if current_A > 0: time_to_full = (nominal_Ah - remaining_Ah) / current_A if current_A < 0: time_to_empty = remaining_Ah / abs(current_A) ``` ## Cell Voltages The app sends: ```text DD A5 04 00 FF FC 77 ``` The response command `0x04` payload is a sequence of big-endian 16-bit cell voltages scaled by `0.001 V`.