Is there a way to discover BLE peripheral service

2020-04-07 06:44发布

问题:

I find that my implementation of the BLE protocol in iOS7 to be very slow in the start up phase. The startup sequence amounts to ~68% of the whole execution time in my app.

What can I do to make it faster?

I've timed it, and here's what I get.

     t     dt   
37.598          [BLE] Discovered peripheral at RSSI -27 with UUID:XYZ
37.599  0.001   [BLE] Connecting to peripheral                                                                            
37.602  0.003   [BLE] Scanning stopped                                                                                           
37.685  0.083   [BLE] Peripheral connected                                                                                
38.48   0.795   [BLE] Discovered service  
38.599  0.119   [BLE] Discovered characteristic    

As you can see there's a huge bottle neck before discovering the service.

My startup code simplified:

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    switch (central.state) {
        case CBCentralManagerStatePoweredOn:
            [central scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUuid]]
                                            options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}];
            break;
        case CBCentralManagerStatePoweredOff:
            [central stopScan];
            break;
        default:
            break;
    }
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    if (self.discoveredPeripheral != peripheral) {
        self.discoveredPeripheral = peripheral; // Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
        [central connectPeripheral:peripheral options:nil];
        [central stopScan];
    }
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    [peripheral discoverServices:@[[CBUUID UUIDWithString:kServiceUuid]]];
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:@[array of characteristics]
                                 forService:service];
    }
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    ...
}

EDIT

I've learned that similar apps on Android does this ten times faster (making the Android app feel snappier -> better user experience) so I'm curious if it's my implementation, BLE layer or hardware that's the bottleneck. It's tested on an iPhone 4S.

回答1:

When you encrypt the connection, iOS should cache the GATT database. Therefore, subsequent discovery calls after the first one should happen instantaneously.

Since iOS 7, even characteristic values are cached, meaning that you can read static values like a "Device name" through the characteristic's value property. If you want to update them, you still have to issue a Read Characteristic Value request.

For details regarding the caching behavior, check WWDC 2013 session 703 from slide 48 (should probably watch the corresponding part in the video).

For connection and discovery time, it's mainly the advertisement interval. There are a couple of advertisement intervals that are recommended by Apple for best performance in the Bluetooth Accessory Design Guidelines For Apple Products (section 3.5 Advertising Interval). Also, you should disable scanning when you connect, as scanning slows down the connection process by about 55x.

Note that the iOS based limitations on the number of packets sent per connection event should not affect discovery time noticeably (unless you have a giant GATT database and are querying the whole thing). Those limitations should only become visible for "Write Without Response" and "Characteristic Value Notification", as per the LE protocol design.



回答2:

There's no framework-level API to improve the discovery speed. BTLE is co-existing with Classic Bluetooth (at the system framework level) and Wi-Fi (at the antenna level), so the maximum time on antenna is limited by the system.