How to communicate with Bluetooth Low Energy devices on iOS

Posted on Posted in Xamarin.iOS

Overview

iOS provides a library called CoreBluetooth which allows to communicate with BLE devices. It hides many low level operations, therefore we don’t have to worry about protocol specification. However, we need to get familiar with some new terms related with BLE, so let’s start with basic definitions to understand how the communication works.

Bluetooth Low Energy (BLE, Bluetooth Smart)

Bluetooth Smart is a wireless technology designed for integration with devices related to healthcare, home automation, beacons, etc. Energy consumption has been reduced significantly, which is perfect for small bands and detectors. Some devices can function for 1 – 2 years using only a single small battery.

Central

Central is a device, which is able to detect and communicate with BLE devices called peripherals. It could be for example an iPhone.

Peripheral

Peripheral is a Bluetooth low energy device like activity tracker or blood pressure monitor.

Peripheral's data structure (source: developer.apple.com)
Peripheral’s data structure (source: developer.apple.com)
Service

Service contains a set of similar information and functionalities. Peripheral may expose multiple services like battery service, signal service, steps counter service etc.

Characteristic

Each service contains characteristics with a data value. For steps counter service it could be a number of steps and a bit flag to reset its value.

Broadcasting

Peripheral transmits an advertisement packet at regular intervals, this process is called broadcasting. Central device can detect peripherals by intercepting those packets. It’s similar to detecting Wi-Fi networks. Some peripherals stop broadcasting once they are connected.

Discovering

Discovery is a process in which central device intercepts packets and detects peripherals, services and characteristics.

Pairing

Pairing is a process in which the peripheral becomes bounded with the central. For example, an iPhone will connect to paired peripheral each time it’s in range.

Notifications

It’s possible to subscribe for changes in chosen characteristics. Peripheral will send us a notification once a value is updated.

Identifier (UUID)

Each device, service, and characteristic has a unique identifier, which can be used to find specific information. There are also some well-known identifiers for services like battery service (UUID: 0x180F) or device information service (UUID: 0x180A). You can find more identifiers here.

For some reason, property CBPeripheral.UUID throws an exception, use CBPeripheral.Identifier instead to get UUID of device.

Swift Implementation

You can find Swift implementation here:
Swift – Bluetooth Low Energy communication using Flow Controllers.

Implementation

CoreBluetooth framework provides several classes, which represent things defined above like CBCentralManager, CBPeripheral, CBService and CBCharacteristic. CBCentralManager is the main class to communicate with BLE devices. Default CoreBluetooth interface is based on events, I will transform it into more user-friendly asynchronous methods, which return Task.

Below I will provide code samples for each step of communication. Presented code is written in Xamarin.iOS technology.

An iOS app linked on or after iOS 10.0 must include in its Info.plist file the usage description keys for the types of data it needs to access or it will crash. To access Bluetooth peripheral data specifically, it must include NSBluetoothPeripheralUsageDescription.

1. Discovering devices

Scanning for devices uses a lot of energy, therefore it should be running only when necessary and for a short time period. CBCentralManager provides a method, which takes a service UUID in order to find desired device. It’s also possible to provide an empty array to look for all available BLE devices.

For some reason, discovering devices with specific service doesn’t work sometimes. Probably it depends on device specification.

Before invoking any operation we need to make sure that CBCentralManager is in a proper state:
CBCentralManagerState.PoweredOn.

Below there is a full code sample with implemented scanning. In further paragraphs, I will implement the next methods, which should be appended to this class.

2. Connecting to peripheral

To connect to discovered peripheral we can just call:

However, in real life it’s useful to have an asynchronous version with connection timeout:

3. Discovering services and characteristics

To discover a service we need to call DiscoverServices method:

We can do better and implement another asynchronous method:

4. Reading and writing characteristic value

CBCharacteristic has property Value which returns NSData with raw bytes. However, before accessing property value we need to call peripheral.ReadValue(characteristic) and wait for data in event handler.

Similary we can write value by calling:
peripheral.WriteValue(value, characteristic, CBCharacteristicWriteType.WithoutResponse).

Asynchronous versions of those methods could look like that:

5. Retrieving connected/paired devices

This method returns connected BLE devices with iPhone. It’s useful because some peripherals stop broadcasting once they are connected, so it wouldn’t be possible to discover them using the scan method. You can read more in Swift – Bluetooth Low Energy – how to get paired devices?

6. Sample usage

Complete BluetoothService

Background mode

Read my new post to find out how to handle background mode:
Bluetooth Low Energy – background mode on iOS

References

You can find more information here: