核心蓝牙在扫描特定CBUUID时找不到外围设备 [英] Core Bluetooth doesn't find peripherals when scanning for specific CBUUID

查看:2302
本文介绍了核心蓝牙在扫描特定CBUUID时找不到外围设备的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题似乎「已回答」这里 a>,但没有任何代码来显示他们做了不同的,我不得不问一个新的问题。



我有自己的代码具有相同的行为,其中扫描对于使用Core的特定CBUUID在OS X上的蓝牙的CBCentralManager不会发现作为CBPeripheralManager的外围设备的iOS设备(除非之前已经发现它的服务)。要查看我的代码是否有问题,我下载了 Apple的示例代码。在两个iOS设备上运行示例代码的工作原理如下,但是当将CBCentralManager代码复制到OS X应用程序时,它无法找到iOS设备。



为OS X应用上传了一个Xcode项目,它在托管在WikiUpload 上,因为这似乎是最不容易。如果有人喜欢,也可以在我的托管上复制。



这里是OS X项目中的AppDelegate.m代码(CoreBluetooth框架在项目中链接):

  #import< CoreBluetooth / CoreBluetooth.h> 

@interface AppDelegate()< CBCentralManagerDelegate,CBPeripheralDelegate>

@property(强,非原子)CBCentralManager * centralManager;
@property(strong,nonatomic)CBPeripheral * discoveredPeripheral;
@property(strong,nonatomic)NSMutableData * data;

@property(weak)IBOutlet NSWindow * window;
@end

@implementation AppDelegate

@synthesize centralManager = _centralManager,discoveredPeripheral = _discoveredPeripheral,data = _data;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
//在这里插入代码来初始化应用程序
//启动CBCentralManager
_centralManager = [CBCentralManager alloc] initWithDelegate:self queue:nil];

//在某处存储传入数据
_data = [[NSMutableData alloc] init];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
//在此插入代码以删除应用程序
}


#pragma mark - 中央方法



/ ** centralManagerDidUpdateState是必需的协议方法。
*通常,你会检查其他状态,以确保当前设备支持LE,上电等。
*在这个实例中,我们只是使用它来等待CBCentralManagerStatePoweredOn表示
*中央已准备好使用。
* /
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
if(central.state!= CBCentralManagerStatePoweredOn){
// ,你会正确地处理所有的状态
return;
}

//状态必须是CBCentralManagerStatePoweredOn ...

// ...所以开始扫描
[self scan];

}


/ **扫描外设 - 专门为我们的服务的128位CBUUID
* /
- (void)scan
{
//这里没有什么,不像在iOS上它找到设备

[self.centralManager scanForPeripheralsWithServices:@ [[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]
options:@ {CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];

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

NSLog(@扫描开始);
}


/ **只要发现广告TRANSFER_SERVICE_UUID的外设,就会发生此回调。
*我们检查RSSI,以确保它足够接近我们感兴趣的,如果是,
*我们开始连接过程
* /
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{

NSLog @ at%@,peripheral.name,RSSI);

//发出RSSI检查

if(self.discoveredPeripheral!= peripheral){

//保存外设的本地副本,所以CoreBluetooth不能摆脱它
self.discoveredPeripheral = peripheral;

//并连接
NSLog(@连接到外围设备%@,外设);



[self.centralManager connectPeripheral:peripheral options:nil];
}
}


/ **如果连接由于某种原因失败,我们需要处理它。
* /
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)外围错误:(NSError *)错误
{
NSLog连接到%@。(%@),外围设备,[error localizedDescription]);
[self cleanup];
}


/ **我们已经连接到外设,现在我们需要发现服务和特性来找到传输特性。
* /
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@Peripheral Connected);

//停止扫描
[self.centralManager stopScan];
NSLog(@扫描已停止);

//清除我们可能已经有的数据
[self.data setLength:0];

//确保我们得到发现回调
peripheral.delegate = self;

//仅搜索与我们的UUID匹配的服务
[peripheral discoverServices:@ [[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]];
}


