BLE Always getting "state = connecting" from CBPeripheral device - ios

I am getting stuck to make the connection between device and iPhone always I got this state even I keep the reference of Peripheral at the file level OR global. :(
CBPeripheral: 0x1700f7200, identifier =
6F44C292-FA88-41B2-9534-F9EFFDC02708, name = (null), state =
connecting
I have to transfer data to the device also send some command.
I used the below code in didDiscover peripheral:
if (peripheral.identifier.uuidString == "6F44C292-FA88-41B2-9534-F9EFFDC02708") {//6F44C292-FA88-41B2-9534-F9EFFDC02708
connectingPeripheral = peripheral
connectingPeripheral.delegate = self
cbManager.connect(connectingPeripheral, options: [CBConnectPeripheralOptionNotifyOnDisconnectionKey:true])
cbManager.stopScan()
}
OR is there any way to check BLE device is working or not?

Related

Scan for peripherals with services returns nothing

I'm trying to scan for a specific device by it's service uuid - in this case JBL speaker, in future cases, I would like to scan for devices inside a car.
At first, I scanned for nearby peripherals with nil.
self.centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:true])
Once I found my speaker (this is the data I get):
peripheral identifier = 0BD95D98-D979-67DA-F3AA-C6C03781E70B
discovered peripheral = <CBPeripheral: 0x283ed4000, identifier = 0BD95D98-D979-67DA-F3AA-C6C03781E70B, name = JBL Flip 4, state = disconnected>
advertisement data = ["kCBAdvDataRxSecondaryPHY": 0, "kCBAdvDataTimestamp": 643124133.584141, "kCBAdvDataIsConnectable": 1, "kCBAdvDataManufacturerData": <5700d11e 0100ff58>, "kCBAdvDataRxPrimaryPHY": 0, "kCBAdvDataLocalName": JBL Flip 4]
kCBAdvDataManufacturerData = {length = 8, bytes = 0x5700d11e0100ff58}
I connected to the speaker, and when I got the didConnect callback I called for discover services:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
self.connectedPeripheral = peripheral
peripheral.delegate = self
peripheral.discoverServices(nil)
}
When didDiscoverServices services returned I saw the following output:
services = Optional([<CBService: 0x281adc700, isPrimary = YES, UUID = 65786365-6C70-6F69-6E74-2E636F6D0000>, <CBService: 0x281adcb40, isPrimary = YES, UUID = FE8F>])
At this point, I wanted to use the service uuids that I got, and to scan for the JBL speaker.
So instead of called for scanForPeripherals with nil, I used an array with the services like this:
self.centralManager?.scanForPeripherals(withServices: [CBUUID(string: "FE8F"), CBUUID(string: "65786365-6C70-6F69-6E74-2E636F6D0000")], options: [CBCentralManagerScanOptionAllowDuplicatesKey:true])
Then I relaunched the app, but this time I didn't find any device.
Why is this so?
See the doc of scanForPeripherals(withServices:options:):
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
The service you got 65786365-6C70-6F69-6E74-2E636F6D0000, you got it AFTER you made before a connection to it (didConnect), and after "scanning more infos on the device" (discoverServices).
But it was not advertising it, it was not telling everyone that it has a service with that UUID. It keeps it secret. So you can't scan for it.

How to read characteristics in background BLE using CoreBluetooth

I'm developing app which reads specific characteristics in the background mode. Can this be achieved in the background with selecting the background mode as "Uses Bluetooth LE Accessories" in the plist. Is there any chances of rejecting the app in background if we read particular characteristics of a known service? Reading the characteristics should happen continuously. If we setNotify to "True" will this work in the background. Please provide some valuable suggestions/work around if anyone is aware of it. Thanks in advance.
Can this be achieved in the background with selecting the background mode as "Uses Bluetooth LE Accessories" in the plist.
Yes you can definitely read ble characteristics when your app is in background mode setting the “Uses Bluetooth LE Accessories” background mode in the capabilities tab
Is there any chances of rejecting the app in background if we read particular characteristics of a known service?
If your app needs ble background mode and correctly declares it in the capabilities tab, the app won’t be rejected
Reading the characteristics should happen continuously. If we setNotify to "True" will this work in the background
In order to get notified each time the ble characteristic of your ble peripheral changes its value, you can correctly setNotify to true on the desired characteristic. Each time the characteristic changes its value the peripheral(_:didUpdateStateFor:Error) callback will be triggered and you can get the updated value. Setting the background mode correctly, this callback will be triggered even if your application is in background and your phone is locked.
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,
error: Error?)
{
print("characteristic: ", characteristic)
guard let string = String(bytes: characteristic.value!, encoding: .utf8) else {
return
}
}
Capabilities -> On Background Modes -> Select External accessory communication & Uses Bluetooth LE accessories
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService,
error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
print(characteristic)
if characteristic.properties.contains(.read) {
print("\(characteristic.uuid): properties contains .read")
peripheral.readValue(for: characteristic)
}
if characteristic.properties.contains(.notify) {
print("\(characteristic.uuid): properties contains .notify")
}
}
}

