iOS, IoT Peripherals, and how to maintain a permanent connection

TL;DR: source code here

They’re out there. An army of refridgerators, smart cups, watches, AC controllers, pace makers, and more. They each make up the Internet of Things (IoT).

It’s common for IoT devices to use Bluetooth Low Energy (BLE). This tutorial was built against a BLE Nano v2. It was quick and easy to setup a basic peripheral using Arduino Studio.

Setup and Restore state

First, create a CBCentralManager. Its best to keep this in a Singleton class somewhere.

1
2
let options = [CBCentralManagerOptionRestoreIdentifierKey: "com.app.ble-central"]
self.centralManager = CBCentralManager(delegate: self, queue: nil, options: options)

Notice the restore Identifier option. This allows the central manager to restore state after the application closes. To respond to state restoration, the CBCentralManagerDelegate will need to listen for centralManager(\ central: CBCentralManager, willRestoreState dict:)_

1
2
3
4
5
6
internal func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {

// Contains an array of peripherals that were connected
// or in a pending connection state at the time the app
// was terminated by the system.
let restoredPeripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral]

Delegate methods, such as peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) for events missed during the down time are called by the system immediately after willRestoreState returns.

Scanning & Connecting with Peripherals

Using the same CBCentralManager it’s possible to call on the system to scan for and connect with peripherals, but first wait for the CBCentralManagerDelegate to report the state has changed to

1
2
3
4
5
6
extension BLECentral: CBCentralManagerDelegate {

internal func centralManagerDidUpdateState(_ central: CBCentralManager) {

if self.centralManager.state == .poweredOn {
// make stuff happen

It’s now possible to begin scanning. By calling centralManager.scanForPeripherals(withServices: [serviceUUID], options: nil) provide the service ID hard coded into your BLE peripheral. This will filter out everything that’s not your peripheral. Pass nil to see all the Bluetooth hardware near you.

When a matching peripheral is found, the system calls the Central Manager Delegate

1
internal func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {

At this point, The CBPeripheral can be passed back to the UI so the user can make a selection.

To pair, simply call centralManager.connect(peripheral, options: nil). Finally, assign your peripheral’s delegate property to discover services and characteristics.

BLE Characteristic Changes

last update time 2018-09-18