Connecting to a Peripheral

Once the user taps a row in the found devices table view, the connectToDevice() function is called.

Download: file
func connectToDevice () {
   centralManager?.connect(blePeripheral!, options: nil)
}

The connectToDevice() function calls centralManager?.connect to establishes a local connection to the desired peripheral.

Once the connection is made, the central manager calls the centralManager(_:didConnect) delegate function to provide incoming information about the newly connected peripheral.

Download: file
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("*****************************")
        print("Connection complete")
        print("Peripheral info: \(blePeripheral)")
        
        //Stop Scan- We don't need to scan once we've connected to a peripheral. We got what we came for.
        centralManager?.stopScan()
        print("Scan Stopped")
        
        //Erase data that we might have
        data.length = 0
        
        //Discovery callback
        peripheral.delegate = self
        //Only look for services that matches transmit uuid
        peripheral.discoverServices([BLEService_UUID])
}

Within this function, we perform a couple of actions:

  • Display the selected peripheral’s info in the console

  • Stop scanning

  • Erase any data from any previous scans

  • Set the peripheral’s delegate 

  • Discover the peripheral’s services

Ok - Now, we'll need to handle the possible incoming services from the peripheral.

Discovering Services

Once the peripheral's services are successfully discovered, the central manager will call the didDiscoverServices() delegate function. didDiscoverService() handles and filters services, so that we can use whichever service we are interested in right away.

Download: file
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        print("*******************************************************")

        if ((error) != nil) {
            print("Error discovering services: \(error!.localizedDescription)")
            return
        }
        
        guard let services = peripheral.services else {
            return
        }
        //We need to discover the all characteristic
        for service in services {
            
            peripheral.discoverCharacteristics(nil, for: service)
        }
        print("Discovered Services: \(services)")
    }

First, we handle any possible errors returned by the central manager, then we request characteristics for each service returned by calling discoverCharacteristics(_:)

Discovering Characteristics

Now that we've called the discoverCharacteristics(_:) function, the central manager will call the didDiscoverCharacteristicsFor() delegate function and provide the discovered characteristics of the specified service. 

Download: file
      func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
       
        print("*******************************************************")
        
        if ((error) != nil) {
            print("Error discovering services: \(error!.localizedDescription)")
            return
        }
        
        guard let characteristics = service.characteristics else {
            return
        }
        
        print("Found \(characteristics.count) characteristics!")
    
        for characteristic in characteristics {
            //looks for the right characteristic
            
            if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Rx)  {
               rxCharacteristic = characteristic
                
                //Once found, subscribe to the this particular characteristic...
                peripheral.setNotifyValue(true, for: rxCharacteristic!)
                // We can return after calling CBPeripheral.setNotifyValue because CBPeripheralDelegate's
                // didUpdateNotificationStateForCharacteristic method will be called automatically
                peripheral.readValue(for: characteristic)
                print("Rx Characteristic: \(characteristic.uuid)")
            }
            if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Tx){
                txCharacteristic = characteristic
                print("Tx Characteristic: \(characteristic.uuid)")
            }
        peripheral.discoverDescriptors(for: characteristic)
    }
}
    

A couple of things are happening in this function: 

  1. Handle errors and print characteristic info to the debug console
  2. Look through the array of characteristics for a match to our desired UUIDs.
  3. Perform any necessary actions for the matching characteristics
  4. Discover descriptors for each characteristic

In this case, the specific UUIDs we're looking for are stored in the BLE_Characteristic_uuid_Rx and BLE_Characteristic_uuid_Tx variables.  

When we find the RX characteristic, we subscribe to updates to its value by calling setNotifyValue() - this is how we receive data from the peripheral. Additionally, we read the current value from the characteristic and print its info to the console.

When we find the TX characteristic, we save a reference to it so we can write values to it later - this is how we send data to the peripheral.

Disconnecting from Peripheral

When the user taps the Disconnect button, the disconnectFromDevice() function is called.

Download: file
func disconnectFromDevice () {
	if blePeripheral != nil {
	centralManager?.cancelPeripheralConnection(blePeripheral!)
    }
 }

The disconnectFromDevice() function first checks if there is a current peripheral set - if there is, it calls cancelPeripheralConnection() which cancels an active or pending local connection to the peripheral.

Now, we can proceed to reading and writing using CBCharacteristics.

This guide was first published on Jul 27, 2017. It was last updated on Jul 27, 2017.
This page (Connecting to a Peripheral) was last updated on Aug 10, 2020.