0

I have created two iOS apps; one a Bluetooth LE peripheral that advertises a service, and one a Bluetooth LE central that scans for the advertised service. The peripheral is running on my iPhone5s, and the central is running on my iPad Mini. I initially set the central up to scan for the specific advertised service, but later changed it to listen to any service. In either case, the iPad Mini app acting as a central never detects any advertised service. I am uncertain whether its a problem with the way I setup the peripheral manager to advertise, or if its a problem with the way I setup the central manager to scan, or a device configuration problem. Please offer suggestions or tests I can perform to get this working.

The following is the relevant code for the iPhone5s app acting as a peripheral:

CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];

CBUUID *immediateAlertServiceUUID = [CBUUID UUIDWithString: IMMEDIATE_ALERT_SERVICE_UUID];
CBUUID *alertLevelCharacteristicUUID = [CBUUID UUIDWithString: ALERT_LEVEL_CHARACTERISTIC_UUID];
CBUUID *myCustomCharacteristicUUID = [CBUUID UUIDWithString: MY_CUSTOM_CHARACTERISTIC_UUID];

alertLevelCharacteristic =
[[CBMutableCharacteristic alloc] initWithType:alertLevelCharacteristicUUID
                                   properties:CBCharacteristicPropertyRead
                                        value: nil permissions:CBAttributePermissionsReadable];
myCustomCharacteristic =
[[CBMutableCharacteristic alloc] initWithType:myCustomCharacteristicUUID
                                   properties:CBCharacteristicPropertyRead
                                        value: nil permissions:CBAttributePermissionsReadable];

NSArray *myCharacteristics = @[alertLevelCharacteristic, myCustomCharacteristic];

// Now setup the service
myService = [[CBMutableService alloc] initWithType:immediateAlertServiceUUID primary:YES];

// Finally, associate the characteristic with the service. This is an array of characteristics
myService.characteristics = myCharacteristics;

[peripheralManager addService:myService];

... wait for user to push button to start advertising ...

// Start Advertising
[peripheralManager startAdvertising:@{ CBAdvertisementDataLocalNameKey : @"My Service",
                                       CBAdvertisementDataServiceUUIDsKey : @[myService.UUID] }];

And here are the necessary delegate methods. NOTE: delegate method peripheralManagerDidUpdateState fires and indicates that "CoreBluetooth BLE hardware is powered on and ready" (same is true on the central side). Delegate method peripheralManager:didAddService:error fires without error (see output below). And delegate method peripheralManagerDidStartAdvertising:error fires without an error). Here is the service info printed from didAddService:

<CBMutableService: 0x17008efb0 Primary = YES, UUID = 1802, Included Services = (null), Characteristics = (
"<CBMutableCharacteristic: 0x1702c1500 UUID = 2A06, Value = (null), Properties = 0x2, Permissions = 0x1, Descriptors = (null), SubscribedCentrals = (\n)>",
"<CBMutableCharacteristic: 0x1702c15e0 UUID = 66E613B5-7225-42C6-A9C2-11FADAE62899, Value = (null), Properties = 0x2, Permissions = 0x1, Descriptors = (null), SubscribedCentrals = (\n)>")>

CBPeripheralManager Delegate Methods (sorry for all the code, just trying to be complete.):

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {

// Determine the state of the peripheral
if ([peripheral state] == CBPeripheralManagerStatePoweredOff) {
    NSLog(@"CoreBluetooth BLE hardware is powered off");
}
else if ([peripheral state] == CBPeripheralManagerStatePoweredOn) {
    NSLog(@"CoreBluetooth BLE hardware is powered on and ready");
}
else if ([peripheral state] == CBPeripheralManagerStateUnauthorized) {
    NSLog(@"CoreBluetooth BLE state is unauthorized");
}
else if ([peripheral state] == CBPeripheralManagerStateUnknown) {
    NSLog(@"CoreBluetooth BLE state is unknown");
}
else if ([peripheral state] == CBPeripheralManagerStateUnsupported) {
    NSLog(@"CoreBluetooth BLE hardware is unsupported on this platform");
}
}

