xcode iPhone 在重新连接时未发现蓝牙 LE 标签上的服务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12004623/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 01:15:31  来源:igfitidea点击:

iPhone does not discover Services on a Bluetooth LE tag on reconnection

iphonexcodeios5bluetoothcore-bluetooth

提问by Manju Kiran

I am working on a Bluetooth LE application for iOS. I am using the Core Bluetooth framework within iOS to handle all communications.

我正在开发适用于 iOS 的蓝牙 LE 应用程序。我在 iOS 中使用 Core Bluetooth 框架来处理所有通信。

Question & Description:

问题和描述:

When I use a single tag, despite numerous connections and disconnections, the single tag connects seamlessly and the phone discovers it services.

Also, when multiple Bluetooth LE tags connect for the first time, they connect seamlessly and the phone discovers their services.

When the tags get disconnected and then reconnect to the phone, the tags connect fine. But one of the two tags (either one) does not seem to advertise its services. i.e when the app is open and the tag reconnects, the DiscoverServicesmethod does not call the didDiscoverServicesdelegate.

当我使用单个标签时,尽管有多次连接和断开连接,但单个标签会无缝连接并且手机会发现它的服务。

此外,当多个蓝牙 LE 标签首次连接时,它们会无缝连接并且手机会发现它们的服务。

当标签断开连接然后重新连接到手机时,标签连接正常。但是这两个标签之一(其中一个)似乎没有宣传其服务。即当应用程序打开并且标签重新连接时,DiscoverServices方法不会调用didDiscoverServices委托。

Why is this happening only when connection with multiple devices takes place.

为什么只有在与多个设备连接时才会发生这种情况。

I have set the peripheral.delegatecorrectly. I have tried everything, including doing repeated re-connect, repeated DiscoverServices calls to the tag. Nothing seems to work.

我已经正确设置了peripheral.delegate。我已经尝试了所有方法,包括重复重新连接、重复对标签进行 DiscoverServices 调用。似乎没有任何效果。

How can I re-connect to multiple tags to the phone and still discover all services.

Please help

Thanks,
Manju

如何将多个标签重新连接到手机并仍然发现所有服务。

请帮忙

谢谢,
Manju

采纳答案by Manju Kiran

Turns out that there was command I was issuing to the device in the"didDiscovercharacteristicsForService" delegate method which was causing the connection instability.

If you are facing similar issues, I suggest you to let the delegate method complete without any intervention (of any kind) and pass the CBPeripheral to another function designed by you to pass any values / issue a command to the devices.

Thanks anyway Wilhemsen.

结果是我在“ didDiscovercharacteristicsForService”委托方法中向设备发出了导致连接不稳定的命令。

如果您面临类似的问题,我建议您让委托方法在没有任何干预(任何类型)的情况下完成,并将 CBPeripheral 传递给您设计的另一个函数,以向设备传递任何值/发出命令。

无论如何,谢谢威廉森。

So the steps are as follows..
1> Search for Tag,
2> If in range, CONNECT to Tag
3> If Connected, call DISCOVER services method (do not interrupt)
4> IN DidDiscoverServices, call DISCOVER Characteristics Method
..
In DidDiscoverCharacteristics Method, wait until all Characteristics are discovered.. Then , at the end, call a function in your code that will do the necessary setup..
...
Code Sample

所以步骤如下..
1> Search for Tag,
2> If in range, CONNECT to Tag
3> If Connected, 调用DISCOVER services方法(不要中断)
4> IN DidDiscoverServices, 调用DISCOVER Characteristics Method
..
In DidDiscoverCharacteristics方法,等到所有的特征都被发现。然后,最后,在你的代码中调用一个函数来进行必要的设置
......
代码示例

-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{

    for (int i=0; i < peripheral.services.count; i++) {
        CBService *s = [peripheral.services objectAtIndex:i];
        printf("Fetching characteristics for service with UUID : %s\r\n",[self CBUUIDToString:s.UUID]);
        [peripheral discoverCharacteristics:nil forService:s];
    }

}


- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{        
    if (!error)
    {
        CBService *s = [peripheral.services objectAtIndex:(peripheral.services.count - 1)];
        if([self compareCBUUID:service.UUID UUID2:s.UUID])
        {
           // This is when the **Tag** is completely connected to the Phone and is in a mode where it can accept Instructions from Device well.

            printf("Finished discovering characteristics");
           // Now Do the Setup of the Tag
            [self setupPeripheralForUse:peripheral];

        }
    }

    else
    {
        printf("Characteristic discorvery unsuccessfull !\r\n");
    }
}

-(void) setupPeripheralForUse:(CBPeripheral *)peripheral
{

    // DO all your setup in this function. Separate Perpiheral Setup from the process that synchronizes the tag and the phone. 

}

