Create BLE Central - iOS

Hello everyone , In my last blog Create BLE Peripheral - iOS We saw how to create BLE Peripheral to advertise data. Today we will see how to create a BLE Central to consume that advertisement data. Create a new Xcode project using the Single-View Application template. Name it BleCentral . Once the project is created, add the CoreBluetooth.framework to it. Then, open the ViewController.h file and add the following line:

#import <CoreBluetooth/CoreBluetooth.h>
As we are creating a BLE Central, you must implement protocols namely CBCentralManagerDelegate and CBPeripheralDelegate in your class.
@interface ViewController : UIViewController <CBCentralManagerDelegate, CBPeripheralDelegate>
Now add below properties
@property (strong, nonatomic) CBCentralManager *bleManager;
@property (strong, nonatomic) NSMutableData *data;
@property(nonatomic, strong) CBPeripheral *connectedPeripheral;
Initialize bleManager object in your ViewDidLoad
self.bleManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
Passing 'nil' to parameter queue specifies CBPeripheralManager will run on main thread. If you want it to run on other thread , you can create and pass it to the parameter. Once we initialize central manager , we need to check its state. This will allow to verify that the device your application is running on is Bluetooth LE compliant. You do this implementing the following delegate method:
 - (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    switch (central.state) {
        case CBCentralManagerStatePoweredOn:
            // Scans for any peripheral
            [self.bleManager scanForPeripheralsWithServices:
@[[CBUUID UUIDWithString:ADVERTISING_SERVICE_UUID_16]] 
options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
            break;
        default:
            NSLog(@"Central Manager did change state");
            break;
    }
}
The -scanForPeripheralsWithServices:options: method scans for peripherals that are advertising services. If you pass nil as first argument, the central manager starts to look for any service. The ADVERTISING_SERVICE_UUID_16 is the same UUID string I used for the Peripheral . Add below lines to your code before implementation
#define ADVERTISING_SERVICE_UUID_16 @"FFC0"
#define ADVERTISING_CHAR_UUID_16 @"b71e"
Here, ADVERTISING_SERVICE_UUID_16 is service UUID and ADVERTISING_CHAR_UUID_16 is a chracteristics UUID. Same like we defined in peripheral As soon as a Peripheral is discovered during the scanning, below method of the BLE Central delegate receives callback
 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral
 advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
This call notifies the BLE Central Manager delegate (the view controller) that a peripheral with advertisement data and RSSI was discovered. RSSI stands for Received Signal Strength Indicator. It is used for finding proximity of the peripheral. One can use this for adding filter for any peripheral and service. Advertisement Data is received in the form of NSDictionary. Write below code for this method
- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI {
    
  NSLog(@"peripheralUUID : %@",[advertisementData objectForKey:CBAdvertisementDataServiceUUIDsKey]);
  NSLog(@"local name : %@",[advertisementData objectForKey:CBAdvertisementDataLocalNameKey]);
  NSLog(@"Manufacturer : %@",[advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey]);
  NSLog(@"RSSI: %@", RSSI);
  [self.bleManager stopScan];
  [self.bleManager connectPeripheral:peripheral options:nil];

}
The options parameter is an optional NSDictionary and can use the following keys if needed For more information you can refer this link. Depending on the result of the connection, the delegate can receive either centralManager:didFailToConnectPeripheral:error: or centralManager:didConnectPeripheral:. In case of success, you can ask the Peripheral which services is advertising. So, in the didConnectPeripheral callback like following:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    // Clears the data that we may already have
    [self.data setLength:0];
    // Sets the peripheral delegate
    [self.connectedPeripheral setDelegate:self];
    // Asks the peripheral to discover the service
    [self.connectedPeripheral discoverServices:
@[[CBUUID UUIDWithString:ADVERTISING_SERVICE_UUID_16]]];
}