- (void)peripheralManager:(CBPeripheralManager *)peripheral
        didAddService:(CBService *)service
                error:(NSError *)error {

if (error) {
    NSLog(@"Error publishing service: %@", [error localizedDescription]);

    return;
}
else {
    NSLog(@"Hurray! Your Service has been successfully published as: %@", service);
}
}

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
                                   error:(NSError *)error {

if (error == nil) {
    NSLog(@"Your service is now advertising");
}
else {
    NSLog(@"In peripheralManagerDidStartAdvertising: Your service advertising failed with error: %@", error);
}

}

And here is the relevant central code that runs on the iPad Mini:

// Scan for all available CoreBluetooth LE devices
NSArray *services = @[[CBUUID UUIDWithString:IMMEDIATE_ALERT_SERVICE_UUID]];

CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

//[centralManager scanForPeripheralsWithServices:services options:nil];
[centralManager scanForPeripheralsWithServices:nil options:nil];
self.centralManager = centralManager;

And here is one of the Central delegate methods. Except for centralManagerDidUpdateState:, none of the delegate methods fire.

// CBPeripheralDelegate - Invoked when you discover the peripheral's available services.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    NSLog(@"Did Discover Services");
    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

// CBCentralManagerDelegate - This is called with the CBPeripheral class as its main input parameter. This contains most of the information there is to know about a BLE peripheral.
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(@"Did Discover Peripheral");
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
if (![localName isEqual:@"My Service"]) {
    // We found the Device
    [self.centralManager stopScan];
    self.myPeripheral = peripheral;
    peripheral.delegate = self;
    [self.centralManager connectPeripheral:peripheral options:nil];
}
}

As a final note, I question whether BLE even works on my devices. I loaded a couple different iBeacon apps on the iPhone and iPad Mini to see if I can get the two devices to recognize iBeacons (one transmits, one receives), but they did not discover iBeacons either. I also tried with two iPhones. I also turned Bluetooth off then on. I also tried powering the devices off/on. Both devices are running in the foreground. Still no luck. Please help.

JeffB6688
  • 3,782
  • 5
  • 38
  • 58
  • 1
    When do you scan? It's not clear if you wait for the [peripheralManager state] to be "ok". Also, does LightBlue see your devices? Does 2 apps with two Ligthblue devices, it works? – Larme Jan 22 '15 at 17:05
  • @Larme I have tried starting up the Central first and starting up the Peripheral first. Neither approach works. Question: once the scan is started, doesn't it continually scan (i.e. does it matter which starts up first)? I have not tried LightBlue. I will try that now. – JeffB6688 Jan 22 '15 at 17:13
  • @Larme Ok, I tried LightBlue. It does detect the service being advertised from my iPhone. So this would suggest that there is a problem with the Central scan. Do you see anything wrong with the code I have in the Central for scanning and detecting the service? – JeffB6688 Jan 22 '15 at 17:23
  • Where is the `centralManager:didDiscoverPeripheral:advertisementData:RSSI:` method? – Larme Jan 22 '15 at 17:25
  • @Larme I just added it to the Question above. I never get to that delegate method either – JeffB6688 Jan 22 '15 at 17:38
  • 1
    What's the state of CBCentralManager state when you start the scan? – Larme Jan 22 '15 at 17:50
  • @Larme Ah. Thats it. I was scanning immediately after initializing the Central Manager. When I moved the start of the scan after the centralManagerDidUpdateState indicates the BLE is powered on, it works. Thanks. – JeffB6688 Jan 22 '15 at 18:08
  • There should be a message in the console saying that. – Larme Jan 22 '15 at 18:09

1 Answers1

2

I'll concatenate all the comments here:

Using apps like LightBlue or BLE Utility can help you to find if your issue is on the peripheral side or central one, since you're developing both sides yourself.

Before looking for CBServices, you have to connect to a CBPeripheral. Method that you did show before hand, and seems that it wasn't obvious.

Also, before starting a scan with the CBCentralManager you have to check its state, and it has to be CBPeripheralManagerStatePoweredOn.

Larme
  • 24,190
  • 6
  • 51
  • 81