iOS Bluetooth LE cant get notification programatically but can in other apps - ios

I'm trying to get my iOS program to communicate with a custom Bluetooth LE device. I can connect to it, read all the services, and read all the CBCharacteristic objects for each service.
I am trying to get notified when one specific CBCharacteristic is updated. The CBCharacteristic.properties is set to 0x10 (Notify) but the CBCharacteristic.isNotifying is false.
After calling the following line of code:
myDevice.peripheral.setNotifyValue(true, forCharacteristic: myChar)
I am expecting to receive notifications via the CBPeripheralDelegate function:
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {}
but it never gets called.
In addition, using the LightBlue Explorer app from PunchThrough.com I CAN get notifications, so I know it is possible. What is the LightBlue app doing that I am not?
Has anyone seen something similar to this?
For reference, the device uses WLT2564M Bluetooth module. Also I can read the value using
myDevice.peripheral.readValueForCharacteristic(myChar)
without any issues.

A few things to check:
the peripheral property needs to persist for the duration of its use
you must have connected to it (and weren't disconnected in the meantime)
check that the delegate is properly set
check logs
make sure you implement peripheral:didUpdateNotificationStateForCharacteristic:error: and log any errors there

Related

BLE device scanning in background using CoreBluetooth swift

I have an app demanding to scan BLE devices around in background mode when app is not active in foreground.
I have implemented such functionality using CoreBluetooth framework. This is code, I am using to scan device. First I have all device in DB, fetch and creating array.
for item in self.allItems ?? [] {
let uuid = UUID(uuidString: item.identifier)!
let id = CBUUID(nsuuid: uuid)
self.allServiceIds.append(id)
}
And when start scanning, passing same array in method.
self.centralManager?.scanForPeripherals(withServices: self.allServiceIds, options: [CBCentralManagerScanOptionAllowDuplicatesKey:true])
Also I have tried to pass service ids in array as I read lots of articles suggesting in background mode it is required.
Also inside Capabilities, I have checked required options. But still it is not scanning in when app is in background.
Any ideas are welcome.
A few tips:
Get your app working in the foreground first. Only once this is working should you try to get it working in the background.
Make sure that bluetooth is turned on in phone settings.
Make sure you have obtained Bluetooth permission from the user. An iPhone will send a dialog to prompt you for this permission the first time an app runs (it is triggered by CBCentralManager usage.) If you deny this permission, you won't see the dialog again and you must go to Settings -> Your App -> Permissions -> Bluetooth to enable it manually.
Take care that you have set the CBCentralManagerDelegate properly and that you are getting callbacks. In particular, log the callback to centralManagerDidUpdateState(_ central: CBCentralManager) and make sure that you see central.state transition to .poweredOn. Only once you reach the poweredOn state should you start scanning. See: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518888-centralmanagerdidupdatestate
Start off testing in the foreground without filtering self.centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:true]) (note the withServices: nil). Only once you get callbacks detecting peripherals should you start filtering for specific service UUIDs. This will help you eliminate all other problems before filtering on the service.
If you can get all of the above working, there are tricks you can do to get duplicate detections even in the background.
If you cannot get the above working, please described the results of the above, and show more of your code including where you instantiate the CBCentralManager, where you set the delegate, showing the delegate callback methods, and describing which delegate callbacks get called and when.

didWriteValueFor not calling for second time when try to write using .writeValue

I am working on an app which we are connecting a chip by BLE and performing an operation like Connect/Read/Edit/Write.
Reading task working properly and try to write data after connection successful with the chip but getting the issue described below:
Connect BLE
Write first package of NSData using the .writeValue method on selected peripheral and characteristics.
Sent package call didWriteValueFor delegate method's as response.
Again I sent/write a second package of NSData using .writeValue
on different/static/predefined characteristics with it's matched peripheral. But the second time the response is not coming or the didWriteValueFor delegate is not calling.
I have managed/converted byte data in data NSData same as first to write/sent a package to BLE.
I send package data the first time and it's responding properly, but the second time it's not responding and no delegate is calling didWriteValueFor or didUpdateValueFor.
Thanks in advance.

iOS BLE CentralManager didFailToConnect to peripheral not being called

