Why unsupported central manager state ?

Beware of the caveats of project capabilities

Several days ago I received a Kenwood TH D74 portable transceiver. Although I have a Yaesu FT60 and some other smaller and unimportant transceivers, I don’t have any transceiver that is APRS capable. What’s more important, this transceiver is BT and USB capable, providing serial communication, which opens some interesting opportunities.

Today I opened XCode for the first time in two or even three years. Boy, so many changes. I dove into, decided to choose an approach based on Core Bluetooth framework. And Objective-C. Nonetheless, I should mention that all recent examples on the internet based on Swift. Seems this is the new shit @ Apple, but as I don’t have time to learn a new programming language, I decided is faster to remember the old one so I wrote some lines in Objective-C, started like this:

#import <Cocoa/Cocoa.h>
#import <CoreBluetooth/CoreBluetooth.h>

@interface AppDelegate : NSObject <NSApplicationDelegate, CBCentralManagerDelegate, CBPeripheralDelegate> {
    CBCentralManager *manager;
    CBPeripheral *statie;
    NSArray *thuuid;
}

@property (assign) IBOutlet NSTextField *textField;
@property (assign) IBOutlet NSButton *scanButton;

- (IBAction)btnStartScan:(id)sender;


@end

And continued with one of the delegate methods:

- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
    switch (central.state) {

The delegate method centralManagerDidUpdateState is invoked when the central manager’s state is updated and this is a required method. Documentation says:

You implement this required method to ensure that Bluetooth low energy is supported and available to use on the central device. You should issue commands to the central manager only when the state of the central manager is powered on, as indicated by the poweredOn constant. A state with a value lower than poweredOn implies that scanning has stopped and that any connected peripherals have been disconnected. If the state moves below poweredOff, all CBPeripheral objects obtained from this central manager become invalid and must be retrieved or discovered again. For a complete list and discussion of the possible values representing the state of the central manager, see the CBCentralManagerState enumeration in CBCentralManager.

So I overrode it:

- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
    switch (central.state) {
        case CBManagerStateUnknown:
            _textField.stringValue = @"Central state is unkown";
            break;
        case CBManagerStateResetting:
            _textField.stringValue = @"Central state is resetting";
            break;
        case CBManagerStateUnsupported :
            _textField.stringValue = @"Central state is unsupported";
            break;
        case CBManagerStateUnauthorized:
            _textField.stringValue = @"Central state is unauthorized";
            break;
        case CBManagerStatePoweredOff:
            _textField.stringValue = @"Central state is powered off";
            break;
        case CBManagerStatePoweredOn:
            _textField.stringValue = @"Central state is powered on";
            [self startScan];
            break;
    }
}

The _textField variable in the code is a reference to an IBOutlet that points to my control text field in the main window where messages are displayed. Build it, run it. To my surprise:

Whaat ?

I banged my head for several hours and found no solution. Then it struck me that it might have some connection with the IDE. I went to project capabilities in XCode and look what I found:

That little checkbox should be checked in order to allow enabling low energy BT. Build and run, all fine:

Wheepee ! Core Bluetooth framework is working. 🙂 I still have some issues understanding why is this happening, but I assume this is more iOS related than OS X. The documentation says:

When your iOS app is in the background or in a suspended state, its Bluetooth-related capabilities are affected. By default, your app is unable to perform Bluetooth low energy tasks while it is in the background or in a suspended state. That said, if your app needs to perform Bluetooth low energy tasks while in the background, you can declare it to support one or both of the Core Bluetooth background execution modes (there’s one for the central role, and one for the peripheral role). Even when you declare one or both of these background execution modes, certain Bluetooth tasks operate differently while your app is in the background. You want to take these differences into account when designing your app.

Even apps that support background processing may be terminated by the system at any time to free up memory for the current foreground app. As of iOS 7, Core Bluetooth supports saving state information for central and peripheral manager objects and restoring that state at app launch time. You can use this feature to support long-term actions involving Bluetooth devices.

For now all fine. My Objective-C is still not as bad as I thought and writing some lines of code automatically re-wires my brain to remember what I though was forgotten stuff. Nice, but I suspect clouds will pop at the horizon: I have no idea where to get my transceiver’s UUID from and this seems to be central for any CB-based application. We will see. Enough for today. Time for another beer.

Leave a Reply

Your email address will not be published. Required fields are marked *