At this point, the Peripheral starts notifying its delegate.The Peripheral delegate receives -peripheral:didDiscoverServices:. If there is no error, the Peripheral can be asked to discover the characteristics for a given service. You can do it in this way:

 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    if (error) {
        NSLog(@"Error while discovering services: %@", error);
        return;
    }
 
    for (CBService *service in peripheral.services) {
        // Discovers the characteristics for a given service
        if ([service.UUID isEqual:[CBUUID UUIDWithString:ADVERTISING_SERVICE_UUID_16]]) {
            [self.peripheral discoverCharacteristics:
@[[CBUUID UUIDWithString:ADVERTISING_CHAR_UUID_16]] forService:service];
        }
    }
}
Now, if a characteristic is discovered, the Peripheral delegate receives -peripheral:didDiscoverCharacteristicsForService:error:. At this point, the Peripheral can be asked to notify its delegate as soon as the characteristic value is updated using -setNotifyValue:forCharacteristic: Below is the code for same
 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:
(CBService *)service error:(NSError *)error {
    if (error) {
        NSLog(@"Error while discovering characteristic: %@", error);
        return;
    }
    if ([service.UUID isEqual:[CBUUID UUIDWithString:ADVERTISING_SERVICE_UUID_16]]) {
        for (CBCharacteristic *characteristic in service.characteristics) {
            if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:ADVERTISING_CHAR_UUID_16]]) {
                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            }
        }
    }
}
Here, if the value of a characteristic is updated, then the Peripheral delegate receives -peripheral:didUpdateNotificationStateForCharacteristic: error:. Here, you can read the new value using -readValueForCharacteristic:
 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:
(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"Error while updating notification state: %@", error);
    }
 
    // Exits if it\'s not the transfer characteristic
    if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:ADVERTISING_CHAR_UUID_16]]) {
        return;
    }
 
    // Notification has started
    if (characteristic.isNotifying) {
        NSLog(@"Notification began on %@", characteristic);
        [peripheral readValueForCharacteristic:characteristic];
    } else { // Notification has stopped
        // so disconnect from the peripheral
        NSLog(@"Notification stopped on %@.  Disconnecting", characteristic);
        [self.bleManager cancelPeripheralConnection:self.connectedPeripheral];
    }
}
When the BLE Peripheral sends the new value, the Peripheral delegate receives -peripheral:didUpdateValueForCharacteristic:error:. The second argument of this method contains the characteristic. You can then read its value using the -value property. This is a NSData containing the value of the characteristic. At this point, you can either disconnect or wait for other data.

Conclusion : In this blog I have described how to create a basic BLE Central Application consuming advertise using Core Bluetooth Framework. I hope this tutorial will help you for creating simple BLE Central and Peripheral. Happy Coding....

Create BLE Peripheral - iOS

Hello everyone , Lets create application for Bluetooth Low Energy Peripheral in objective C. This application will simply advertise data for BLE Central devices. Create a new Xcode project using the Single-View Application template. Name it BleServer . Once the project is created, add the CoreBluetooth.framework to it. Then, open the ViewController.h file and add the following line:

 #import <CoreBluetooth/CoreBluetooth.h> 

Add CBPeripheralManagerDelegate protocol to the ViewController.

Now create a property of peripheral manager

 @property(nonatomic, strong) CBPeripheralManager *peripheral;

Let us initialize this property in viewDidLoad.

self.peripheral = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

Passing 'nil' to parameter queue specifies CBPeripheralManager will run on main thread. If you want it to run on other thread , you can create and pass it to the parameter.

