I have worked on an app which read heart rate from server kinds of bluetooth low energy HRM(Heart Rate Monitor)by bluetooth notification.
And I have added the "App communicates using CoreBluetooth" into the app's "Background Modes".
It works fine when the app is in foreground, but when the app is in background, one of the Heart Rate Monitors is always to be disconnect with the app in sever minutes.
So I debugged the app and find that, this kind of HRM is always push notification irregularly.The interval between notifications is from 1 second to 20+ second.
I know that when the app is in background, iOS will stop the bluetooth connection if it is too long between bluetooth notifications.
So my question is: Is it possible to keep the connection when the app is in background?
Is reconnection the only way to solve my problem?
Detecting the disconnection and reconnecting to the peripheral will make your app more robust as it will handle the case where the peripheral goes out of range and then returns in addition to the case you are seeing.
When the peripheral is disconnected your centralManager:didDisconnectPeripheral method will be called on your delegate. In this method you can call connectPeripheral:options again to re-establish the connection. iOS will either do this immediately if the device is still in range or automatically later, once the device comes back into range.
Once your centralManager:didConnectPeripheral method is called you can re-establish the characteristic monitoring.
Edit Clarify that you can call connectPeripheral immediately
Related
I am currently using CoreBluetooth to scan for peripherals. Every 30 seconds it sends its packet of information to the cloud by using a Timer. This is working good in foreground. I would like this exact operation to function seamlessly in the background too.
I have declared a CBUUID ahead of time for it seek out the designated peripheral in the background. Upon entering background mode, the scan stops functioning after 10~ seconds. How do I continually make the scan operate continue in the background?
I was looking into Bluetooth State Preservation, would this alleviate the issue? Should it also not be on the main thread?
After the packets are advertised I would like them stored in memory. I am aware the DiscoverPeripherals logs the peripherals... is it possible to log the RSSI and additional peripheral data in chronological order and have it an operation take affect per an interval? I was looking at BGProcessingTask to fire off a function in the background. Would a better approach be to use CoreData to store the memory and clear it after? Best/easy suggestion is appreicated.
I have changed the CBUUID to the right peripheral for centralManager.scanForPeripherals(withServices: [uuid], options: nil)
with uuid being the CBUUID. That seem to be allowing the peripheral to be detected in the background. It stopped logging the scan after a short period of time. Right when I put the app in background, it functions properly for a bit. I theorize it could just be calling it on the same thread and it may not be operating in the background all together. I did test this by changing the the withServices to nil, the result was that the operating was not being logged at all once I closed to the background.
I was expecting the operation to continually be scanning on the basis of the Timer every period, after the period is up it would send it the cloud just how it was doing it in the foreground.
There are lots of restrictions on iOS apps when they are not in the foreground. Once an app moves from the foreground it is suspended and can only execute in the background for specific reasons and for limited durations.
In general, anything based on a Timer will not fire when the app is not in the foreground.
Some Core Bluetooth events are delivered while your app is in the background:
Pending connect operations can complete with a corresponding delivery to your app.
Peripheral disconnections will be delivered to your app
GATT Notify/Indicate operations from a connected peripheral will be delivered to your app.
Discovery of new peripherals advertising a service that you are specifically scanning for will be delivered to your app.
It is this last behaviour that you are relying on.
While you have done the right thing by specifying the specific service you are interested in, your plans are being thwarted by the fact that Core Bluetooth will not deliver repeated discovery notifications for a particular peripheral.
When your app is in the foreground you can use the CBCentralManagerAllowDuplicatesKey option to request a discovery notification each time a peripheral advertisement is seen, even if an advertisement from that peripheral has been seen before. This option has no effect when your app is not in the background.
The best way to gather data on a periodic basis from a peripheral (whether in the background or foreground) is for that peripheral to send its data via Notify/Indicate, however you seem to be trying to scan for the existence of peripherals rather than gather specific data from them, so this may not work for you.
When one of the supported Core Bluetooth background events occurs and your app has been jettisoned, State restoration allows your app to respond after iOS relaunches your app. It will not help you in this case.
It probably isn't possible to do what you want, at least not without changing the behaviour of your peripheral.
I have a use case where my objective-c application needs to immediately use iBeacon after it's been terminated in order to wake the application up from a terminated state, connect to BLE and send a command to the device. I have a larger longer running post found here that you can check out for my code if need be.
The Problem
The problem so far, happens when I run the application, search for previously paired devices and/or scan for peripherals, find my BLE device and connect. Once connected the user pairs the BLE connection so that they can send encrypted characteristic data over BLE connection. Without pairing (aka auth/bond in the device's naming conventions) the user cannot send the data to the device at all. It never makes it there. As soon as you pair you can send the command...
When I terminate the app, in the applicationWillTerminate method, I run through this code...
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(#"*** Application Will Terminate.");
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSNumber *mode = [userDefaults objectForKey:#"deviceConnectedMode"];
if([mode intValue] == CONNECTED_MODE_INDICATOR) {
[self.bluetoothManager sendCodeToBTDevice:#"magiccommand1"
characteristic:self.bluetoothManager.wipeCharacteristic];
//I have been turning this command on and off in testing to see if I can get it to work better while disconnecting in the device rather than in the app...
//The command magiccommand2 wipes the auth/bond inside of the device
// [self.bluetoothManager sendCodeToBTDevice:#"magiccommand2"
// characteristic:self.bluetoothManager.disconnectCharacteristic];
//Place where I attempt to stop being connected to BT
[self.bluetoothManager disconnectDevice];
[self.beaconManager startMonitoring];
NSLog(#"*** Application terminated from connected mode!");
} else {
NSLog(#"*** DriveCare terminated without violation!");
}
}
What I am trying to accomplish
The magiccommand1 and magiccommand2 commands are just silly test strings (for now, 128 bit tokens later) that the device is listening for over the serial port. Once they receive the commands they react by attempting to wipe the auth/bond on the device and also disconnecting from BLE in the device.
So the only way I can seem to get the app to wake up from a terminated state is with iBeacon. So I am having to do a bunch of seemingly dirty stuff here just to get this round peg in a square hole. In the app's lifecycle it connects and pairs and when I terminate I want it to completely be removed as a connected device from BLE. My hope is that iBeacon will wake up the app, connect back to BLE, shut off iBeacon monitoring and then send a command to the BLE device from a terminated state. This turning on/off or connecting/disconnecting from iBeacon to BLE and back would most likely cause the user to have to re-pair and I don't want this.
More Problems
When I call [self.centralManager cancelPeripheralConnection:self.thePeripheral]; the iOS system level BT manager seems to auto reconnect almost instantly (because of the pairing) so there is no time for the connection to be severed and iBeacon to be picked up again. If I attempt to disconnect from my centralManager instance before sending the disconnect command to my box (as seen above in code commented out) they obviously wont send either. If I just use only that CBCentralManager disconnect method it isn't enough for iBeacon to begin being detected as the iOS system is still paired with the device. Lastly, if I then go into my iOS system BT manager and choose "Forget this device", the iBeacon gets picked up again and my didEnterRegion method fires!
This is a lot of back and forth between iBeacon and BLE and I just wish I didn't even need iBeacon to wake up the app. I have all info.plist background BLE and iBeacon services turned on. If I don't connect to BLE at all and never pair once and connect my device, the local app notifications slide in without problem letting me know that the iBeacon didEnterRegion and didExitRegion methods are being fired without trouble.
What am I doing wrong here?
I figured out this answer! I was sort of on the right path... It's not a great idea to try and disconnect from BT pairing in order to try to get iBeacon delegate methods to fire. It causes a lot of weird dirty code, tightly coupled things that shouldn't be and worse... the dreaded spaghetti code.
Instead my solution was to build a board with 2 Bluetooth chips in it. One as a dedicated iBeacon chip and the other as a permanent connected/paired/auth and bonded chip. This way there does not need to be any timed connection and disconnection events to be monitored (or worse... resolved race conditions). Waiting for a device to disconnect in iOS is not a true disconnect, it is instead when the iOS system thinks it's disconnected (in the phone's side of things). The things going on inside of the peripheral system paint a very different picture...
This 2 chip approach made life so much easier on the development side because now anytime you need the application to wake up to handle something, you can power cycle the iBeacon device and receive the appropriate event.
Side note: When using iBeacon the didEnterRegion method fires almost instantly where as the didExitRegion can take 30 or more seconds sometimes to fire. Therefore if you need immediate beacon detection resolution you should try to make sure the iBeacon is being powered on and not off for this... if even possible.
I am trying to create an app that wakes up to background mode whenever it detects a new peripheral that advertises a pre-defined service. I want the wakeup to happen even if the user minimized the app, the app was suspended, the app was killed by the user or the app was killed by the system. Obviously I also want the app to be accepted to the app store (so don't suggest solutions like adding all possible UIBackgroundModes and run in the background forever...).
I can add bluetooth-central to the UIBackgroundModes because my app fits the description here - it needs to communicate with a peripheral in the background.
I found some documentation here about State Preservation and Restoration. It describes a scenario where an app connects to a bluetooth door lock, and the connection breaks because the user went far away from the door. From what I understand, when the user returns to the door and the iPhone detects the door lock (by its MAC address, I suppose), it reconnects to the door lock and wakes up the app in Background mode (please correct me if I am wrong).
I don't want to preserve a connection to the peripheral (and waste its battery). I want the app to run scanForPeripheralWithServices that will wake up the app when a new peripheral is detected, even if the app was killed\suspended by the user\system.
I found a relevant answer here to a different question, saying I could simulate an app-termination-by-the-OS using kill(getpid(), SIGKILL); and then, if my app uses State Preservation & Restoration, it should wake up (right?).
When my app detects a new\old peripheral, I want it to create a quick connection, read some characteristics and disconnect. Unforetunately this is not possible with iBeacon Monitoring that does not give a Peripheral object (only a region). I know I can detect the peripheral by running scanForPeripheralsWithServices after my app enters the iBeacon region, but this feels strange - a direct continuous scanForPeripheralsWithServices that survives suspension & kill, would make much more sense.
Any ideas?
If you know for sure that this is not possible, and depending on iBeacon Monitoring is the only way, please let me know.
The key part of the question is:
I want the app to run scanForPeripheralWithServices that will wake up the app when a new peripheral is detected, even if the app was killed\suspended by the user\system.
Unfortunately, this is not possible with CoreBluetooth alone. If the user kills an app, it will not get new OS launch events from CoreBluetooth unless the user manually launches the app again.
Fortunately, CoreLocation does not have this restriction. Since iOS 7.1, it will launch a killed app to notify it of a Bluetooth beacon detection. This would allow you a few seconds of background running time to scan for peripherals.
I am working on an iOS Bluetooth LE application. First I pair the IOS (central) to my bluetooth chip (peripheral)
1) Discover peripherals.
2) Connect to peripheral.
3) Getting services and characteristics.
4) Able to read data and write data from the characteristics.
5) Save the peripheral
6) Disconnect from the peripheral
When I run the app the second time, it connects to the peripheral automatically and writes and reads data from the characteristic and disconnects from it. The App works in the background for 10 minutes.
I would like to run this app in the background. That is I want to call the methods which connect, write and read values in the background every 6 hours. Is this possible? My App works in the background for only 10 minutes. Any suggestion will be of great help.
First you will need to be sure that you added:
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>
to your Info.plist which will allow your app to run in the background.
However this will work as follows: it will allow your app to "wake up" to any bluetooth event and run methods that you like to fire when your app is getting any bluetooth activity.
So for example:
you can scan for bluetooth devices in the background and once found something that fits your criterias you can run code, as didDiscoverPeripheral fires (connect / read / write etc..).
you can be connected to a device and subscribed for notification from a specific characteristic. Once the characteristic changes value your app in the background should get a notification and didUpdateValueForCharacteristic callback will be called so again you can call methods from this point, still running in the background.
maybe you can think of other use-cases. I've mentioned these scenarios to give you an idea how you can definitely work in the background with BTLE.
These scenarios should not be limited to the 10 minutes timeframe.
However I have noticed that if you run something else in background mode (e.g: a loop) and the 10 minutes passes your app might be killed from the background so even the above mentioned callbacks will not work.
So to answer your 6 hours question: it would be possible if your BTLE device could initiate some BLE action every 6 hours to wake your app up. OTherwise I'm not sure you can start your activity from the app (ios) side.
When using the BLE with CoreBluetooth (no iBeacon), is there a way to wake an app that is not running when the device receives a Bluetooth signal?
I'm simulating a beacon with the RedBearlab's BLE Shield (http://redbearlab.com/bleshield/).
Thanks,
DAN
* UPDATE 03/05/14 *
It looks like Apple has introduced a major update with iOS 7.1: now iOS will open your app for you if it detects a UUID that matches your app. The app only needs to be installed, it doesn't have to be running (logic in AppDelegate needed to answer the wake-up call).
If the app was running in the background and then closed (I mean here terminated - and you do not see it anymore in the list of background apps when you double click the home button) due to memory pressure from other apps, then the iOS will take care of waking it up again when a delegate is called (assuming you have background modes listed in .plist).
If the app was running in the background and then closed by the user (again I mean here terminated. So the user double clicked to get the list of apps running in the background and then clicked on your app in the background list until it wiggled and then pressed the 'x' next to the app to kill it) then this is a clear indication that the user does not want the app running in the background and the app will not be re-launched. The user has to relaunch the app for its delegates to start functioning again in the background (new in iOS7).
Update:
I am not sure if this is in the documentation but it is in CoreBluetooth WWDC 2013 Video. I recommend you watch it. They spent a good portion of the video on how CoreBluetooth behaves in the background.
From what I understand, if your app has not previously connected with the BLE Peripheral, then no.
If your app has previously connected with the BLE Peripheral, then use:
-connectPeripheral:options
Connection requests don't time out. The best place to call this method is when your app loses connectivity with the BLE peripheral. You will get notified when you lose connection to the peripheral in the CBCentralManagerDelegate Protocol:
-centralManager: didDisconnectPeripheral: error
So the next time your App comes in range of the BLE Peripheral, it will trigger this method. Also note that you will need to set up State Preservation and Restoration when you initialize a CBCentralManager.
https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
the system wakes up your app when any of the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are
invoked, allowing your app to handle important central role events,
such as when a connection is established or torn down, when a
peripheral sends updated characteristic values, and when a central
manager’s state changes.
To perform certain peripheral role tasks while in the background, you
must include the UIBackgroundModes key with the bluetooth-peripheral
value in your app’s Info.plist file. When this key-value pair is
included in the app’s Info.plist file, the system wakes up your app to
process read, write, and subscription events.
You could try to declare voip in info.plist. For the time being, my application was automatically relaunched after a time, even user terminated it.