/ **发现传输服务
* /
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices: *)error
{
if(error){
NSLog(@发现服务错误:%@,[error localizedDescription]);
[self cleanup];
return;
}

//发现我们想要的特性...

//循环遍历新填充的peripheral.services数组,以防万一有多个。 for service:service];
for(CBService * service in peripheral.services){
[peripheral discoverCharacteristics:@ [[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]
}
}


/ **发现传输特性。
*一旦找到了,我们想订阅它,这让外围设备知道我们想要它包含的数据
* /
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)服务错误:(NSError *)错误
{
//处理错误(如果有)
if(错误){
NSLog发现特性:%@,[error localizedDescription]);
[self cleanup];
return;
}

//再次,我们循环遍历数组,以防万一。
for(CBCharacteristic * characteristic in service.characteristics){

//并检查是否是正确的
if([characteristic.UUID isEqual:[CBUUUUUUWiString:TRANSFER_CHARACTERISTIC_UUID] ]){

//如果是,请订阅它
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}

//一旦完成,我们只需要等待数据输入。
}


/ **这个回调让我们知道通过特性
* /
- (void)peripheral:(CBPeripheral *)外围didUpdateValueForCharacteristic:(CBCharacteristic *)特性错误:(NSError *)error
{
if(error){
NSLog(@错误发现特性:%@,[error localizedDescription]);
return;
}

NSString * stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];

//我们有我们需要的一切吗?
if([stringFromData isEqualToString:@EOM]){

//我们有,所以显示数据
//[self.textview setText:[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]];
NSLog(@Text:%@,[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]);
//取消我们订阅的特性
[peripheral setNotifyValue:NO forCharacteristic:characteristic];

//并断开与peripehral的连接
[self.centralManager cancelPeripheralConnection:peripheral];
}

//否则,只是添加数据到我们已经有了
[self.data appendData:characteristic.value];

//记录它
NSLog(@Received:%@,stringFromData);
}


/ **外围设备让我们知道我们的订阅/取消订阅是否发生
* /
- (void)peripheral :( CBeperipheral *)外围didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)特性错误:(NSError *)错误
{
if(错误){
NSLog(@更改通知状态时出错:%@ .localizedDescription);
}

//如果不是传递特性则退出
if(![characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]){
return;
}

//通知已启动
if(characteristic.Notifying){
NSLog(@通知开始于%@,特征);
}

//通知已停止
else {
//因此与外设断开连接
NSLog(@通知已在%@停止,特征);
[self.centralManager cancelPeripheralConnection:peripheral];
}
}


/ **一旦断开连接,我们需要清除外设的本地副本
* /
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)外围错误:(NSError *)错误
{
NSLog(@Peripheral Disconnected);
self.discoveredPeripheral = nil;

//我们断开连接,所以再次扫描
[self scan];
}


