Scanning RSSI of BLE peripherals in background - ios

UPDATE 15/6 - 1 - ALTERNATIVE METHOD
As per Paulw's suggestion, I changed the strategy and instead of discovering for peripherals, I connected with peripheral and it worked fine when either peripheral or central are in foreground. The problem occurs when:
1) I setup notifications on delegate calls.
2) Both peripheral and central are in background.
3) As I bring them closer, I see no delegate calls.
4) As soon as I bring either peripheral or central in foreground, I get didDiscover call and everything works fine even if I now put app in both phones in background.
In short, the central does not discover peripheral if both are in background state.
This is the code for both parts:
func startAsPeripheral() {
peripheralManager = CBPeripheralManager(delegate: self, queue: DispatchQueue.global(qos: .background))
}
func startAsCentral() {
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.global(qos: .background))
}
}
extension BluetoothManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
LogManager.shared.log(object: "Central state update")
LogManager.shared.log(object: central.state)
if central.state != .poweredOn {
LogManager.shared.log(object: "Central is not powered on")
} else {
LogManager.shared.log(object: "Central scanning for: " + self.serviceUUID.uuidString);
self.centralManager.scanForPeripherals(withServices: [self.serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey : false])
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.centralManager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
self.centralManager.connect(self.peripheral, options: nil)
let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String
LogManager.shared.log(object: "Peripheral (" + (name ?? "unknown") + "): " + RSSI.stringValue)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
if peripheral == self.peripheral {
LogManager.shared.log(object: "Connected.")
peripheral.discoverServices([serviceUUID])
}
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
if peripheral == self.peripheral {
LogManager.shared.log(object: "Disconnected")
self.peripheral = nil
LogManager.shared.log(object: "Central scanning for: " + self.serviceUUID.uuidString);
self.centralManager.scanForPeripherals(withServices: [self.serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
}
}
}
extension BluetoothManager: CBPeripheralDelegate {
func peripheral( _ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
LogManager.shared.log(object: "Services: \(peripheral.services!)")
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral( _ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
LogManager.shared.log(object: "Characteristics: \(service.characteristics!)")
}
}
extension BluetoothManager: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .unknown:
LogManager.shared.log(object: "Bluetooth Device is UNKNOWN")
case .unsupported:
LogManager.shared.log(object: "Bluetooth Device is UNSUPPORTED")
case .unauthorized:
LogManager.shared.log(object: "Bluetooth Device is UNAUTHORIZED")
case .resetting:
LogManager.shared.log(object: "Bluetooth Device is RESETTING")
case .poweredOff:
LogManager.shared.log(object: "Bluetooth Device is POWERED OFF")
case .poweredOn:
LogManager.shared.log(object: "Bluetooth Device is POWERED ON")
let service = serviceUUID
let myService = CBMutableService(type: service, primary: true)
let writeCharacteristics = CBMutableCharacteristic(type: characteristricUUID, properties: .write, value: nil, permissions: .writeable)
myService.characteristics = [writeCharacteristics]
peripheralManager.add(myService)
peripheralManager.startAdvertising([CBAdvertisementDataLocalNameKey : "Spacedemic", CBAdvertisementDataServiceUUIDsKey : [service]])
LogManager.shared.log(object: "Started Advertising")
default:
LogManager.shared.log(object: "Unknown State")
}
}
}
ORIGINAL QUESTION
I have an app used for contact tracing. I configure it to work as both central and peripheral, both should work in background AND foreground. This is for contact tracing, so users won't have the application in foreground state in most cases. Advertising works fine as what I have observed, both in background and foreground. The problem here is that when central is in background, it sometimes gets into didDiscover only one time and sometimes never. Thus if a peripheral is in range, I am unable to detect the RSSI from central or if the user moves away from the central. In contrast, when in foreground, I get frequent updates with RSSI in didDiscover method.
How can I solve this? Or is there a workaround to get the desired results?

Related

CC2650 Module cannot Connect via Bluetooth with iOS app

