ios - Objective-c 蓝牙说它已连接但 CBCentralManager 没有发现它

标签 ios objective-c bluetooth cbperipheral cbcentralmanager

在我的应用程序中,我首先使用委托(delegate)方法 retrieveConnectedPeripheralsWithServices 搜索以前连接的设备,如果没有找到,然后扫描附近的设备。这是代码

- (void)searchForPeripherals {
    NSLog(@"***  scanning for Bluetooth peripherals...  ***");

    //Check to see if any devices were connected already
    if(self.ourPeripheral != nil) {
        [self connectToDevice];

    } else {
        //Search for previously paired devices...
        bool foundPairedDevices = [self checkForAndConnectToPreviouslyPairedDevices];

        //None found, scan for new devices nearby...
        if(!foundPairedDevices) {
            NSLog(@"No paired boxes found, checking device powered state...");

            //If the app user has "bluetooth" option turned on
            if(self.centralManager.state == CBCentralManagerStatePoweredOn) {
                NSLog(@"--- central manager powered on.");
                NSLog(@"...begin scanning...");

                [self.centralManager scanForPeripheralsWithServices:nil
                                                            options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @(YES)}];

            //No, user has "bluetooth" turned off
            } else {
                NSLog(@"--- central manager is NOT powered on.");
            }
        }
    }
}


- (bool)checkForAndConnectToPreviouslyPairedDevices {
    NSLog(@"our peripheral device: %@", self.ourPeripheral);

    NSArray *dcBoxesFound = [self.centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
    NSLog(@"Previously paired DC boxes?: %@", dcBoxesFound);

    //Are there any previously paired devices?
    if(dcBoxesFound != nil && [dcBoxesFound count] > 0) {
        CBPeripheral *newestBoxFound = [dcBoxesFound firstObject];
        newestBoxFound.delegate = self;

        self.ourPeripheral = newestBoxFound;
        self.deviceUUID = [CBUUID UUIDWithString:[newestBoxFound.identifier UUIDString]];

        [self connectToDevice];

        return YES;

    } else {
        return NO;
    }
}


- (void)connectToDevice {
    if(self.ourPeripheral != nil) {
        [self.centralManager connectPeripheral:self.ourPeripheral options:nil];
    }
}


- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI {

    NSLog(@"scanned and found this peripheral: %@", peripheral);
    NSLog(@"advertisment data: %@", advertisementData);

    if(!self.isConnectedToDevice && [[advertisementData objectForKey:@"kCBAdvDataLocalName"] localizedCaseInsensitiveContainsString:@"dc-"]) {
        NSLog(@"\n");
        NSLog(@"-------------------------------------------");
        NSLog(@"DC peripheral found! Attempting to connect to the following...: %@", peripheral);

        peripheral.delegate = self;
        self.ourPeripheral = peripheral;

        self.deviceUUID = [CBUUID UUIDWithString:[peripheral.identifier UUIDString]];

        [self connectToDevice];

        NSLog(@"-------------------------------------------");
        NSLog(@"\n");
    }
}


- (void)centralManager:(CBCentralManager *)central
  didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@"--- didConnectPeripheral");

    peripheral.delegate = self;
    [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
}

问题

当我启动应用程序时,它通常可以正常连接。一切都很好。然后在非常不可预测和奇怪的时候,BT 连接会以某种方式“卡住”。我所说的卡住的意思是它永远不会真正连接起来。我盒子上的蓝灯亮起,就好像系统已连接到它一样,但在我的应用程序中却发生了这种情况。

1) retrieveConnectedPeripheralsWithServices 方法找到以前连接的设备的空数组,告诉调用它的方法没有找到以前连接的设备。

2) scanForPeripheralsWithServices:nil 方法被触发并开始使用 didDiscoverPeripheral 方法进行扫描。

3) didDiscoverPeripheral 从未在附近发现任何在 kCBAdvDataLocalName 中前缀为“dc-”或其他东西的盒子