/ **当事情出错或者连接完成时调用。
*如果有任何订阅,或者直接断开连接,则取消任何订阅。
*(如果涉及订阅,didUpdateNotificationStateForCharacteristic将取消连接)
* /
- (void)cleanup
{
//如果我们're not connected
if(!self.discoveredPeripheral.isConnected){
return;
}

//看看我们是否订阅了外设上的特性
if(self.discoveredPeripheral.services!= nil){
for(CBService *服务在self.discoveredPeripheral.services){
if(service.characteristics!= nil){
for(CBCharacteristic * characteristic in service.characteristics){
if([characteristic.UUID isEqual: [CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]){
if(characteristics.isNotifying){
//正在通知,因此取消订阅
[self.discoveredPeripheral setNotifyValue:NO forCharacteristic:characteristic];

//我们完成了。
return;
}
}
}
}
}
}

//如果我们有这么远,重新连接,但我们没有订阅,所以我们只是断开
[self.centralManager cancelPeripheralConnection:self.discoveredPeripheral];
}

在AppDelegate.h中有UUID定义:

  #ifndef LE_Transfer_TransferService_h 
#define LE_Transfer_TransferService_h

#define TRANSFER_SERVICE_UUID @E20A39F4-73F5-4BC4-A12F-17D1AD07A961
#define TRANSFER_CHARACTERISTIC_UUID @08590F7E-DB05-467E-8757-72F6FAEB13D4

#endif

这里有什么问题?根据上面的链接问题,服务必须是广告包的一部分,但是据我所知,这正是iOS外设正在做的事情。

  [self.peripheralManager startAdvertising:@ {CBAdvertisementDataServiceUUIDsKey:@ [[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]}]; Corebluetooth可能非常令人沮丧,你可以使用Corebluetooth来解决这个问题。这里有一些东西要尝试:



#1:已连接以停止广告的外围设备。如果您成功连接到外设,则需要重新启动广告。



#2:iOS缓存已发现的状态并提供服务。没有程序化的方式来刷新/清除缓存。尝试在iOS设备和Mac上禁用BT,然后重新启用。然后尝试其他连接。



#3:您的UUID有问题。尝试扫描UUID参数设置为nil的外设。



#4:如果Wi-Fi已打开,Mac上的BT连接可能会很麻烦。请尝试在您的Mac上停用WLAN,然后重试。我发现BTLE完全无法使用WiFi启用,所以我不得不使用以太网,当我在MacBook上的任何BTLE开发。


It seems this question was "answered" here, but without any code to show what they did differently I'm having to ask a new question.

I have my own code with the same behaviour, where scanning for specific CBUUIDs using Core Bluetooth's CBCentralManager on OS X doesn't discover an iOS device acting as a peripheral with CBPeripheralManager (unless it and its services have previously been discovered). To see if it's something wrong in my code, I downloaded Apple's sample code. Running the sample code on two iOS devices works as intended, however when copying the CBCentralManager code to an OS X app, it fails to find the iOS device.

I've uploaded an Xcode project for the OS X app, it's hosted on WikiUpload as that seems to be the least dodgy. There's also a copy on my hosting, if people prefer.

Here's the AppDelegate.m code in the OS X project also (the CoreBluetooth framework is linked in the project):

#import <CoreBluetooth/CoreBluetooth.h>

@interface AppDelegate () <CBCentralManagerDelegate, CBPeripheralDelegate>

@property (strong, nonatomic) CBCentralManager      *centralManager;
@property (strong, nonatomic) CBPeripheral          *discoveredPeripheral;
@property (strong, nonatomic) NSMutableData         *data;

@property (weak) IBOutlet NSWindow *window;
@end

@implementation AppDelegate

@synthesize centralManager = _centralManager, discoveredPeripheral = _discoveredPeripheral, data = _data;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    // Start up the CBCentralManager
    _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

    // And somewhere to store the incoming data
    _data = [[NSMutableData alloc] init];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}


#pragma mark - Central Methods



/** centralManagerDidUpdateState is a required protocol method.
 *  Usually, you'd check for other states to make sure the current device supports LE, is powered on, etc.
 *  In this instance, we're just using it to wait for CBCentralManagerStatePoweredOn, which indicates
 *  the Central is ready to be used.
 */
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    if (central.state != CBCentralManagerStatePoweredOn) {
        // In a real app, you'd deal with all the states correctly
        return;
    }

    // The state must be CBCentralManagerStatePoweredOn...

    // ... so start scanning
    [self scan];

}


/** Scan for peripherals - specifically for our service's 128bit CBUUID
 */
- (void)scan
{
    // This brings up nothing, unlike on iOS where it finds the device straight away

    [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]
                                                options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];

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

    NSLog(@"Scanning started");
}


/** This callback comes whenever a peripheral that is advertising the TRANSFER_SERVICE_UUID is discovered.
 *  We check the RSSI, to make sure it's close enough that we're interested in it, and if it is,
 *  we start the connection process
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{

    NSLog(@"Discovered %@ at %@", peripheral.name, RSSI);

    //Took out RSSI check

    if (self.discoveredPeripheral != peripheral) {

        // Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
        self.discoveredPeripheral = peripheral;

        // And connect
        NSLog(@"Connecting to peripheral %@", peripheral);



        [self.centralManager connectPeripheral:peripheral options:nil];
    }
}


/** If the connection fails for whatever reason, we need to deal with it.
 */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"Failed to connect to %@. (%@)", peripheral, [error localizedDescription]);
    [self cleanup];
}