Once the Peripheral Manager is initialized, we need to check its state. this ensures that Bluetooth low energy is supported and available to use on the local peripheral device. For this we need to implement below required method of CBPeripheralManagerDelegate.

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    switch (peripheral.state) {
        case CBPeripheralManagerStatePoweredOn:
            [self setupBLEService];
            break;
        case CBManagerStatePoweredOff: {
            [self disableBLEService];
            break;
        }
        default:
            NSLog(@"Peripheral Manager did change state");
            break;
    }
}
Here, We check the state of the Peripheral Manager. If its state is CBPeripheralManagerStatePoweredOn (or CBManagerStatePoweredOn available on iOS 10.0 and newer), then the device is using Bluetooth LE and we are good to go. We will set up our services and characteristics here. If its state is CBPeripheralManagerStatePoweredOff (or CBManagerStatePoweredOff available on iOS 10.0 and newer), then device not using Bluetooth LE currently or its off. We will disable our services here.

Service and Characteristic : Bluetooth Services and Characteristics are identified using uuid's. You can learn more about BLE services from this link. UUID's are generally 16bit or 128 bit. The advertising packet data length is limited to 31 bytes by the spec. So we will be using 16 bit UUID here. To start with, let us define UUID's service and characteristics respectively. You can change it with the UUID's you wish.

#define ADVERTISING_SERVICE_UUID_16 @"FFC0"
#define ADVERTISING_CHAR_UUID_16 @"b71e"

Lets create properties for service and characteristic

@property(nonatomic, strong) CBMutableCharacteristic *characteristic;
@property(nonatomic, strong) CBMutableService *service;

Now lets write code to set up and disable BLE services ,

- (void)setupBLEService {
    // If the service is already registered, we need to re-register it again.
    if (self.service) {
        [self.peripheral removeService:self.service];
    }
    
    // Create a BLE Peripheral Service and set it to be the primary. If it
    // is not set to the primary, it will not be found when the app is in the
    // background.
    self.service = [[CBMutableService alloc]
                    initWithType:ADVERTISING_SERVICE_UUID_16 primary:YES];
    
    // Set up the characteristic in the service. This characteristic is only
    // readable through subscription (CBCharacteristicsPropertyNotify) and has
    // no default value set.
   
    self.characteristic =
    [[CBMutableCharacteristic alloc]
     initWithType:self.characteristicUUID
     properties:CBCharacteristicPropertyNotify | CBCharacteristicPropertyRead
     value:nil
     permissions:CBAttributePermissionsReadable];
    
    // Assign the characteristic.
    self.service.characteristics =
    [NSArray arrayWithObject:self.characteristic];
    
    // Add the service to the peripheral manager.
    [self.peripheral addService:self.service];
}

- (void)disableService {
    [self.peripheral removeService:self.service];
    self.service = nil;
    [self.peripheral stopAdvertising];
}

Once we add the service to the Peripheral Manager it notifies its delegate with the method -peripheralManager:didAddService:error:. Here, if there is no error, we can start advertising the service:

 - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service 
error:(NSError *)error {
    if (error == nil) {
        // stop current running advertisement
        if (self.peripheral.isAdvertising) {
            [self.peripheral stopAdvertising];
        }
       // create advertisement dictionary containing service UUID and  name for the advertisement
        NSDictionary *advertisement = @{ CBAdvertisementDataServiceUUIDsKey : 
@[[CBUUID UUIDWithString:ADVERTISING_SERVICE_UUID_16]], 
CBAdvertisementDataLocalNameKey: @"BLEServerApp" };

       // start advertisement
        [self.peripheral startAdvertising:advertisement];
    }
}

You can now run the application. it will work like a peripheral. Make sure you turn on device bluetooth.

When the Peripheral Manager starts advertising the service, its delegate receives the message -peripheralManagerDidStartAdvertising:error: and when the Central subscribes to the service, the delegate receives -peripheralManager:central:didSubscribeToCharacteristic:. This is the place where you can generate dynamic data for the Central.

To send data to the Central, you need to prepare some chunk of data and then send updateValue:forCharacteristic:onSubscribedCentrals: to the Peripheral.

Conclusion : In this blog I have described how to create a basic BLE Peripheral Application making advertise using Core Bluetooth Framework. Lets see how to create application for BLE Central to listen these advertise in my next blog.... Happy Coding....

GitHub – iOSKida

iOSKida