CBCentralManager -- willRestore -- CBCentralManagerRestoredStateScanServicesKey

When restoring state with CBCentralManager the dictionary sometimes contains a scanned peripheral's UUID
internal func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
let scannedPeripherals = dict[CBCentralManagerRestoredStateScanServicesKey] as? [CBUUID]
}
In contrast to willRestoreState, didDiscoverPeripheral provides CBPeripheral objects that the Central Manager can connect with.
My question is, what can I do with the UUID? The Central Manager can't connect or detect the device when this happens.
Since the peripheral has already been discovered you don't need to scan for it.
You can pass the identifier to retrievePeripherals(withIdentifiers): in order to obtain a CBPeripheral instance that you can connect to.

Bluetooth BLE Device disconnects as soon as connected?

I've been writing an app using Swift that connects to a bluetooth BLE device. For some reason, the app doesn't always connect to the device. In this case it will connect but gets disconnected straight away. This only happens maybe 1 in 10 times it connects, but definitely interferes with the reliability of the app.
I'm using CoreBluetooth to connect to the BLE device. Attempting connection again usually always gets it to reconnect, and other apps that communicate with this device works correctly every time, so I'm confident that it is not a problem with the peripheral.
I'd just like to know if there is anyone out there who has had a similar issue or if there is a particular reason why this may be happening?
EDIT: Here's the code for the willSelectRow of my table. This is where I get the peripheral to connect.
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
centralManager.stopScan()
connectingPeripheral.append(discoveredDeviceArrayInfo[indexPath.row])
connectPeripheralNow(connectingPeripheral[0])
return indexPath
}
This is where I get it to connect, at this point I select the row which sets the CBPeripheral details of the device to connect to.
connectPeripheralNow looks like this:
func connectPeripheralNow(peripheral: CBPeripheral!){
self.centralManager.connectPeripheral(peripheral, options: nil)
}
didConnectPeripheral and didDiscoverServices looks like this
func centralManager(central: CBCentralManager,didConnectPeripheral peripheral: CBPeripheral)
{
peripheral.delegate = self
peripheral.discoverServices([CBUUID(string: "FFE5")])
print("SUCCESS: Connected to " + peripheral.name!)
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?)
{
if let servicePeripherals = peripheral.services as [CBService]!
{
for servicePeripheral in servicePeripherals
{
print("INFORMATION: Service discovered " + String(stringInterpolationSegment: servicePeripheral.UUID))
peripheral.discoverCharacteristics(nil, forService: servicePeripheral)
}
}
}
Just for the info, I do get a 'SUCCESS: Connected to xxx' message appear which shows that it is connecting. If you need more code, let me know!
What you are mentioning is more of an expected behaviour. BTLE is designed to consume very little amount of energy so it drops the connection as soon as possible. If you do not need a permanent connection then follow Discover --> Connect --> Read/write --> Setup timer --> When timer fires Connect.
If you need a permanent connection, you should subscribe for characteristic which transmits real time data like heart rate application. You would need to implement setNotifyValue:forCharacteristic: method. Read Apple Documentation for more details.

CBCentralManager didFailToConnectPeripheral

I'm developing an app for OS X (10.10) and iOS 8 to exchange some messages with a BLE peripheral. I want my app to find and connect to the peripheral, to send the message to it, and lastly to disconnect, so that the peripheral is free to be accessed from some other device. Everything works fine except for the following particular but still important situation: I scan for BLE devices and I find my device but still I don't connect to it because I have no messages to send. Then I walk away (out of range) or simply I turn the peripheral off. I still have saved in a var my CBPeripheral and I try to connect to it. Nothing happens while I would like the method centralManager:didFailToConnectPeripheral:error: to be called so that I can start to scan again.
centralManager:didConnectPeripheral working properly.
The documentation tells that bluetooth connection requests don't time out so I was wondering what other kinds of problems could lead to a connection failure
func connect(){
if let peripheral = self.peripheralBLE { //Check if the CBPeripheral I was looking for is properly stored
centralManager!.connectPeripheral(peripheral, options: nil)
}
else {
println("Nothing to connect"); //Didn't find any peripheral
}
}
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
print("connected");
println(peripheral.name);
if (peripheral == nil) {
return;
}
// Create new service class
if (peripheral == self.peripheralBLE) { //if it is the peripheral I was looking for then initialize the service for communication
self.bleService = RFduino(initWithPeripheral: peripheral)
}
connectionStatus = .connected; //update connection status
// Stop scanning for new devices
central.stopScan()
}
func centralManager(central: CBCentralManager!, didFailToConnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
println("Failed to connect peripheral");
if peripheral == self.peripheralBLE{
self.bleService = nil;
self.peripheralBLE = nil;
self.connectionStatus = .disconnected;
}
self.startScanning();
}

Resources