I am using CBCentralManager to scan for nearby BLE devices. Below is sample code:-
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOff:
debugPrint("Central manager state is Powered Off")
case .poweredOn:
debugPrint("Central manager state is Powered On")
loggerUtils.log.debug("Central manager state is Powered On")
centralManager.scanForPeripherals(withServices: nil, options: nil)
case .resetting:
debugPrint("Central manager state is resetting")
case .unauthorized:
debugPrint("Central manager state is unauthorised")
case .unknown:
debugPrint("Central manager state is unknown")
case .unsupported:
debugPrint("Central manager state is unsupported")
default:
debugPrint("Central Manager state is unrecognised....enum raw value is \(central.state.rawValue)")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("\n****\nPeripheral is \(peripheral.name ?? "No Name")\n*****\nAdvertisement data is \(advertisementData)\n*****\nRSSI is \(RSSI) ")
}
It shows the nearby speakers and other BLE enabled devices. But it is not showing any Android Phone whose bluetooth is powered on from settings option. I can see the android device in device list inSettings->Bluetooth in same iPhone.
Related
I'm writing a very basic App that shall scan the environment for peripherals that provide a specific service and displays the device IDs and the RSSI in a table view. Nothing special.
Here is what I'm trying:
extension BLEScanner: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
debug?.debug("Powered on")
central.scanForPeripherals(withServices: [CBUUID(string: "FD6F")],
options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
])
default:
break
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let uuid = peripheral.identifier
debug?.debug("UUID: \(uuid) \(peripheral.name) \(RSSI)")
}
}
This code works as expected on devices running iOS12 and even on MacOSX, but when I run it on iOS13, no peripherals are being discovered.
I also tried other service UUIDs with the same result.
Isn't it possible to discover peripherals with specific services with iOS13 anymore?
Or could it be a device issue? I've installed iOS 13.5 on an iPad Pro (first generation), iOS13.6 on an iPhone 8 and iOS12.4 on an iPhone 6.
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?
Is there a way to discover all Bluetooth devices in an iOS app? I am using CoreBluetooth and I get all iDevices around plus one unknown device. However if I go to General -> Bluetooth -> Devices it lists a couple of devices that are not listed by the example app I am making. I need to find a none Apple device in the list and connect to it. Is this possible? And how come the two lists (the system one and the one in the app) are different?
In case it is important it needs to be used for ODB2 system later on, but for now just listing the none Apple device in the discovered devices will do.
Here is the code I am using to list the devices:
class HRMViewController: UIViewController {
var centralManager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
}
extension HRMViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
print("central.state is .unknown")
case .resetting:
print("central.state is .resetting")
case .unsupported:
print("central.state is .unsupported")
case .unauthorized:
print("central.state is .unauthorized")
case .poweredOff:
print("central.state is .poweredOff")
case .poweredOn:
print("central.state is .poweredOn")
centralManager.scanForPeripherals(withServices: nil)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(advertisementData)
print(peripheral)
}
}
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.
I'm trying to scan for a specific BLE Device on my app by specifying a CBUUID in the scanForPeripheralsWithServices(), but if I mention it scan is never processed.
Here is my CBCentralManagerDelegate code:
func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state) {
case CBCentralManagerState.PoweredOff:
print("BLE powered off")
self.clearDevices()
case CBCentralManagerState.Unauthorized:
// Indicate to user that the iOS device does not support BLE.
print("BLE not supported")
break
case CBCentralManagerState.Unknown:
// Wait for another event
print("BLE unknown event")
break
case CBCentralManagerState.PoweredOn:
print("BLE powered on")
self.startScanning()
break
case CBCentralManagerState.Resetting:
print("BLE reset")
self.clearDevices()
case CBCentralManagerState.Unsupported:
print("BLE unsupported event")
break
}
}
func startScanning() {
print("\(NSDate()) Start scanning...")
if let central = centralManager {
let dictionaryOfOptions = [CBCentralManagerScanOptionAllowDuplicatesKey : true]
let ble = [CBUUID(string: "B737D0FF-AF53-9B83-E5D2-922140A9FFFF")]
central.scanForPeripheralsWithServices(ble, options: nil)
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered peripheral \(RSSI) dBM name: \(peripheral.name)")
print("UUID: \(peripheral.identifier.UUIDString)")
print("Peripheral \(peripheral.identifier)")
.....send event to server....
sleep(delayPolling)
self.startScanning()
}
If I change the scanForPeripheralsWithServices() without any CBUUID String it works fine:
central.scanForPeripheralsWithServices(nil, options: dictionaryOfOptions)
My main concern if to be able to launch this Thread in background and from Apple documentation, we need to scan for a specific device by specifying the CBUUID to be able to run my task.
Can you tell me where I'm wrong?