1

I am trying to disconnect from a BLE device (raspberry-pi3), the delegates for disconnection are being triggered and upon checking explicitly if the peripheral is disconnected it shows that the peripheral is indeed disconnected. But, when I see in the settings app, I still see the device is connected.

Following is what I am trying:

Setup CBCentralManager as I come to the page.

func setupBLE() {
    lblStatus.text = "Establising connection to the Scrubport"
    self.manager = CBCentralManager(delegate: self, queue: .main)
}

Handle any state change if any

func centralManagerDidUpdateState(_ central: CBCentralManager) {
    if central.state == .poweredOn {
        self.dismissAlert()
        self.showLoader()
        self.timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: false, block: { _ in
          // No device found. Stop scanning.
        })
        self.manager?.scanForPeripherals(withServices: [SERVICE_UUID])
        return
    }
        
    else if central.state == .unauthorized {
        // Permission handlers here:
        return
    }
        
    // Handle all other cases.
}

Upon find the device try connecting

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    self.peripheral = peripheral
    connect()
}
func connect() {
    guard let peripheral = peripheral else {
        // Making sure we got the peripheral.
        return
    }
    lblStatus.text = "Connecting..."
    self.manager?.connect(peripheral)
}

Upon connection discover the service

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    lblStatus.text = "Connected, preparing device for transfer..."
    self.timer.invalidate()
    central.stopScan()
    peripheral.delegate = self
    peripheral.discoverServices([SERVICE_UUID])
}

Find the characteristics next

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    if let services = peripheral.services {
        for service in services {
            if service.uuid == SERVICE_UUID {
              peripheral.discoverCharacteristics([CHARACTERISTICS_UUID], for: service)
            }
        }
    }
}

Request notification

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    if let characteristics = service.characteristics {
        for characteristic in characteristics {
            if characteristic.uuid == self.CHARACTERISTICS_UUID {
                self.characteristic = characteristic
                peripheral.setNotifyValue(true, for: characteristic)
            }
        }
    }
}

Upon getting an update for notification change I do the write and it is all successful.

func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
    if characteristic.isNotifying {
        self.stopLoader()
        lblStatus.text = "Notifying now.."
    } else {
        print("Un-notified")
        print("Trying disconnection")
        manager?.cancelPeripheralConnection(peripheral)
    }
}

At the end I want to disconnect, or handle if app is closed or pressed back mid way

Trying clean up using following code:

fileprivate func cleanup() {
    print("CLEANING")
    guard let manager = manager else {
        return
    }
  
    print("Check scan.")
    if manager.isScanning {
        print("Stopping scan.")
        manager.stopScan()
    }
  
    // Don't do anything if we're not connected
    // self.discoveredPeripheral.isConnected is deprecated
    print("Checking peripheral connection")
    guard peripheral?.state == .connected else {
        print("No peripheral connected.")
        return
    }
  
    // See if we are subscribed to a characteristic on the peripheral
    print("Checking services")
    guard let services = peripheral?.services else {
        print("No services connection found.")
        cancelPeripheralConnection()
        return
    }
  
    print("Looping services")
    for service in services {
        print("Checking characteristics")
        guard let characteristics = service.characteristics else {
            print("No characteristics")
            continue
        }
        print("Looping characteristics")
        for characteristic in characteristics {
            print("Comparing characteristics UUID is notifying")
            if characteristic.uuid.isEqual(CHARACTERISTICS_UUID) && characteristic.isNotifying {
                print("Un-notifying")
                peripheral?.setNotifyValue(false, for: characteristic)
            } else {
                print("Nope not the one.", characteristic.isNotifying)
            }
        }
    }
}
fileprivate func cancelPeripheralConnection() {
    print("Remove peripheral connection")
    guard let manager = manager, let peripheral = peripheral else {
        print("Manager or peripheral not found!")
        return
    }
    print("Cancelling peripheral connection.")
    // If we've got this far, we're connected, but we're not subscribed, so we just disconnect
    manager.cancelPeripheralConnection(peripheral)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
    print("SERVICE INFO: didDisconnectPeripheral", peripheral.name ?? "Unknown")
    print("Was there an error?", error ?? "No error")

    self.peripheral = nil

    let peripherals = central.retrieveConnectedPeripherals(withServices: [])
    let _peri = central.retrievePeripherals(withIdentifiers: [peripheral.identifier])

    _peri.forEach { per in
        print(per.state == .connected, "connected")
        print(per.state == .connecting, "connecting")
        print(per.state == .disconnected, "disconnected")
        print(per.state == .disconnecting, "disconnecting")
    }

    print(peripherals)
}

Following are the logs that get printed on cleanup():

CLEANING
Check scan.
Checking peripheral connection
Checking services
Looping services
Checking characteristics
Looping characteristics
Comparing characteristics UUID is notifying
Un-notifying
Un-notified
Trying disconnection
SERVICE INFO: didDisconnectPeripheral raspberrypi-cm3
Was there an error? No error
false connected
false connecting
true disconnected
false disconnecting
[]

But this what I see in the settings app after clean up.

  • The connection in settings is not your app. Core Bluetooth connections do not show in settings. – Paulw11 Jan 07 '23 at 22:38
  • But that's the issue, it is actually showing up. And if there is no disconnection initiated from the machine side I remain connect even after the `didDisconnectPeripheral` are called. – Mantiqh Technologies Jan 08 '23 at 09:07
  • To confirm whether it is your app or not, swipe up on your app. If the connection disappears, it was your app. Do you have more than one `CBCentralManager` instance in your app? – Paulw11 Jan 08 '23 at 09:08
  • Also, there is no need to cancel notifications. You can just tear down the connection. The peripheral should automatically clean up – Paulw11 Jan 08 '23 at 09:10
  • The connection is still maintained even if I kill the app. – Mantiqh Technologies Jan 08 '23 at 10:15
  • No just one `CBCentralManager`. – Mantiqh Technologies Jan 08 '23 at 10:15
  • Was trying that out as part of cleanup thinking explicit disconnections from the services and characteristic notification might help, but no success :( – Mantiqh Technologies Jan 08 '23 at 10:16
  • If the connection is maintained after you kill the app, then the connection is nothing to do with your app. iOS has made some other, probably legacy Bluetooth, connection to the device. There is nothing you can do about it in your code. – Paulw11 Jan 08 '23 at 10:55
  • Thanks @Paulw11, I'll try and understand the hardware part to check what's happening on timeout there. Appreciate your time. – Mantiqh Technologies Jan 09 '23 at 11:27

0 Answers0