回答by jhalla14

I had the same problem but realized that I wasn't setting the delegateto CBPeripheralafter didConnectPeripheralis called.

我遇到了同样的问题,但意识到我没有设置delegateto CBPeripheralafterdidConnectPeripheral被调用。

- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"Peripheral Connected: %@", peripheral.name);

    peripheral.delegate = self;

    if (peripheral.services) {
        [self peripheral:peripheral didDiscoverServices:nil];
    } else {
        [peripheral discoverServices:@[[CBUUID UUIDWithString:CUSTOM_UUID]]];
    }  
}

回答by P.L.

I was facing a similar issue with CoreBluetooth to connect to Bluetooth LE devices, in my case connecting to iOS devices (peripherals) from my Mac (central).

我在使用 CoreBluetooth 连接到蓝牙 LE 设备时遇到了类似的问题,在我的情况下是从我的 Mac(中央)连接到 iOS 设备(外设)。

If I get you correctly, the pattern is quite consistent, the first time I run my Mac app for debuging, it always detected and connected to any bluetooth LE devices (peripherals), most importantly, it also discover their services/characteristics fine. The problem starts on the second run (for example, change some code, hit cmd-R to relaunch the debug). The central still detects peripherals and connects to them, but, it fails to discover any services/characteristics. In other words, the delegate peripheral:didDiscoverServices:and peripheral:didDiscoverCharacteristicsForService:error:never get called.

如果我理解正确,模式非常一致,当我第一次运行我的 Mac 应用程序进行调试时,它总是检测到并连接到任何蓝牙 LE 设备(外设),最重要的是,它还可以很好地发现它们的服务/特性。问题从第二次运行开始(例如,更改一些代码,按 cmd-R 重新启动调试)。中央仍然检测外围设备并连接到它们,但是,它无法发现任何服务/特征。换句话说,委托peripheral:didDiscoverServices:peripheral:didDiscoverCharacteristicsForService:error:永远不会被调用。

The solution after a lot of trial and errors, is surprisingly simple. It seems that CoreBluetooth caches servicesand characteristicsfor peripherals that are still connected, although locally it looks like it had been disconnected to the app, the peripheral still maintains a bluetooth connection to the system. For these type of connections, there is no need to (re)discover the services and characteristics, just access them directly from the peripheral object, check for nilto know if you should discover them. Also, as mentioned, since the peripheral is in a state that is in between connections, it is best to call cancelPeripheralConnection:right before attempting to connect. The gist of it as following, assuming we already discovered the peripheral to connects to:

经过大量试验和错误后的解决方案出奇地简单。似乎 CoreBluetooth 缓存servicescharacteristics仍然连接的外围设备,尽管在本地看起来它已与应用程序断开连接,但外围设备仍然保持与系统的蓝牙连接。对于这些类型的连接,不需要(重新)发现服务和特征,只需直接从外围对象访问它们,检查nil是否应该发现它们。此外,如前所述,由于外围设备处于连接之间的状态,因此最好cancelPeripheralConnection:在尝试连接之前调用。其要点如下,假设我们已经发现要连接的外设:

-(void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    [central cancelPeripheralConnection:peripheral]; //IMPORTANT, to clear off any pending connections    
    [central connectPeripheral:peripheral options:nil];
}

-(void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    peripheral.delegate = self;

    if(peripheral.services)
        [self peripheral:peripheral didDiscoverServices:nil]; //already discovered services, DO NOT re-discover. Just pass along the peripheral.
    else
        [peripheral discoverServices:nil]; //yet to discover, normal path. Discover your services needed
}

-(void) peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    for(CBService* svc in peripheral.services)
    {
        if(svc.characteristics)
            [self peripheral:peripheral didDiscoverCharacteristicsForService:svc error:nil]; //already discovered characteristic before, DO NOT do it again
        else
            [peripheral discoverCharacteristics:nil
                                     forService:svc]; //need to discover characteristics
    }
}

-(void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    for(CBCharacteristic* c in service.characteristics)
    {
        //Do some work with the characteristic...
    }
}

This works well for me for a CBCentralManager in Mac app. Never tested it in iOS, but I assume it should be quite similar.

对于 Mac 应用程序中的 CBCentralManager,这对我来说很有效。从未在 iOS 中测试过它,但我认为它应该非常相似。

回答by Grahambo

I've been having the same issue. it seems to occur about 1/3 the time in my case. I tried the solution provided by P.L. but I had no success on iOS. There are perhaps many moving pieces at work here which can contribute to this problem (bluetooth device firmware, CoreBluetooth, etc.) but I solved it by simply keeping track of the devices which are pending service/characteristic discovery in an NSMutableDictionary and using GCD to check if it had completed it's discovery in a reasonable amount of time and trying again if necessary. So something like this:

我一直有同样的问题。在我的情况下,它似乎发生了大约 1/3 的时间。我尝试了 PL 提供的解决方案,但在 iOS 上没有成功。这里可能有很多移动部分在起作用,可能会导致这个问题(蓝牙设备固件、CoreBluetooth 等),但我通过简单地跟踪 NSMutableDictionary 中挂起的服务/特征发现的设备并使用 GCD 来解决它检查它是否在合理的时间内完成了它的发现,并在必要时重试。所以像这样:

- (void)requestDiscoverServicesForPeripheral:(CBPeripheral *)peripheral 
{
      peripheral.delegate = self;
      NSString *uuid =[self stringUUIDForUUID:peripheral.UUID];
      NSLog(@"discovering %d services ", self.interestingServices.count);
      [peripheral discoverServices:self.interestingServices];
      [self.pendingConnectionDevices setObject:peripheral forKey:uuid];
       __weak typeof(self) weakSelf = self;
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if ([weakSelf.pendingConnectionDevices objectForKey:uuid]) {

               //request discover services again
               NSLog(@"services did not discover, requesting again!");
               [weakSelf.btManager cancelPeripheralConnection:peripheral];
               [weakSelf.btManager connectPeripheral:peripheral options:nil];
         }
    });
 }

Then, when peripheral:didDiscoverServices:errorcalls back I remove it from my pendingConnectionDevices dictionary. This seems to work pretty well. I've seen it try to discover services up to 3 times before succeeding. I hope this helps.

然后,当peripheral:didDiscoverServices:error回电时,我将它从我的 pendingConnectionDevices 字典中删除。这似乎工作得很好。我已经看到它在成功之前尝试发现服务多达 3 次。我希望这有帮助。

回答by Orange

Sometimes it's hardware issue. I just encountered a case that the hardware will enter a sleeping mode which is scannable, connectable, but not calling back at all for discoverServices.

有时是硬件问题。我刚刚遇到一个情况,硬件将进入睡眠模式,该模式可扫描、可连接,但对于discoverServices 根本不回调。

Also there's a situation that happened to me when developing BLE all night and suddenly, the device become silence for discoverServices no matter what I did. Finally I found it will become normal if I reboot my iPhone5s.

此外,我在整夜开发 BLE 时发生了一种情况,突然间,无论我做什么,设备都会为 findServices 静音。最后我发现如果我重新启动我的 iPhone5s,它会变得正常。

And one day the bluetooth hardware engineer told me that the spec of BLE only says device must be connectable again within 30 seconds. So if I connect and disconnect over and over again, it may not working as expected... Which I still doubt it.

有一天,蓝牙硬件工程师告诉我,BLE 的规范只说设备必须在 30 秒内再次连接。因此,如果我一遍又一遍地连接和断开连接,它可能无法按预期工作......我仍然对此表示怀疑。

回答by Maihan Nijat

I was trying to apply all above answers but it wasn't working for me because I was missing CBPeripheralDelegateon class.

我试图应用以上所有答案,但它对我不起作用,因为我缺课CBPeripheralDelegate了。

class BLEHandler : NSObject, CBCentralManagerDelegate, CBPeripheralDelegate{

  func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {

    peripheral.delegate = self

    peripheral.discoverServices(nil)

    print("Connected: \(peripheral.state == .connected ? "YES" : "NO")")
  }

  func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {

    for service: CBService in peripheral.services! {
        peripheral.discoverCharacteristics(nil, for: service)
        print(service)
    }
  }

}

回答by HotJard

My headache was caused by lack of strong reference to peripheral

我的头痛是由于缺乏对外围设备强烈参考引起的

So I've add the reference and the problem is gone

所以我添加了参考,问题就消失了

private var activePeripheral: CBPeripheral?

func connect(_ peripheral: CBPeripheral) {
        disconnect(peripheral)
        activePeripheral = peripheral
        self.central.connect(peripheral, options: nil)
    }

回答by Mark

I'm doing everything you say in your answer, but I still see this issue from time to time. My guess is Core Bluetooth gets into a weird state, probably due to a specific sequence of connection, subscription and disconnection.

我正在做你在回答中所说的一切,但我仍然不时看到这个问题。我的猜测是 Core Bluetooth 进入一个奇怪的状态,可能是由于特定的连接、订阅和断开顺序。

The only way I've found to fix the problem is to restart the iOS device. This issue might be fixed in iOS 7.1 though.

我发现解决问题的唯一方法是重新启动 iOS 设备。不过,这个问题可能会在 iOS 7.1 中得到修复。