Im working on ios application that works with BLE device. The device uses BLE indications to provide data to the app. The goal is to achieve 100% data retrieval.
According to the hardware team device behaves like this:
CCCD Persistence: Device maintains Client Characteristic Configuration Descriptor (CCCD) with indication-enabled state across reconnections
Resume Point: Device resends indications starting from the last unacknowledged indication before disconnection
No Custom Logic: Follows standard BLE specification for indication reliability
So it is expected that the device restores the indication streams from the last acknowledged one.
My connection routine is:
Discover services
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
connectedPeripherals[peripheral.identifier] = peripheral
peripheral.delegate = self
updatePeripheralState(peripheral.identifier, to: .connected)
print("Starting service discovery...")
peripheral.discoverServices(nil)
}
Discover characteristics:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let error = error {
print("Characteristic discovery failed for service \(service.uuid): \(error.localizedDescription)")
return
}
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
if service.uuid == targetServiceUUID && characteristic.uuid == targetCharacteristicUUID {
print("Found target characteristic! Enabling indications...")
peripheral.setNotifyValue(true, for: characteristic)
print(characteristic.properties.description)
}
}
}
Then the data retrieval:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
print("Error reading characteristic value: \(error.localizedDescription)")
return
}
if characteristic.service?.uuid == targetServiceUUID && characteristic.uuid == targetCharacteristicUUID {
if let data = characteristic.value {
let formatter = DateFormatter()
formatter.timeStyle = .medium
formatter.dateStyle = .none
// data filtering since device is sending some other events sometims
if data.count >= 15 {
let event = decodeBytes(bytes: data)
let now = Date()
let timestamp = timestampFormatter.string(from: now)
print("[\(timestamp)] Auto Increment: \(event.autoIncrement) Type: \(event.type)")
}
} else {
print("Received indication with no data")
}
}
}
Using PacketLogger from xcode toolbox i have confirmed that:
The device starts sending indications right after didConnect finishes
The phone is sending ACKS for those indications
Indications are not reaching didUpdateValueFor until peripheral.setNotifyValue(true, for: characteristic) properly executes
This mekes me drop some data data on each reconnect.
I already know I can do better in terms of service and characteristics discovery: I should discover only that one which is giving me the indications.
But my intuition is: discover only the service and characteristic i care about will minimize the impact, but not guarantee 100% data retrieval
Is this expected and confirmed CoreBluetooth behavior?
2
0
124