如果我要进入 iOS 设置 -> 蓝牙 -> 忘记这个设备(必须按两次,这让我觉得它以某种方式“卡住了”)然后实际上一起关闭 BT 选项......等等2 秒,然后再次打开它...然后重新启动应用程序,一切正常。

1) 应用启动

2) 查看之前没有连接过的设备

3) 扫描外围设备并找到我的前缀框名称“dc-whatever”

4) 要求我与 BT 设备配对,然后我恢复了全部功能

如果我随后关闭应用程序并重新启动它,retrieveConnectedPeripheralsWithServices 会毫无问题地找到我之前连接的盒子并无缝连接...

那么……这是怎么回事?为什么它有时似乎随机“卡住”?

编辑:

我确实意识到配对和连接不是一回事,所以有些方法的命名非常糟糕。

最佳答案

这是 Bluetooth LE 编程的一个常见问题,由于 API 都是异步的,因此变得更加困难。

典型的解决方案是让代码在某些事情未完成时重试。而且因为您不一定会收到错误消息(您可能不会收到下一个回调),您最终要做的是设置计时器以在合理的时间段后开始重试——比如 5-10 秒。

正如您所注意到的,在某些情况下(例如在后台,如果设备已连接,或以其他方式停止广播)您只会收到一个对 didDiscoverPeripheral 的回调。因此,您确实需要保存 CBPeripheral 对象的副本,以便在发生超时时可以重新使用它。这是我将使用的基本逻辑:

  1. 当您在 didDiscoverPeripheral 中发现新的外围设备时,将对象实例保存在名为 peripheralForConnectionAttempt 的变量中,然后启动 5 秒计时器。
  2. 如果连接在 didConnectPeripheral 中成功完成,则将 peripheralForConnectionAttempt 的值设置为 nil。
  3. 当计时器关闭时,检查 peripheralForConnectionAttempt 是否不为 nil,如果是,则重试此对象,就像调用了 didDiscoverPeripheral 一样。您可能希望有一个计数器将重试次数限制为 10 次或类似次数。

边注:

当你说蓝灯卡住时,这可能意味着BLE Peripheral设备认为它已经建立了BLE连接。执行此操作的外设通常会停止广播,直到连接断开。如果 iOS 设备认为它没有连接而外设连接了,这会让你处于一个糟糕的境地,你必须以某种方式断开连接。不幸的是,CoreLocation 没有提供 API 来执行此操作。

在一个项目中,如果发生通信超时,我通过断开连接在蓝牙外设固件中实现了这一点。但是如果你不控制固件,你显然不能这样做。

如您所见,为蓝牙循环供电会中断任何事件连接。但在 iOS 上,您不能以编程方式执行此操作。

如果您不控制外围固件,并且无法想出任何方法来避免锁定与设备的连接(也许通过改变您的操作时间?)那么您可能别无选择,只能简单地检测问题,适当时提醒用户,并在用例允许的情况下指导用户循环使用蓝牙。我知道,这不是一个理想的解决方案。

关于ios - Objective-c 蓝牙说它已连接但 CBCentralManager 没有发现它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45197868/

相关文章:

ios - 将 GMSMarker 添加到 map 时,应用程序仅在 iPhone 5 上崩溃 : EXC_BAD_ACCESS

IOS:如何在 UIMapView 上显示标签文本值

Android在蓝牙套接字上设置超时

iphone - 滑动后 UITableViewCell 上的自定义按钮

ios - 如何更改 UINavigationController 推送过渡样式?

ios - 我想在单击相应按钮时执行获取显示不同的 Controller

iphone - 设置参数后,Restkit sendObject 不发布对象

android - 如何在Android中查找蓝牙配对失败的原因?

java - 尝试连接 BluetoothGatt 时回调中出现未处理的异常

ios - 无限旋转 UIView 并暂停/停止动画,保持状态,并从当前状态继续旋转