Cyprian
Cyprian

Reputation: 9453

CBCentralManager scanForPeripheralsWithServices:nil is not returning any results

I am trying to display all available BLE beacons. I got some Estimote and Kontakt.io beacons with me and for some reason the below BLE scanning code does not find any of them.

I went through all the possible SO questions relating to BLE discovery and that codes is exactly as in other places.

App source code here

- (void)viewDidLoad {
    [super viewDidLoad];

     self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}


/*
 Request CBCentralManager to scan for all available services
 */
- (void) startScan
{
    NSLog(@"Start scanning");

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber  numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];

    [self.manager scanForPeripheralsWithServices:nil options:options];
}

This delegate method never gets called

/*
 Invoked when the central discovers bt peripheral while scanning.
 */
- (void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)aPeripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{

    NSLog(@"THIS NEVER GETS CALLED");

}

Upvotes: 1

Views: 7647

Answers (3)

Cyprian
Cyprian

Reputation: 9453

The problem was posed by the version of the OS I was testing it with. Let's just say this version that I was testing was 4+4 (as it is under the NDA)

Apparently that new version will abort scanning if you trigger

[self.manager scanForPeripheralsWithServices:nil options:options];

before

CBCentralManagerStatePoweredOn is ON

So simply starting scanning after that state made it work for me.

#pragma mark - CBCentralManager delegate methods
/*
 Invoked whenever the central manager's state is updated.
 */
- (void) centralManagerDidUpdateState:(CBCentralManager *)central
{
    [self isLECapableHardware]? NSLog(@"YES"):NSLog(@"NO");
}


- (BOOL) isLECapableHardware
{
    NSString * state = nil;

    switch ([self.manager state])
    {
        case CBCentralManagerStateUnsupported:
            state = @"The platform/hardware doesn't support Bluetooth Low Energy.";
            break;
        case CBCentralManagerStateUnauthorized:
            state = @"The app is not authorized to use Bluetooth Low Energy.";
            break;
        case CBCentralManagerStatePoweredOff:
            state = @"Bluetooth is currently powered off.";
            break;
        case CBCentralManagerStatePoweredOn:

//---> HERE

            [self startScan];


            return TRUE;
        case CBCentralManagerStateUnknown:
        default:
            return FALSE;

    }

    return FALSE;
}

Upvotes: 3

Paulw11
Paulw11

Reputation: 115041

iBeacon's aren't accessed as peripherals - they are beacons. They are handled through the Core Location framework, not the Core Bluetooth framework.

There may be vendor specific services advertised by the beacons that can be detected by Core Bluetooth.

Your code doesn't wait until the CBCentralManager is in the powered on state. I made the following changes and it worked on both iOS 7.1 and iOS8 -

- (void)viewDidLoad {
    [super viewDidLoad];

     self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

}

- (void) startScan
{
    NSLog(@"Start scanning");

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber  numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];

    [self.manager scanForPeripheralsWithServices:nil options:options];

}


- (BOOL) isLECapableHardware
{
    NSString * state = nil;

    switch ([self.manager state])
    {
        case CBCentralManagerStateUnsupported:
            state = @"The platform/hardware doesn't support Bluetooth Low Energy.";
            break;
        case CBCentralManagerStateUnauthorized:
            state = @"The app is not authorized to use Bluetooth Low Energy.";
            break;
        case CBCentralManagerStatePoweredOff:
            state = @"Bluetooth is currently powered off.";
            break;
        case CBCentralManagerStatePoweredOn:
            [self startScan];
            return TRUE;
        case CBCentralManagerStateUnknown:
        default:
            return FALSE;

    }

    NSLog(@"Central manager state: %@", state);

    UIAlertView *alert = [[UIAlertView alloc] init];
    [alert setMessage:state];
    [alert addButtonWithTitle:@"OK"];
    [alert show];

    return FALSE;
}

Upvotes: 7

Keith Coughtrey
Keith Coughtrey

Reputation: 1495

I downloaded your code and found there was no call to startScan. Once I added that it worked for me. I added some details to the logging like this:

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

    NSLog(@"Discovered %@ %@", aPeripheral, advertisementData);

}

and I get this output:

2014-09-08 15:17:11.827 BTTest[4266:60b] New state 5
2014-09-08 15:17:11.830 BTTest[4266:60b] YES
2014-09-08 15:17:11.831 BTTest[4266:60b] Start scanning
2014-09-08 15:17:11.842 BTTest[4266:60b] CoreBluetooth[WARNING] <CBCentralManager: 0x17d6e5d0> is disabling duplicate filtering, but is using the default queue (main thread) for delegate events
2014-09-08 15:17:12.438 BTTest[4266:60b] per <CBPeripheral: 0x17d91220 identifier = C6E33BA0-F6E7-5830-0643-A47855AD27B9, Name = "(null)", state = disconnected> {
    kCBAdvDataChannel = 37;
    kCBAdvDataIsConnectable = 1;
}
2014-09-08 15:17:12.595 BTTest[4266:60b] per <CBPeripheral: 0x17d89b60 identifier = 4FF1E398-E24C-8739-A19B-9DF8A2A5493B, Name = "Flex", state = disconnected> {
    kCBAdvDataChannel = 38;
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = Flex;
    kCBAdvDataServiceData =     {
        "Device Information" = <0704>;
    };
    kCBAdvDataServiceUUIDs =     (
        "ADABFB00-6E7D-4601-BDA2-BFFAA68956BA"
    );
    kCBAdvDataTxPowerLevel = "-6";
}

Upvotes: 1

Related Questions