/** We've connected to the peripheral, now we need to discover the services and characteristics to find the 'transfer' characteristic.
 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"Peripheral Connected");

    // Stop scanning
    [self.centralManager stopScan];
    NSLog(@"Scanning stopped");

    // Clear the data that we may already have
    [self.data setLength:0];

    // Make sure we get the discovery callbacks
    peripheral.delegate = self;

    // Search only for services that match our UUID
    [peripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]];
}


/** The Transfer Service was discovered
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error) {
        NSLog(@"Error discovering services: %@", [error localizedDescription]);
        [self cleanup];
        return;
    }

    // Discover the characteristic we want...

    // Loop through the newly filled peripheral.services array, just in case there's more than one.
    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]] forService:service];
    }
}


/** The Transfer characteristic was discovered.
 *  Once this has been found, we want to subscribe to it, which lets the peripheral know we want the data it contains
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    // Deal with errors (if any)
    if (error) {
        NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
        [self cleanup];
        return;
    }

    // Again, we loop through the array, just in case.
    for (CBCharacteristic *characteristic in service.characteristics) {

        // And check if it's the right one
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {

            // If it is, subscribe to it
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
    }

    // Once this is complete, we just need to wait for the data to come in.
}


/** This callback lets us know more data has arrived via notification on the characteristic
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error) {
        NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
        return;
    }

    NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];

    // Have we got everything we need?
    if ([stringFromData isEqualToString:@"EOM"]) {

        // We have, so show the data,
        //[self.textview setText:[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]];
        NSLog(@"Text: %@", [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]);
        // Cancel our subscription to the characteristic
        [peripheral setNotifyValue:NO forCharacteristic:characteristic];

        // and disconnect from the peripehral
        [self.centralManager cancelPeripheralConnection:peripheral];
    }

    // Otherwise, just add the data on to what we already have
    [self.data appendData:characteristic.value];

    // Log it
    NSLog(@"Received: %@", stringFromData);
}


/** The peripheral letting us know whether our subscribe/unsubscribe happened or not
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error) {
        NSLog(@"Error changing notification state: %@", error.localizedDescription);
    }

    // Exit if it's not the transfer characteristic
    if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
        return;
    }

    // Notification has started
    if (characteristic.isNotifying) {
        NSLog(@"Notification began on %@", characteristic);
    }

    // Notification has stopped
    else {
        // so disconnect from the peripheral
        NSLog(@"Notification stopped on %@.  Disconnecting", characteristic);
        [self.centralManager cancelPeripheralConnection:peripheral];
    }
}


/** Once the disconnection happens, we need to clean up our local copy of the peripheral
 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"Peripheral Disconnected");
    self.discoveredPeripheral = nil;

    // We're disconnected, so start scanning again
    [self scan];
}


/** Call this when things either go wrong, or you're done with the connection.
 *  This cancels any subscriptions if there are any, or straight disconnects if not.
 *  (didUpdateNotificationStateForCharacteristic will cancel the connection if a subscription is involved)
 */
- (void)cleanup
{
    // Don't do anything if we're not connected
    if (!self.discoveredPeripheral.isConnected) {
        return;
    }

    // See if we are subscribed to a characteristic on the peripheral
    if (self.discoveredPeripheral.services != nil) {
        for (CBService *service in self.discoveredPeripheral.services) {
            if (service.characteristics != nil) {
                for (CBCharacteristic *characteristic in service.characteristics) {
                    if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
                        if (characteristic.isNotifying) {
                            // It is notifying, so unsubscribe
                            [self.discoveredPeripheral setNotifyValue:NO forCharacteristic:characteristic];

                            // And we're done.
                            return;
                        }
                    }
                }
            }
        }
    }

    // If we've got this far, we're connected, but we're not subscribed, so we just disconnect
    [self.centralManager cancelPeripheralConnection:self.discoveredPeripheral];
}

And in AppDelegate.h there's the UUID definitions:

#ifndef LE_Transfer_TransferService_h
#define LE_Transfer_TransferService_h

#define TRANSFER_SERVICE_UUID           @"E20A39F4-73F5-4BC4-A12F-17D1AD07A961"
#define TRANSFER_CHARACTERISTIC_UUID    @"08590F7E-DB05-467E-8757-72F6FAEB13D4"

#endif

What's the problem here? According to the above linked question the service has to be part of the advertising packet, but as far as I can see that's exactly what the iOS peripheral is doing with

[self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];

解决方案

CoreBluetooth can be very frustrating. Here are a couple things to try:

#1: A peripheral that has been connected to stops advertising. If you make a successful connection to the peripheral, you'll need to restart advertising.

#2: iOS caches discovered status and provided services. There's no programmatic way to refresh / clear the cache. Try disabling BT on the iOS device and on the Mac and re-enabling it. Then attempt another connection.

#3: There's a problem with your UUIDs. Try scanning for peripherals with the UUID parameter set to nil. You should then discover all peripherals in range.

#4: The BT connection on a Mac can be finicky if the Wifi is on. Try disabling Wifi on your Mac and try again. I've found BTLE completely unusable with Wifi enabled so I've had to use ethernet when doing any BTLE dev on my MacBook.

这篇关于核心蓝牙在扫描特定CBUUID时找不到外围设备的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