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

时间:2017-07-19 18:00:33

标签: ios objective-c bluetooth cbperipheral cbcentralmanager

在我的应用中,我首先使用委托方法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从来没有发现任何附近的方框,其中{?1}}的前缀为“dc-”或其他

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

1)应用程序启动

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

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

4)要求我与BT设备配对,然后我有完整的功能

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

那么......这里发生了什么?为什么它有时会被随机“卡住”?

编辑:

我确实认识到配对和连接不是一回事,所以某些方法的命名非常差。

1 个答案:

答案 0 :(得分:1)

这是蓝牙LE编程的常见问题,因为API都是异步的,因此更加困难。

典型的解决方案是使代码在某些内容无法完成时重试。而且因为你不一定会得到错误信息(你可能不会得到下一个回调)你最终要做的是设置定时器以在合理的时间段后开始重试 - 比如说5-10秒。 / p>

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

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

    当你说蓝灯卡住时,这可能意味着BLE外围设备认为它已经建立了BLE连接。执行此操作的外围设备通常会在连接中断之前停止广告。如果iOS设备认为它没有连接且外围设备没有连接,这会让你处于一种糟糕的情况,你必须以某种方式断开连接。不幸的是,CoreLocation没有为您提供API。

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

    如您所见,蓝牙的循环供电将破坏任何活动连接。但是在iOS上,你不能以编程方式执行此操作。

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