I try to develop an iOS app using Swift. This app will be controlled Texas Instrument's CC2650 module via Bluetooth. I'm using iOS Core Bluetooth library. I took Bluetooth connection codes from
https://github.com/hoiberg/HM10-BluetoothSerial-iOS
import UIKit
import CoreBluetooth
var serial: BluetoothSerial!
final class BluetoothSerial: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var delegate: BluetoothSerialDelegate!
var centralManager: CBCentralManager!
}
var writeType: CBCharacteristicWriteType = .withResponse
init(delegate: BluetoothSerialDelegate) {
super.init()
self.delegate = delegate
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func startScan() {
guard centralManager.state == .poweredOn else { return }
let uuid = CBUUID(string: "0x180A")
centralManager.scanForPeripherals(withServices: [uuid], options: nil)
let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [uuid])
for peripheral in peripherals {
let StrenghtOfRSSI = peripheral.readRSSI()
delegate.serialDidDiscoverPeripheral(peripheral, RSSI: nil)
}
}
func stopScan() {
centralManager.stopScan()
}
func connectToPeripheral(_ peripheral: CBPeripheral) {
pendingPeripheral = peripheral
centralManager.connect(peripheral, options: nil)
}
func sendMessageToDevice(_ message: String) {
guard isReady else { return }
if let data = message.data(using: String.Encoding.init(rawValue: UInt(message)!)) {
connectedPeripheral?.writeValue(data, for: writeCharacteristic!, type: .withResponse)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
delegate.serialDidDiscoverPeripheral(peripheral, RSSI: RSSI)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
pendingPeripheral = nil
connectedPeripheral = peripheral
delegate.serialDidConnect(peripheral)
peripheral.discoverServices([CBUUID(string: "F0001110-0451-4000-B000-000000000000")])
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
connectedPeripheral = nil
pendingPeripheral = nil
delegate.serialDidDisconnect(peripheral, error: error as NSError?)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
pendingPeripheral = nil
delegate.serialDidFailToConnect(peripheral, error: error as NSError?)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
connectedPeripheral = nil
pendingPeripheral = nil
delegate.serialDidChangeState()
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
if bluetoothCounter == false {
if characteristic.uuid == CBUUID(string: "F0001112-0451-4000-B000-000000000000") {
peripheral.setNotifyValue(true, for: characteristic)
writeCharacteristic = characteristic
delegate.serialIsReady(peripheral)
}
} else {
if characteristic.uuid == CBUUID(string: "F0001113-0451-4000-B000-000000000000") {
peripheral.setNotifyValue(true, for: characteristic
writeCharacteristic = characteristic
delegate.serialIsReady(peripheral)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
let data = characteristic.value
guard data != nil else { return }
delegate.serialDidReceiveData(data!)
if let str = String(data: data!, encoding: String.Encoding.utf8) {
delegate.serialDidReceiveString(str)
} else {
print("Received an invalid string!")
}
}
}
But I have a problem with it. When my app scan for the peripheral, it can not find CC2650 module directly. However, there is something interesting situation happening. When I open BLE Scanner app (https://itunes.apple.com/us/app/ble-scanner-4-0/id1221763603?mt=8) I can discover my CC2650 module and send messages. After that, I open my app I can also discover my CC2650 and can send messages. My app cannot find CC2650 module directly.
I cannot solve this problem. I try everything which I found about Bluetooth connection.
I need some help with it.
Thank you guys.
I would first check to see the status of the BLE adapter using something like:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var consoleMsg = ""
switch(central.state) {
case .poweredOff:
consoleMsg = "BLE is off"
case .poweredOn:
consoleMsg = "BLE is on"
self.centralManager.scanForPeripherals(withServices: nil, options: nil)
case .resetting:
consoleMsg = "BLE is resetting"
case .unauthorized:
consoleMsg = "BLE is UA"
case .unknown:
consoleMsg = "BLE status unknown"
case .unsupported:
consoleMsg = "BLE is unsupported"
}
self.delegate?.statusUpdated(statusText: consoleMsg)
print("\(consoleMsg) \n", terminator: "")
}
This code will replace your startScan method, if it prints "BLE is on" you are okay and it will start scanning for peripherals.
From your code it looks like you aren't actually connecting to a peripheral either.
For example, you call centralManager.connect(peripheral, options: nil) only within connectToPeripheral and I don't see a call to that from within the serialDidDiscoverPeripheral section.
You need to scan, then register for the didDiscoverPeripheral delegate method, check to see if that peripheral is the one you want (via a UUID or device name, you can get this from the BLE explorer app you are using), then connect to it, then scan for services then interact with them (read, write, notify etc).
You can add:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = peripheral.name {
print(peripheralName)
if (peripheralName == "Your peripheral name"){
self.peripheral = peripheral
self.peripheral.delegate = self // This is to subscribe to delegate methods from the peripheral
self.centralManager.stopScan() // Found your peripheral so stop scanning
self.centralManager.connect(self.peripheral, options: nil)
}
}
}
That will connect (and print out to the console) to a peripheral. Then you need to scan for services (you can start this within the didConnect delegate method:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
Then you appear to have the code for discovering services and characteristics so that should all work ok.
Side note:
You call
let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [uuid]) which will only retrieve already connected peripherals, so if you connect to it via another app (e.g. the BLE explorer) that is why it appears in your app.

swift: pair with bluetooth device with button click

Until now the iPhone has paired to a bluetooth device when the app is opened, but I want to pair with a button click instead. I tried to put all the scanning and connection functions in a IBAction, but nothing happens when I push the button. In the first function after the IBAction I have a print output to check if it even enters the function, but I don't get the print. I also tried to put a delay after the manager.scanForPeripherals but didn't work either. What am I missing here? anyone know?
Thanks for any replies! here is my code (without the delay):
var manager: CBCentralManager!
var device: CBPeripheral?
var characteristics: [CBCharacteristic]?
var serviceUUID = "1234"
var char1 = "FFE1"
let deviceName = "HMSoft"
var connected = CBPeripheralState.connected
var disconnected = CBPeripheralState.disconnected
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager = CBCentralManager(delegate: self, queue: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn: break
// manager.scanForPeripherals(withServices: nil, options: nil)
case .unknown: break
case .resetting: break
case .unsupported: break
case .unauthorized: break
case .poweredOff:
break
}
}
#IBAction func connectBluetooth(_ sender: Any) {
manager.scanForPeripherals(withServices: nil, options: nil)
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
if peripheralName == self.deviceName {
// save a reference to the sensor tag
self.device = peripheral
self.device!.delegate = self
// Request a connection to the peripheral
self.manager.connect(self.device!, options: nil)
print("Check")
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if error != nil {
return
}
if let services = peripheral.services {
for service in services {
if (service.uuid == CBUUID(string: serviceUUID)) {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
device = peripheral
characteristics = service.characteristics
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if error != nil {
return
}
}
}
When you initialise the CBCentralManager and set its delegate, the delegate will start receiving events.
So, if you only want the CBCentralManager to be active once the user has tapped a button, rather than as soon as the screen loads, the minimum you have to do is move the code that initialises the manager from viewDidLoad to the button action.
So:
Move manager = CBCentralManager(delegate: self, queue: nil) from viewDidLoad to #IBAction func connectBluetooth(_ sender: Any),
Remove the call to scanForPeripherals from the IBAction, and;
Uncomment // manager.scanForPeripherals(withServices: nil, options: nil) in centralManagerDidUpdateState.
Don't put the function func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) inside the button.
Try instead this:
#IBAction func connectBluetooth(_ sender: Any) {
manager = CBCentralManager(delegate: self, queue: nil)
manager.scanForPeripherals(withServices: nil, options: nil)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
if peripheralName == self.deviceName {
// save a reference to the sensor tag
self.device = peripheral
self.device!.delegate = self
// Request a connection to the peripheral
self.manager.connect(self.device!, options: nil)
print("Check")
}
}
}
It's quite important that the device know that BLE is on before you can scan.
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch (central.state) {
case .unsupported:
print("BLE is unsupported")
case .unauthorized:
print("BLE is unauthorized")
case .unknown:
print("BLE is unknown")
case .resetting:
print("BLE is reseting")
case .poweredOff:
print("BLE is powered off")
case .poweredOn:
print("BLE is powered on")
//must be called
}
}

