This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Getting an iOS central app to bond

I have an iOS app which is a central to a peripheral running on an nRF51822. A similar Android app works fine and is able to connect to the peripheral, discover services, bond explicitly, and read an encrypted characteristic.

On iOS, however, there is no explicit bonding in the CoreBluetooth API. I am working on the basis that if I want to bond, I should just read an encrypted characteristic in order to force bonding to occur.

When is the right time to do that? If I do it when discovering the characteristic I want to read, in this function...

func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!)

... I never get a callback to peripheral:didUpdateValueForCharacteristic. Reading an unencrypted characteristic at this point does work though, I believe. Is there any other way to force bonding to occur? Nowhere in the nRF Toolbox iOS app code, as an example, can I find an explicit bond going on.

Here's the iOS app code:

import CoreBluetooth
import CoreLocation

class BluetoothPeripheralController: NSObject, CBPeripheralDelegate {

    private var _peripheral: CBPeripheral!
    var peripheral: CBPeripheral! {
        get {
            return self._peripheral
        }
        set {
            if self._peripheral != nil {
                self._peripheral = nil
            }

            self._peripheral = newValue

            if self._peripheral != nil {
                self.peripheralDidChange(newValue)
            }
        }
    }

    private var notificationCenter: NSNotificationCenter = {
        return NSNotificationCenter.defaultCenter()
    }()

    private var testModeCharacteristic: CBCharacteristic?

    private var voltageCharacteristic: CBCharacteristic?

    private var firmwareLoggingCharacteristic: CBCharacteristic?

    static let voltageDidChangeNotification = "voltageDidChangeNotification"

    static let testModeDidChangeNotification = "testModeDidChangeNotification"

    static let bonded = "bonded"

    static let firmwareLoggingDidChangeNotification = "firmwareLoggingDidChangeNotification"

    func peripheralDidChange(peripheral: CBPeripheral) {
        peripheral.delegate = self
    }

    func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
        if error != nil {
            log.error("Error: \(error)")
            return
        }

        for service in peripheral.services {
            log.verbose("Discovering characteristics for service: \(BleService().lookup(service.UUID))")

            peripheral.discoverCharacteristics(nil, forService: service as! CBService)
        }
    }

    // Hold on to characteristic instances so we can read/write/notify on them later.
    func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
        if let chars = service.characteristics as? [CBCharacteristic] {
            log.verbose("* \(BleService().lookup(service.UUID))")

            for char in chars {
                log.verbose("** \(BleCharacteristic().lookup(char.UUID))")

                if char.UUID.isEqual(BleCharacteristic.voltageCharacteristicUUID) {
                    voltageCharacteristic = char
                }

                if char.UUID.isEqual(BleCharacteristic.testModeCharacteristicUUID) {
                    testModeCharacteristic = char

                    log.verbose("Reading value for test mode.")

                    // Reading any encrypted value here does NOT result in a callback to peripheral:didUpdateValueForCharacteristic.
                    peripheral.readValueForCharacteristic(char)
                }

                if char.UUID.isEqual(BleCharacteristic.nordicNrfUartRxUUID) {
                    firmwareLoggingCharacteristic = char
                }
            }
        }
    }

    func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
        if error != nil {
            log.verbose("Error on value: \(error)")
            return
        }

        let data: NSData = characteristic.value

        log.verbose("Got value \(data) for characterisitc \(BleCharacteristic().lookup(characteristic.UUID))")

        switch characteristic.UUID {
        case BleCharacteristic.voltageCharacteristicUUID:
            var voltage: Float = 0.0

            data.getBytes(&voltage, length: sizeof(Float))

            log.debug("Voltage: \(voltage)")

            // The map view is interested in this.
            self.notificationCenter.postNotificationName(BluetoothPeripheralController.voltageDidChangeNotification, object: self, userInfo: ["voltage": NSNumber(float: voltage)])

        case BleCharacteristic.testModeCharacteristicUUID:
            log.debug("Test mode: \(data). We are now bonded.")

            self.notificationCenter.postNotificationName(BluetoothPeripheralController.bonded, object: self, userInfo: nil)

        default:
            log.debug("Unknown characteristic UUID: \(characteristic.UUID)")
        }
    }

    func peripheral(peripheral: CBPeripheral!, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
        if error != nil {
            log.verbose("Error updating notification state: \(error)")
            return
        }

        switch characteristic.UUID {
        case BleCharacteristic.nordicNrfUartRxUUID:
            log.verbose("Notification state change on firmware logging. Now notifying: \(characteristic.isNotifying)")

        default:
            log.verbose("Unhandled characteristic: \(characteristic.description)")
        }
    }

    // Read/write
    func readTestMode() {
        if testModeCharacteristic == nil {
            log.warning("Trying to read test mode before we have a characterisitc instance.")

            return
        }

        // Calls back to peripheral:didUpdateValueForCharacteristic.
        peripheral.readValueForCharacteristic(testModeCharacteristic)
    }

}

Here are my security params for the device manager on the Nordic side. The device has no user input capabilities.

// Timeout for Pairing Request or Security Request (in seconds).
#define SEC_PARAM_TIMEOUT                 30

// Perform bonding.
#define SEC_PARAM_BOND                    1

// Man In The Middle protection NOT required.
#define SEC_PARAM_MITM                    0

// No I/O capabilities.
#define SEC_PARAM_IO_CAPABILITIES         BLE_GAP_IO_CAPS_NONE

// Out Of Band data not available.
#define SEC_PARAM_OOB                     0

// Minimum encryption key size.
#define SEC_PARAM_MIN_KEY_SIZE            7

// Maximum encryption key size.
#define SEC_PARAM_MAX_KEY_SIZE            16
Parents Reply Children
No Data
Related