Site icon Storm ID Blog

Bluetooth Low Energy, and its application in iOS

What is it?

Bluetooth Low Energy (BLE) is a technology for allowing devices to communicate in a way that is highly optimized for conserving battery power.

BLE devices include things like FitBit trackers, heart-rate monitors, set-top-boxes, and fancy thermostats. The Internet of things. Very exciting possibilities!

BLE defined in the Bluetooth 4.0 specification, and is not compatible with older Bluetooth architectures.

BLE has far lower bit-rates than older technologies, and different radio setup. This means much better battery life for devices.

The central and the peripheral

When a BLE is connected to the other, one is termed the peripheral, the other is termed the central. The central is the consumer of data from the peripheral. For example, when you connect your iPhone to your FitBit, the iPhone is the central and the FitBit is the peripheral.

A central may have many peripherals, but a peripheral may (normally) only have one central.

The BLE device (e.g. FitBit) continually broadcasts advertisement packets. When another device (e.g. iPhone) wants to make a connection, it scans for these advertisement packets. Once an advertisement packet is discovered, a connection request is sent to the broadcaster. The broadcaster accepts the connection request, and the pair are peripheral and central.

Services and characteristics

Communication between connected central and peripheral takes place by reading and writing of characteristic values exposed by the peripheral. Characteristics are grouped into services. Services and characteristics are identified by unique identifiers. Characteristic values are 16 bytes long.

A central can directly read the value of a characteristic, or it can register with the peripheral to receive updates when the value changes.

iOS and CoreBluetooth

The iOS SDK abstracts the complexities of BLE workings with the CoreBluetooth framework. The main players:

Discovery of peripherals, services, and characteristics all happen through asynchronous operations. Completions are signaled via callbacks on delegate implementations.

An basic example

In this example we will discover a fictional thermostat, and through a service:

  1. set the threshold temperature
  2. ask to be notified of changes in the ambient temperature

Discovery of peripherals is done by implementing CBCentralManagerDelegate, then scanning with a CBCentralManager. Before attempting to connect to a device, work out if you should, either by UUID, name, or user selection.

- (void)discoverPerhiperals
{
 CBCentralManager *central = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
 [self scan:central];

 return YES;
}

#pragma mark - CBCentralManagerDelegate

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI 
{
 BOOL shouldConnect = /* your code to determine if you should try to connect to this device */

 if (shouldConnect) {
  [central stopScan];
  [central connectPeripheral:peripheral options:nil];
 }
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral 
{
 [self discoverServicesForPeripheral:peripheral];
}

Once you are connected to a perhiperal, you can discover its services. You can discover services by UUID, or discover all the services a perhiperal hosts. Typically you will want to discover one or two well known services by id. – this is faster than discovering all services.

BLE UUIDs are 128 bit and have the form 0000nnnn-0000-1000-8000-00805f9b34fb, often you will see them abbreviated to 0xnnnn. (See page 216 of the Bluetooth Specification V4.0 [Vol3])

{
 _thermostatServiceUuid = [CBUUID UUIDWithString:@"0000nnnn-0000-1000-8000-00805f9b34fb"];
 _heatingTurnsOnTemperatureCharacteristicUuid = CBUUID UUIDWithString:@"0000nnnn-0000-1000-8000-00805f9b34fb"];
 _currentAmbientTemperatureCharacteristicUuid = CBUUID UUIDWithString:@"0000nnnn-0000-1000-8000-00805f9b34fb"];
}

- (void)discoverServicesForPerhiperal:(CBPeripheral*)peripheral
{
 peripheral.delegate = self;
 [peripheral discoverServices:@[_thermostatServiceUuid]];
}


#pragma mark - CBPeripheralDelegate

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error 
{
 CBService *service = peripheral.services[0];
 [peripheral discoverCharacteristics:@[_heatingTurnsOnTemperatureCharacteristicUuid, _currentAmbientTemperatureCharacteristicUuid] forService:service];
}


- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {

 for (CBCharacteristic *characteristic in service.characteristics) {

  if ([characteristic.UUID isEqual:_heatingTurnsOnTemperatureCharacteristicUuid]) {
   // Change the temperature at which the heating turns on
   NSData *data = /* encode 16 bytes of data */
   [_peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
  }

  if ([characteristic.UUID isEqual:_currentAmbientTemperatureCharacteristicUuid]) {
   // Register to receive notifications of changes to ambient temperature
   [peripheral setNotifyValue:YES forCharacteristic:characteristic];
  }
 }
}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
 NSLog(@"didUpdateValueForCharacteristic");
 if ([characteristic.UUID isEqual:_currentAmbientTemperatureCharacteristicUuid]) {

  NSData *data = characteristic.value;
  NSLog(@"The current temperature is @%", /* decode 16 bytes of data */)
 }
}

Exit mobile version