Objective-c蓝牙说它已连接但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甚至从未发现任何附近的盒子,其中kCBAdvDataLocalName中的前缀为“dc-”某事或其他

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

1)应用程序启动

2)看不到以前连接过的设备

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

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

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

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

编辑:

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

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

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

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

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

边注:

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

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

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

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