How to get list of available Bluetooth devices? - ios

I'm currently creating an iPhone app (Xcode 4.3.1, IOS 5) that could use Bluetooth devices! Main goal of this application is indoor navigation (GPS inside buildings isn't really accurate).
The only solution I see here (to keep my app on AppStore) is to try scan for available bluetooth devices!
I tried to use CoreBluetooth framework, but I don't get list of available devices!
Maybe I don't use these functions correctly
#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface AboutBhyperView : UIViewController <CBPeripheralDelegate, CBCentralManagerDelegate>
{
CBCentralManager *mgr;
}
#property (readwrite, nonatomic) CBCentralManager *mgr;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
mgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog([NSString stringWithFormat:#"%#",[advertisementData description]]);
}
-(void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals{
NSLog(#"This is it!");
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
NSString *messtoshow;
switch (central.state) {
case CBCentralManagerStateUnknown:
{
messtoshow=[NSString stringWithFormat:#"State unknown, update imminent."];
break;
}
case CBCentralManagerStateResetting:
{
messtoshow=[NSString stringWithFormat:#"The connection with the system service was momentarily lost, update imminent."];
break;
}
case CBCentralManagerStateUnsupported:
{
messtoshow=[NSString stringWithFormat:#"The platform doesn't support Bluetooth Low Energy"];
break;
}
case CBCentralManagerStateUnauthorized:
{
messtoshow=[NSString stringWithFormat:#"The app is not authorized to use Bluetooth Low Energy"];
break;
}
case CBCentralManagerStatePoweredOff:
{
messtoshow=[NSString stringWithFormat:#"Bluetooth is currently powered off."];
break;
}
case CBCentralManagerStatePoweredOn:
{
messtoshow=[NSString stringWithFormat:#"Bluetooth is currently powered on and available to use."];
[mgr scanForPeripheralsWithServices:nil options:nil];
//[mgr retrieveConnectedPeripherals];
//--- it works, I Do get in this area!
break;
}
}
NSLog(messtoshow);
}
I'm not sure about this line, how to pass correct parameters?
[mgr scanForPeripheralsWithServices:nil options:nil];
I took a look into apple reference .. and I still don't understand what's that CBUUID ???
Every device has some bluetooth id? where can I find it?
- (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs options:(NSDictionary *)options;
Parameters
serviceUUIDs - An array of CBUUIDs the app is interested in.
options - A dictionary to customize the scan, see CBCentralManagerScanOptionAllowDuplicatesKey.
Is there any other way to use Bluetooth on IOS? I mean, older frameworks that don't use BLE 4.0!
any advice would be appreciated!
thanks!

This guide looks promising for Bluetooth 3.0. Remember that the CoreBluetooth framework is ONLY for Bluetooth Low Energy (4.0). At bluetooth.com's dev-pages you can see some examples of globally defined services, and as Guan Yang mentionen, you can see that the heart rate service is 0x180D. UUID`s of the unit is defined by the manufacturer.
Here's a code snippet to maybe help you along the way.
// Initialize a private variable with the heart rate service UUID
CBUUID *heartRate = [CBUUID UUIDWithString:#"180D"];
// Create a dictionary for passing down to the scan with service method
NSDictionary *scanOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
// Tell the central manager (cm) to scan for the heart rate service
[cm scanForPeripheralsWithServices:[NSArray arrayWithObject:heartRate] options:scanOptions]

in Bluetooth, there are "Services". A device publishes 1..N services, and each service has 1..M characteristics. Each service has an unique identifier, called UUID.
For example, a heart rate Bluetooth Sensor that offers the service "heart rate", publishes a service with UUID 0x180D.
(more services here)
So when you perform a search, you must provide a UUID as the criteria, telling which service to search.

You should take a look at one of the examples. Here’s the relevant line:
[manager scanForPeripheralsWithServices:[NSArray arrayWithObject:[CBUUID UUIDWithString:#"180D"]] options:nil];
(I know this is a Mac OS X example, but the iOS CoreBluetooth API is very similar.)
CBUUID identifies services you are interested in, not devices. Standard services have a 16-bit UUID, in this case 0x180d for the heart rate monitor or perhaps 0x180a for device information, while proprietary services have a 128-bit UUID (16 bytes).
Most devices implement the device information service, so if you are just looking for any device, you could try [CBUUID UUIDWithString:#"180A"].

Try giving this while you are scanning for devices
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[self.centralManager scanForPeripheralsWithServices:nil options:options];
you will be able to get the list of devices at didDiscoverPeripheral delegate method

Keep calm and use https://github.com/l0gg3r/LGBluetooth
All you need to do
[[LGCentralManager sharedInstance] scanForPeripheralsByInterval:4
completion:^(NSArray *peripherals) {
}];

Related

BLE background reconnect

I want to reconnect to BLE device after device is moved out/terminated by user or system/reboted in background mode.
I know that it's possible : - see this question with description
Question - How can i setup centralManager for automatically reconnect to peripheral in background mode if app was terminated? Can someone describe step-by-step how it can be done?
Few word about current implementation:
I create centralManager with options like:
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:#{
CBCentralManagerOptionRestoreIdentifierKey: #"myCentralManagerIdentifier",
CBCentralManagerRestoredStatePeripheralsKey : #YES,
CBCentralManagerRestoredStateScanServicesKey : #YES,
CBCentralManagerRestoredStateScanOptionsKey : #YES
}];
After that i start to scan for BLE device
[self.centralManager scanForPeripheralsWithServices:[self discoverableCharacteristics] options:nil];
in - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI i connect to peripheral:
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
[self.centralManager stopScan];
peripheral.delegate = self;
[self.centralManager connectPeripheral:peripheral options: #{
CBConnectPeripheralOptionNotifyOnNotificationKey : #YES
}];
After that i can discover services and characteristics - all looks like ok. When i discover characteristic and read/write data i cancelPeripheralConnection
in didDisconnect i reconnect to device
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
[central connectPeripheral:peripheral options:nil];
}
i also implement centralManager:willRestoreState: like:
NSArray *peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
for (CBPeripheral *peripheral in peripherals) {
[central connectPeripheral:peripheral options:nil];
peripheral.delegate = nil;
}
In plist. added required key App communicates using CoreBluetooth.
Currently if i connected to device and terminate it - it relaunch automatically and connect to device - all it's ok, but if it's terminated again - nothing is happening.
Also if i moved out from peripheral and that come back - nothing happened.
Update
regarding point 5 - my fall - should use this key with connectPeripheral
in WillRestoreState:
NSArray *peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
if (!peripherals.count) {
peripherals = [central retrievePeripheralsWithIdentifiers:[self discoverableCharacteristics]];
}
if (peripherals.count) {
for (CBPeripheral *peripheral in peripherals) {
[central connectPeripheral:peripheral options:#{
CBCentralManagerRestoredStatePeripheralsKey : #YES,
CBCentralManagerRestoredStateScanServicesKey : #YES,
CBCentralManagerRestoredStateScanOptionsKey : #YES
}];
}
} else {
[self startScanning];
}
Current result - app will relaunched if it not swiped out from tray. I use my mac as a peripheral, so some times when i not launch app that make role of peripheral central can connect to mac itself not to required service.
Another question - are it's good option to reconnect to peripheral while lost connection for keeping connection like:
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
[central connectPeripheral:peripheral options:#{
CBCentralManagerRestoredStatePeripheralsKey : #YES,
CBCentralManagerRestoredStateScanServicesKey : #YES,
CBCentralManagerRestoredStateScanOptionsKey : #YES
}];
}
Also try to change notify characteristic on peripheral and read it on device. If all done in foreground - all works perfect, but in case connection was done in background some times didUpdateValueForCharacteristic not called at all, but didUpdateNotificationStateForCharacteristic is called with no error - this mean (i think) that something was done wrong by my side. Maybe u can advice where problem can be
And one more question - is there is some restriction in writing data to characteristics? because in apple sample it's setuped to 20 bytes.
First off I wanna start by saying that I have been working with CoreBluetooth for about two years now and from what I have noticed CoreBluetooth State Preservation and Restoration does not work reliably at all. You can get it working sort of "ok", but you will never get it to reconnect reliably unless Apple fixes it some day.
Having said that, I want to note a few things on your setup.
1) In centralManager:willRestoreState: you can only retrieve peripherals that has done any communication while the app was terminated. This means that you should also implement centralManagerDidUpdateState: and if the state is CBCentralManagerStatePoweredOn then you can use the retrievePeripheralsWithIdentifiers: method to retrieve the other peripheral and reset their delegate as well. This of course means that you have to stor the peripheral identifiers somewhere in your app somewhere. Also remember to reset pending connections here as well.
2) You set the delegate to nil in centralManager:willRestoreState:! So even if it does connect then you will not know about it i:P
3) Your app will only get relaunched if the app was terminated by the system. It will not get relaunched if you manually swipe-kill it from the application list. Neither will it get relaunched if the device is rebooted, unfortunately.
4) The CBConnectPeripheralOptionNotifyOnConnectionKey is not necessary when using the bluetooth-central background mode and is just annoying for a user so I would not use it.
5) CBCentralManagerRestoredStatePeripheralsKey, CBCentralManagerRestoredStateScanServicesKey, CBCentralManagerRestoredStateScanOptionsKey are not valid initialisation options so I don’t get why you are using those..
5) If the bluetooth switches state while the app is terminated then all pending connections will be lost and you will not be relaunched to know about it. This on its own effectively means that State Restoration is rather useless.
Anyhow, I am sad to say it, but if you are developing an app that must rely on the peripheral being reconnected in the background then I wold not recommend doing it. You will be frustrated. I could probably write an essay about all the bugs in Core Bluetooth that Apple does not want to fix. Even more frightening is that you can pretty easily ruin the bluetooth connectivity globally on the device from one single app so that no app can use bluetooth until the device is rebooted. This is pretty bad since it goes against Apples own Sandboxing principle.
If you need any more help, just let me know!
/A

Failing To Discover Bluetooth LE Service Advertisement in iOS

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.

How to get UUID from LE Bluetooth chip?

I have a chip, working on LE Bluetooth and transmitting it's UUID.
I need to discover it from IOS app and get it's UUID.
I know how to establish a connection between two IOS devices, but not with another chips.
Thanks!
You should check out Apple's CoreBluetooth Temperature Example.
First you will use the CBCentralManager to find the available Bluetooth Peripherals with the UUID you are looking for. It's a lengthy process that requires delegates and I can't easily give you code snippets to do this. It will look something like this.
.h file will have these. Remember to add the CoreBluetooth Framework.
#import <CoreBluetooth/CoreBluetooth.h>
CBCentralManager * manager;
CBPeripheral * connected_peripheral;
(Change your UUID accordingly):
NSArray * services=[NSArray arrayWithObjects:
[CBUUID UUIDWithString:#"0bd51666-e7cb-469b-8e4d-2742f1ba77cc"],
nil
];
[manager scanForPeripheralsWithServices:services options: [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerScanOptionAllowDuplicatesKey]];
[manager connectPeripheral:peripheral options:nil];
From there you know you have the right peripheral, but you still need to select it and stop the CBManager from continuing to scan for new devices.
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
[manager stopScan];
NSArray *keys = [NSArray arrayWithObjects:
[CBUUID UUIDWithString:#"0bd51666-e7cb-469b-8e4d-2742f1ba77cc"],
nil];
NSArray *objects = [NSArray arrayWithObjects:
#"My UUID to find",
nil];
serviceNames = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[connected_peripheral setDelegate:self];
[connected_peripheral discoverServices:[serviceNames allKeys]];
}
Now that you have told your peripheral to advertise what services it has, you'll have a delegate for parsing those services.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
CBService *bluetoothService;
for (bluetoothService in connected_peripheral.services) {
if([bluetoothService.UUID isEqual:[CBUUID UUIDWithString:#"0bd51666-e7cb-469b-8e4d-2742f1ba77cc"]])
{
NSLog(#"This is my bluetooth Service to Connect to");
}
}
I wish this process was easier to explain. The best way to figure it out is to download Apple's Temperature Example and run it on your iPhone or iPad (it won't work in the simulator). Even though you probably aren't broadcasting temperature it will find your Bluetooth LE device and will parse through the services it is broadcasting. Placing breakpoints in the LeDiscovery.m file of that project should show you the steps needed to discover your Bluetooth LE chip from an iOS app.
Hope this helps!

Run iOS 6 device as a BLE peripheral

As we know, iOS 6 support running devices (iPhone 4s and above, and new iPad) as a BLE peripheral. There is a demo in WWDC 2012 Session 705 called "advanced core bluetooth". I asked for the source code from Apple. They sent me a modified version of source code (BTLE_Transfer_Draft). Then I:
Run the app in iPhone 5 (iOS 6) in "Peripheral Mode" and start "Advertising"
Run the app in new iPad (iOS 5.1.1) in "Central Mode"
The problem is that the peripheral is never been discovered at all. So I use other testing applications including some downloaded from App Store. All failed to discover peripherals. I think the problem should be in BTLE_Transfer_Draft. Because I'm not sure whether I'm allowed to present the whole source code. So I just show the "peripheral mode" part here:
- (void)viewDidLoad {
[super viewDidLoad];
// Start up the CBPeripheralManager
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
// Opt out from any other state
if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
return;
}
// We're in CBPeripheralManagerStatePoweredOn state...
NSLog(#"self.peripheralManager powered on.");
// ... so build our service.
// Start with the CBMutableCharacteristic
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]
properties:CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable];
// Then the service
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]
primary:YES];
// Add the characteristic to the service
transferService.characteristics = #[self.transferCharacteristic];
// And add it to the peripheral manager
[self.peripheralManager addService:transferService];
}
/** Start advertising
*/
- (IBAction)switchChanged:(id)sender
{
if (self.advertisingSwitch.on) {
// All we advertise is our service's UUID
[self.peripheralManager startAdvertising:#{ CBAdvertisementDataServiceUUIDsKey : #[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
}
else {
[self.peripheralManager stopAdvertising];
}
}
The BLE is in powered on status and the startAdvertising is called. But the BLE central can never discover it.
Post updated:
According to mttrb's suggestion I added "CBAdvertisementDataLocalNameKey" when I startAdvertising. But my service is still can't be discovered by most of the apps including some apps from app store. The only one app can discover my service is an app from app store called "BLE scanner".
My question is: does this mean my application is working as a peripheral? But why my own code can't discover the service? How am I supposed to debug it ?
My code in Central Mode is like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Start up the CBCentralManager
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
if (central.state != CBCentralManagerStatePoweredOn) {
return;
}
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
......
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (error) {
NSLog(#"Error discovering services: %#", [error localizedDescription]);
return;
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
// Deal with errors (if any)
if (error) {
NSLog(#"Error discovering characteristics: %#", [error localizedDescription]);
return;
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(#"Peripheral Disconnected");
self.discoveredPeripheral = nil;
}
The didDiscoverPeripheral and didDiscoverServices are never called. What could be wrong? Any idea? Thanks
There is also a high quality free app called LightBlue that you can use to test your code with. It should be able to pick up all devices advertising in peripheral mode and it can even turn itself into an advertising peripheral if you want to make sure your device is working properly.
I would try moving the startAdvertising: method call up and into the end of your peripheralManagerDidUpdateState: delegate method and see if that helps.
I would also add a CBAdvertisementDataLocalNameKey key-value pair to your startAdvertising: method call. I found things were unreliable when the advertisement didn't have a name.
Finally, I would invest in the BLExplr app available in the App Store to help with scanning for your peripheral. It removes the assumption that your central is working correctly.
This Git hub project also throws some light on the CBPeripheralManager API. Called PeripheralModeTest.
This line is particularly useful for setting the advertising data
NSDictionary *advertisingData = #{CBAdvertisementDataLocalNameKey : #"Device Name", CBAdvertisementDataServiceUUIDsKey : #[[CBUUID UUIDWithString:CBUUIDGenericAccessProfileString]]};
Though I can't see any official documentation in the Apple iOS Developer Library yet. More specifically anything about setting the repeat period for the advertising.
The BTLE Transfer example has this piece of (odd) code, which may cause some troubles:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
// Reject any where the value is above reasonable range
if (RSSI.integerValue > -15) {
return;
}
// Reject if the signal strength is too low to be close enough (Close is around -22dB)
if (RSSI.integerValue < -35) {
return;
}
Just remove those two if-statements as it makes no sense!
I have made a simplified version available here that can be used to test high volumes of messages being sent from peripheral to central.
Please note that the CBPeripheralManager class was first introduced in iOS 6.0.
I do not know witch version of BTLE Central Peripheral Transfer you did actually test but current version has iOS 6 as requirement.
So I would suggest to test linkage against iOS 5.1 to see what compatibility issues it shows.

Cannot find peripheral when scanning for specific service CBUUID

When I use:
CBUUID * uuid = [CBUUID UUIDWithString:#"1800"]; // GAP
DEBUG_LOG(#"CBUUID: %#",uuid); // CBUUID: Generic Access Profile
_centralMan = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
[_centralMan scanForPeripheralsWithServices:[NSArray arrayWithObject:uuid]
options:nil];
I cannot find my peripheral, but when I use:
_centralMan = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
[_centralMan scanForPeripheralsWithServices:nil
options:nil];
…it shows up immediately.
When I connect, I am able to discover/read/write to all the services I am expecting. My understanding is that GAP makes this possible. I have also tried CBUUID's for other services that I know are running (specifically "1804", TX power service), to no avail; the scan never discovers anything.
Even though the services are running (I can communicate w/ them upon connecting), when I scan passing the service UUID array as nil and this delegate method is called...
-(void) centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
DEBUG_LOG(#"Found peripheral w/ UUID: %# RSSI: %# \n AdvData: %#",peripheral.UUID,RSSI,advertisementData);
DEBUG_LOG(#"Services found: %d",peripheral.services.count);
for (CBService * service in peripheral.services) {
DEBUG_LOG(#"Found service: %# w/ UUID %#",service, service.UUID);
}
}
…it always reports "Services found: 0". Should I expect the services array to be populated w/ services found during the scan? (I assume this is at least partially possible, based on scanForPeripheralsWithServices:[NSArray arrayWithObject:uuid]
options:nil.)
Could this be a problem w/ my peripheral's service advertisements? Am I using the API incorrectly? Any guidance is much appreciated! :-)
PS: I'm pretty green w/ Bluetooth.
PPS: I control the source to the peripheral (it's a CC2540DK). So I'd be as likely to believe the problem is there as it is here.
Turns out our peripheral code was not advertising the UUIDs a la this Bluetooth spec.
As soon as we dropped them into the advertising packet, iOS picked them up no sweat.

Resources