11

Why do I get CBCentralManagerStateUnknown on an iPad 2 when using this simple code?

- (BOOL)viewDidLoad {

    bluetoothManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

    if ([manager state] == CBCentralManagerStatePoweredOff) NSLog(@"CBCentralManagerStatePoweredOff");
    if ([manager state] == CBCentralManagerStatePoweredOn) NSLog(@"CBCentralManagerStatePoweredOn");
    if ([manager state] == CBCentralManagerStateResetting) NSLog(@"CBCentralManagerStateResetting");
    if ([manager state] == CBCentralManagerStateUnauthorized) NSLog(@"CBCentralManagerStateUnauthorized");
    if ([manager state] == CBCentralManagerStateUnknown) NSLog(@"CBCentralManagerStateUnknown");
    if ([manager state] == CBCentralManagerStateUnsupported) NSLog(@"CBCentralManagerStateUnsupported");

}

I cannot figure out what CBCentralManagerStateUnknown means. What do I do? The Apple docs just say:

State unknown, update imminent.

I get this response with a Bluetooth device connected, and also when Bluetooth is off. If I try to run something like [manager retrieveConnectedPeripherals], I also get this message in the console:

CoreBluetooth[WARNING] <CBConcreteCentralManager: ...> is not powered on
woz
  • 10,888
  • 3
  • 34
  • 64
  • 1
    It means the state is unknown. In this case, probably because the Bluetooth hardware hasn't booted up yet. What's the problem? – Jonathan Grynspan Sep 20 '12 at 18:23
  • I cannot get it to the state `CBCentralManagerStatePoweredOn` so that I can do anything. What do I have to do to get it to that state? – woz Sep 20 '12 at 18:28
  • I'm a little bit confused. You did mention that "I get this response with a Bluetooth device connected", but you also indicated that you can't do anything. How do you have the device connected in the first place then? – yuklai Oct 11 '12 at 15:36
  • 3
    For one thing, the iPad 2 lacks Bluetooth LE support, so Core Bluetooth will not work with it. That should return CBCentralManagerStateUnsupported, but maybe there's a slight bug in the way this is being checked. – Brad Larson Nov 05 '12 at 19:31
  • @Brad Larsen, I think you are mistaken, I have an iPad2 and it has BLE support. I have used it extensively for dev with a BLE 4.0 peripheral that I designed, it has been invaluable. More info: Its currently running iOS 7.1, model is FD328LL/A. Perhaps not all iPad2 models have BLE support? An easy check is to download the app LightBlue LE and run it, if your peripheral is configured to begin Advertising upon scan, then it will show up . – DasBoos Oct 13 '16 at 15:01
  • @DasBoos - You might be confusing the iPad 2 with the iPad Air 2. The iPad 2 only supported Bluetooth 2.1, not 4.0 / BTLE: https://support.apple.com/kb/sp622?locale=en_US . The iPad 2 I have as a test device lacks BTLE. – Brad Larson Oct 13 '16 at 15:07

6 Answers6

16

I know why delegate is never called. Because the object is deleted from memory. Just make a strong property

@property (strong, nonatomic) DiscoverBluetoothDevices *btDevices;

And in init

@implementation DiscoverBluetoothDevices
- (id) init
{
    self = [super init];
    if(self) {
        centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
        [centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @YES}];

    }
    return self;
}

And now delegate is called properly.

Bartosz Hernas
  • 1,130
  • 1
  • 9
  • 17
  • Try to change your code from [self.manager scanForPeripheralsWithServices:[NSArray arrayWithObject:[CBUUID UUIDWithString:@"180D"]]options:nil]; with NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerScanOptionAllowDuplicatesKey]; [self.manager scanForPeripheralsWithServices:nil options:dictionary]; – york May 29 '13 at 06:43
3

CBCentralManagerStateUnknown simply means iOS has started the BLE process, but has not completed initialization. Give it a moment, and the state will change.

In general, you will "give it a moment" by detecting a state chang in a CBCentralManagerDelegate delegate handler rather than looking at it right after the initialization call. You will implement

- (void) centralManagerDidUpdateState: (CBCentralManager *) central;

There are some good examples that show this, such as Apple's heart rate monitor.

Mike
  • 3,084
  • 1
  • 25
  • 44
  • 3
    I have a breakpoint in that function, but the function never gets called. – woz Sep 26 '12 at 15:24
  • I have the same problem centralManagerDidUpdateState never get called – Prince Kumar Sharma Jun 22 '13 at 10:25
  • 1
    Depending on what you're been doing up to this point, one thing you might try is restarting your iOS device. I've seen iOS get confused about BLE links in the past, generally when multiple apps are used to connect to a single device. Rebooting the device is often the only thing that seems to clear up the problem. – Mike Jun 23 '13 at 16:43
2

If a central's state goes to CBCentralManagerStateUnsupported (while Bluetooth Low Energy is supported by the device) it most likely means the app has done something bad with CoreBluetooth.

Check the iOS Bluetooth Diagnostic Logging logs.

For example, if you do this...

_cm1 = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{ CBCentralManagerOptionRestoreIdentifierKey: @"not_unique" }]; _cm2 = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{ CBCentralManagerOptionRestoreIdentifierKey: @"not_unique" }];

... the second central's state will go to CBCentralManagerStateUnsupported.

Martijn Thé
  • 4,674
  • 3
  • 29
  • 42
1

The actual answer (old question I know); start a scan for peripherals, this will start BT LE up and your delegates will get called back. My Delegates and state info did not change until I did this.

a. Setup your cbcentralmanager as below b. Have the -central* delegates in your code and in your .h file c. NSLog or have a label on screen update with new status. And... Success.

cManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

[cManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @YES}];
1

You need to both retain the CBCentralManager instance (put it in an ivar or private property) and wait for the state change delegate to be called. (The state is always "unknown" if you check it immediately after instantiating the manager. The real state will appear momentarily in the delegate method.)

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
0

In my case I did use AppDelegate as delegate for

CBCentralManagerDelegate

and indirectional for

 AVCaptureMetadataOutputObjectsDelegate.

in one time.

1) Take care about threads. Use

dispatch_get_main_queue() 

or

[NSThread mainThread]

for work with BLE.

2) Take care about using this 2 delegates on 1 object. Because hardware is NOT thead and context save

WINSergey
  • 1,977
  • 27
  • 39