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

Objective-c bluetooth says it's connected but CBCentralManager does not discover it

在我的应用程序中,我首先使用委托方法 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 连接以某种方式获得 "stuck"。我所说的卡住的意思是它永远不会真正连接起来。我的盒子上的蓝灯亮着,就好像系统已连接到它一样,但在我的应用程序中却发生了这种情况。

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

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

3) didDiscoverPeripheral 从来没有在附近发现过 "dc-" 某物或其他

中前缀为 kCBAdvDataLocalName 的任何盒子

如果我要进入 iOS 设置 -> 蓝牙 -> 忘记这个设备(必须按两次,这让我觉得它是 "stuck" 不知何故)然后实际关闭BT 选项一起...等待 2 秒,然后再次打开它...然后重新启动应用程序,一切正常。

1) 应用启动

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

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

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

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

所以...这是怎么回事?为什么它有时似乎随机 "stuck"?

编辑:

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

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

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

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

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

旁注:

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

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

如您所见,重新打开蓝牙电源会中断任何活动连接。但是在 iOS 上,您不能以编程方式执行此操作。

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