How can we connect garmin devices to iOS Application using BLE

I am developing an iOS application for Heart rate monitoring, and want to connect a garmin device to my application using Bluetooth.
But I am not able to list down any Garmin device Using Core bluetooth in Swift using (180D Service UUID).
func startUpCentralManager() {
print("Initializing central manager")
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func discoverDevices() {
print("discovering devices")
let services = [CBUUID(string: HRM_HEART_RATE_SERVICE_UUID),CBUUID(string:HRM_DEVICE_INFO_SERVICE_UUID),CBUUID(string:HRM_DEVICE_BATTERY_LEVEL_SERVICE_UUID),CBUUID(string:CALORIE_SERVICE)]
centralManager.scanForPeripherals(withServices: services, options: nil)
}
//MARK: CBCentralManagerDelegate
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("checking state")
switch (central.state) {
case .poweredOff:
print("CoreBluetooth BLE hardware is powered off")
case .poweredOn:
print("CoreBluetooth BLE hardware is powered on and ready")
blueToothReady = true;
case .resetting:
print("CoreBluetooth BLE hardware is resetting")
case .unauthorized:
print("CoreBluetooth BLE state is unauthorized")
case .unknown:
print("CoreBluetooth BLE state is unknown");
case .unsupported:
print("CoreBluetooth BLE hardware is unsupported on this platform");
}
if blueToothReady {
discoverDevices()
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let nameOfDeviceFound = (advertisementData as NSDictionary).object(forKey: CBAdvertisementDataLocalNameKey) as? NSString
print("\(String(describing: nameOfDeviceFound))")
print("Discovered Peripheral name : ", peripheral.name ?? String())
if !Utilities.allPeripheralList.contains(peripheral)
{
Utilities.allPeripheralList.add(peripheral)
}
let deviceObj = DeviceModel()
deviceObj.deviceName = peripheral.name ?? String()
deviceObj.UDID = String(format: "%#",(peripheral.identifier).uuidString)
self.addingPeripheralToGlobalSearchArray(obj: deviceObj)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
self.connected = peripheral.state == CBPeripheralState.connected ? "YES" : "NO"
print("Connection Status : %#", self.connected)
if self.connected == "YES"{
Utilities.sharedInstance.goToDashboardTabBarScreen(viewController: self)
}
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// central.scanForPeripherals(withServices: nil, options: nil)
}
func addingPeripheralToGlobalSearchArray(obj:DeviceModel){
var isFound:Bool = false
for i in 0..<Utilities.allDeviceList.count{
let deviceObj = Utilities.allDeviceList[i] as! DeviceModel
if obj.UDID == deviceObj.UDID{
isFound = true
}
}
if !isFound{
Utilities.allDeviceList.add(obj)
}
}
Above is the code which I am using to list down BLE devices. I am able to find Mio-Fuse devices using same peace of code but not Garmin devices.
You need to use the Garmin Standard SDK. Contact Garmin and register as a developer. You cannot connect directly using BLE you have to connect via the Garmin SDK API.

Issue with CBPeripheral connecting

CBCentralManager gets stuck when connecting to a device.
After starting to scan, it finds the device and reaches the centralManager: didDiscover: method, then tries to connect to it, but the device stays in connecting status for a while and then disconnects afterwards.
It's easily fixed by restarting the Bluetooth on device, but this isn't a solution. Does anyone have an idea why this happens?
Code:
//MARK: CBCentralManagerDelegate methods
func centralManagerDidUpdateState(_ central: CBCentralManager) {
self.delegate?.centralServiceDidUpdateState(self)
guard let centralManager = self.centralManager else { return }
switch centralManager.state {
case .poweredOn: self.scan()
case .poweredOff: print("poweredOff")
case .resetting: print("resetting")
case .unauthorized: print("unauthorized")
case .unknown: print("unknown")
case .unsupported: print("unsupported")
}
}
func scan() {
self.centralManager?.scanForPeripherals(withServices: nil)
print("Scanning started")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.delegate?.centralService(self, discover: peripheral.identifier.uuidString)
let retrievedPeripherals = central.retrievePeripherals(withIdentifiers: [])
for retrievedPeripheral in retrievedPeripherals {
central.connect(retrievedPeripheral, options: nil)
}
if !retrievedPeripherals.contains(peripheral) {
// And connect
self.discoveredPeripheral = peripheral
self.discoveredPeripheral?.delegate = self
central.stopScan()
central.connect(peripheral, options: nil)
self.rssis[peripheral.identifier.uuidString] = "\(RSSI)"
print("Connecting to peripheral \(peripheral)")
}
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Failed to connect to \(peripheral). (\(error!.localizedDescription))")
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Peripheral Connected with identifier: \(peripheral.identifier.uuidString)")
self.discoveredPeripheral?.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("Discover services for peripheralIdentifier: \(peripheral.identifier.uuidString)")
if let error = error {
print("Error discovering services: \(error.localizedDescription)")
return
}
// Loop through the newly filled peripheral.services array, just in case there's more than one.
for service in peripheral.services ?? [] {
peripheral.discoverCharacteristics(nil, for: service)
}
}
Also I've that log from console:
2017-05-21 21:09:32.049137 MyApp[5871:2645747] [Fabric] Unable to locate application icon
2017-05-21 21:09:32.051153 MyApp[5871:2645747] [Crashlytics] Version 3.8.4 (121)
Scanning started
self.peripheralManager powered on.
Connecting to peripheral <CBPeripheral: 0x1700f9100, identifier = B5E6ACBF-307C-4C13-BB6A-09F4A92ABEC3, name = (null), state = connecting>
Problem is what not centralManager: didFailToConnect, not centralManager: didConnect is not called.
Maybe this issue somehow related to null name of peripheral?
UPDATE #1:
I've added
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10.0) {
print("Connecting to peripheral \(peripheral)")
}
And I get this in the console:
Connecting to peripheral <CBPeripheral: 0x1700f9100, identifier = B5E6ACBF-307C-4C13-BB6A-09F4A92ABEC3, name = (null), state = connecting>
So peripheral still exist after 10 seconds. So it's not a problem with deallocated and strong references.

