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.
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
.