In the case where a peripheral has been discovered by a central, but right before user presses a button to connect, the peripheral goes out of range, so the connection cannot be completed. What method is called by CoreBluetooth?
I thought that centralManager didFailToConnect would be called, but in my code it is not being called, perhaps because the connection never started at all?
In this scenario which method should I use.
Thanks in advance for any help.
public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("[ERROR] Could not connect to peripheral \(peripheral.identifier.uuidString) error: \(error!.localizedDescription)")
}
As said in the previous answer, none of the delegate methods will be called if any device does not initiated the connecting process.didFailToConnect method only invoked if the device initiated a connection procedure but due to any reason that device could not able to connect.
If you want to check if any device is there for the connection, then you need to create one timer for the specific time period and if during that period if no device is scanned then you can show a message showing "no device available" and again check for devices.
Nothing will be called in this scenario since the peripheral was not connected, nor was there an error connecting to the peripheral.
Core Bluetooth will have a pending connection for that peripheral and once it comes back into range the connection will complete and didConnectPeripheral will be called.
I faced a similar case,
What I needed was to handle a scenario where the user already had the device discovered but has waited some time before trying to connect and the device might not be available to connect anymore.
I didn't need any complex solution to maintain the In-Range status for the device just a solution that would recheck if the previously discovered device is still in-range without actually needing to maintain any status.
I solved it using below method:
I created a timeout logic of around 5 seconds (which we will invalidate if the connection was successful before the timeout) along with the connection request action.
If the connection was not made within 5 seconds, the timeout block will execute the disconnection request on the previously discovered peripheral which will cause the didFail delegate to be called by the central manager.
Timer.scheduledTimer(withTimeInterval: 5, repeats: false, block: { timer in
DEVICE_SCANNER_MANAGER.disconnectPeripheral()
DispatchQueue.main.async {
Utility.showAlert(vc: self, title: "", msg: "device went out of range", btnPrimary: "OK", handler: nil)
}
})
DEVICE_SCANNER_MANAGER => Custom manager class to handle all peripheral related operations.

iOS: didDiscoverPeripheral not called in Background mode

I am Working on BLE project, everything works fine when the app in the foreground.It can discover and connect to the peripheral, all the call back method work perfectly.
But the problem is that, when the app in the background mode (I press home button). Only the centralManagerDidUpdateState delegate method get called.
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBCentralManagerStatePoweredOn:
[self.cbCentralManager scanForPeripheralsWithServices:nil options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
break;
default:
break;
}
}
I use scanForPeripheralsWithServices:nil option, But when the app in the background, the didDiscoverPeripheral call back never called. I have edit my plist file with "bluetooth-central" option to support ble central role in background.
Any idea why didDiscoverPeripheral method not call when app in the background?
Paulw11 said are right, If your app find the peripherals in the foreground. It will not call the didDiscoverPeripheral for the same peripherals when it enters the background.
For more information about the iOS BLE Behavior in the background mode. You can check this answer
What exactly can CoreBluetooth applications do whilst in the background?
I was working on Estimote Nearable type beacons. After iOS10 SDK update, I encountered exception from CBCentralManager stating :
<CBCentralManager: 0x17009e050> has provided a restore identifier but the delegate doesn't implement the centralManager:willRestoreState: method
To fix this, Turn-On "Background Mode", in Xcode -> Capabilities -> Background Mode
Scan for nil( scanForPeripheralsWithServices:nil) services will not work in background. You must search for some specific service in background.
You have to set the UUID in scanForPeripheralsWithServices: method which Peripherals/BLE device is advertising.
From Official Apple reference
You can provide an array of CBUUID objects—representing service
UUIDs—in the serviceUUIDs parameter. When you do, the central manager
returns only peripherals that advertise the services you specify
(recommended). If the serviceUUIDs parameter is nil, all discovered
peripherals are returned regardless of their supported services (not
recommended). If the central manager is already scanning with
different parameters, the provided parameters replace them. When the
central manager object discovers a peripheral, it calls the
centralManager:didDiscoverPeripheral:advertisementData:RSSI: method of
its delegate object.
Apps that have specified the bluetooth-central background mode are
allowed to scan while in the background. That said, they must
explicitly scan for one or more services by specifying them in the
serviceUUIDs parameter. The CBCentralManagerOptionShowPowerAlertKey
scan option is ignored while scanning in the background.
Here
Apps that have specified the bluetooth-central background mode are allowed to scan while in the background. That said, they must explicitly scan for one or more services by specifying them in the serviceUUIDs parameter.
So scanForPeripheralsWithServices:nil with nil it will not work in background , you need to specify list of UUIDS

Any private/public API's for getting WIFI signal strength (RSSI) for iOS7 or 8

Hello my app needs to retrieve current wifi signal strength for iOS 7 devices and above. What I have found so far..
Can get SSID and BSSID from CaptiveNetwork api/framework but not signal strength
From my understanding Apple80211 is outdated and doesn't work with iOS 7 and above. MobileWifi.framework is an alternative that apparently works but a jailbreak is needed (I need non jailbreak).
So can anyone help me here? And please don't point me at other stackoverflow questions unless they provide me with a solution, thanks.
There are methods in iOS 7 and earlier that support direct access to RSSI; however, these seem to be deprecated in iOS 8.
It turns out that Apple's (admittedly beta) documentation was wrong. You need to use the protocol, CBPeripheralDelegate.
method def:
override func peripheral(peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: NSError?) {
<#code#>
}
You call the peripheral's -readRSSI: method and it will callback to the above function, where you can extract the value from the NSNumber param.

Resources