Bluetooth doesn't discover devices

First off, I know this question has been asked a lot on SO. And I went through all of them trying to resolve it but to no avail.
I have a simple app that scans for Bluetooth enabled devices. Here's my code.
import CoreBluetooth
import UIKit
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager: CBCentralManager!
var peripheral: CBPeripheral!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
// MARK: - CBCentralManagerDelegate
func centralManagerDidUpdateState(central: CBCentralManager) {
print(#function)
switch central.state {
case .Unsupported:
print("Unsupported")
case .Unauthorized:
print("Unauthorized")
case .PoweredOn:
print("Powered On")
central.scanForPeripheralsWithServices(nil, options: nil)
case .Resetting:
print("Resetting")
case .PoweredOff:
print("Powered Off")
case .Unknown:
print("Unknown")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print(#function)
print("Discovered \(peripheral.name) at \(RSSI)")
if peripheral.name!.containsString(name) {
manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connectPeripheral(peripheral, options: nil)
}
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print(#function)
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
central.scanForPeripheralsWithServices(nil, options: nil)
}
// MARK: - CBPeripheralDelegate
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print(#function)
guard let services = peripheral.services else {
return
}
for service in services {
print(service.UUID)
if service.UUID == serviceUUID {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
print(#function)
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
print(characteristic.UUID)
if characteristic.UUID == scratchUUID {
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
}
}
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print(#function)
var count: UInt32 = 0
if characteristic.UUID == scratchUUID {
characteristic.value!.getBytes(&count, length: sizeof(UInt32))
print(String(format: "%llu", count))
}
}
}
I ran it in my iPhone 5 (iOS 9.3.4). I had a laptop running Windows, an Android phone, an iPad Air 2 and my Mac all Bluetooth turned on. But none of those devices were discovered. centralManagerDidUpdateState method gets called and scanning starts but that's it. didDiscoverPeripheral delegate method never gets called.
I repeated the process, this time ran it in the iPad Air 2 but same result.
If I go to the Bluetooth menu on the device I do see other devices being discovered.
In some SO answers, I saw that running it in the main queue would work. Like so, manager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue()). But that did not work for me either.
I'd really appreciate any help on this.
Your other devices are probably not advertising a BLE service (like Paulw11 mentioned in the comments). All of the devices you mentioned will not appear as discoverable unless they are in the Bluetooth Settings page.
There are apps for iOS, Android, and Windows that have them advertise over LE. The easiest to use is LightBlue for iOS. Run your app on the iPhone while the other iOS device is advertising and you should see the advertisements. It might be useful to get an LE advertisement scanner on Windows or Android to double check that LightBlue is advertising.
Note Windows does not yet support Peripheral Role for Bluetooth LE,
so even though you will be able to see the advertisement you will not
be able to connect to it.

Resources