the BLE device only advertise when button is pressed
I think i don't completely understand the paring protocol...and i cant find an answer to my problem:
My application scans for devices , upon advertising i connect to it, discover it's services and register to get notifications and indications from it (which i get)... I do get the broadcasts for pairing request and pair.
when I turn the BLE device off, and then on and try to get it's notifications nothing happens! (I don't get notifications or indications )until I press the advertising button which triggers the hole process again.
Is this hoe its supposed to work? Whiteout advertising the devices will not be connected?
do I need to call device.connect() in a loop all the time that i'm not connected?
Many thanks to all answerers
Desperate me.
So actually what solved my problem was the second parameter in the connectGatt function... which means autoConnect..
device.connectGatt(context, true, mGattCallback);
You can’t create a connection between two devices without using advertisements.
There is an interval after each advertisement in which the device listens for connection requests. So, you can connect directly without scanning for the advertisements, but the device must be advertising in order to be able to connect to it.
Related
We want Android to automatically connect to our custom made BLE peripheral.
Our peripheral should regularly (but infrequently) advertise and attempt to Indicate some time-sensitive sensor data to the phone. Thus we want the phone to be ready to connect at any time.
Generally, you can pair a smart watch with an Android, and Android will then automatically connect to the smart watch whenever it is in range. So we believe our use case should be feasible.
I read a lot of answers that advise to set the "autoconnect" parameter to true when connecting. I have tried that and the reconnections don't persist through a reboot or even after disabling and re-enabling Bluetooth on Android. This answer by Brian says I should scan in the background, but Android made this unrealistic. If I use a foreground service, my users will hate the app. If I use a background service, I may miss the peripheral's attempts to connect during Android's Doze and the code becomes error prone.
Ideally, I want to do something like what Emil said in his answer here. Please read the follow up question and response.
However, we can't see our app through Android's Bluetooth settings. We can only connect to the peripheral and pair with it using our app (or nrf Connect). In desperation, I tried modifying the peripheral's advertising flags. Then I could see it in Android's Bluetooth settings. But when I try to pair using Android's settings, the attempt fails because the peripheral is not in "pairing mode".
We are building both the app and the peripheral, so we can change both. I want to know if our use case is possible and what we need to do to get it working. We are using the STM32WB for our peripheral.
Use a combination of these techniques:
Bond the device. This might be needed due to the crappy Android Bluetooth LE API design that doesn't take the "address type" as an extra parameter when connecting to a device. When you connect using the Bluetooth device address, it looks up a device with this address in the bonding info, and uses the corresponding address type (random or public).
Use connectGatt with autoConnect set to true. This means no timeout, as well as auto-reconnect if the connection drops. Even if it takes days or weeks until the peripheral starts advertising, it will still work.
Listen to https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#ACTION_STATE_CHANGED and restart your connections when Bluetooth is re-started.
Use a Foreground Service in your application's process to prevent the OS from killing the process. Users can nowadays hide the annoying notification in Android settings if they want to.
Listen to https://developer.android.com/reference/android/content/Intent#ACTION_BOOT_COMPLETED to start your app after boot, including your Foreground Service.
Listen to https://developer.android.com/reference/android/content/Intent#ACTION_PACKAGE_REPLACED to automatically restart your app after an app update. See https://stackoverflow.com/a/2134443/556495 for some instructions.
The best approach is to make sure your peripheral can be bonded. Once you have bonded with it you can ALWAYS use autoconnect because Android stores info about bonded devices and you don't have to scan for it anymore. Hence you avoid the issues with scanning in the background.
Although that resolves the need for scanning, you still need to deal with your app being killed once it is in the background. Using a Foreground Service is still the best solution to my knowledge. I don't think you users will hate your app for it...
I am new to Bluetooth, but have been working for a month or so on a Android Client that connects to a BLE peripheral to write data to a characteristic.
My BLE peripheral is a nrf52832 (Nordic) device and I have loaded the BLE_SM (security manager) example project so that I can use bonding/pairing for secure communications.
My Android code pairs/bonds successfully to the peripheral, but from what I can see the peripheral now stops advertising. I'm not sure if this is normal or it's because I received a gap.onDisconnection event of type REMOTE_USER_TERMINATED_CONNECTION.
Irrespective I am bonded so you would think I could now not need to scan anymore for the device and could just call device.connectGatt(), but it is not working and my callback gets a GATT STATE_DISCONNECTED event.
I read in another post that had pasted the following from Nordic (Tutorial):
It is not possible to connect to a peripheral which is not
advertising, even though one knows its address from before. This is
because the peripheral will only turn on the receiver for a set amount
of time after transmitting an advertisement. This time is used to
listen for connection requests and scan requests.
If this is in fact true, it seems I would be unable to stop the peripheral continually advertising, which is not helpful if I am trying to reduce the peripheral's power consumption.
The citation is 100% true. Connection setup has nothing to do with if the device is bonded or not (assuming you don't use directed advertising). If the peripheral is neither connected nor advertising, the radio is completely off and therefore a central can't connect.
If you want to minimize power consumption, make sure you don't advertise when you don't need to. Depending on your use case, can you have anything triggering advertising? For example a button or a sensor event?
If you need to advertise all the time, you can try use a longer advertising interval to save battery, but this will increase discovery and connection setup time.
What I want to know:
I'm wondering how the android system (like Android smart phone) auto-connects to devices which is paired before.
For example, I pair my bluetooth headset with my android smartphone in the procedure of "turn on scanning/advertising -> click pairing" on day 1. And when I turn on advertising on my headset, it connects automatically on day 2, 3, 4, and so on. The point is, I don't have to make my smartphone scan again to find my (paired) headset.
I can't understand how android system finds that the paired device is turned on. Does the android system scan periodically in background? Even if I don't click "scan" button?
Why I ask:
I want to make my app autoconnect to customized BLE device, after make pairing. I succeed to make pairing(bonding) with createBond() method, but after that, I couldn't find how to make autoconnect. I know I can turn on autoconnect function like this way, connectGatt(XXX, true, XXX), but this autoconnect function doesn't work when the BLE device is disconnected a few days.
So I want to make my app works like android system and Bluetooth headset. But I couldn't find how android bluetooth system works even I dig AOSP codes.
I found many questions (here and here) about problems like mine but there were no answers.
Thanks in advance.
Update:
I found that bt_btif gets activated (with LG smartphone and Nexus 5) when the paired headset is turned on (start advertising). But bt_btif doesn't get activated with my custom BLE device... What can I do?
Executing connectGatt(XXX, true, XXX) is the correct way to go. gatt.connect() also starts an auto connect. Once you execute that, your phone will scan for the device and once it appears it connects to it. The only thing that interrupt this call is either if Bluetooth is turned off on the phone or if your app process is being killed. To avoid your process from being killed, let your app have a foreground service.
One gotcha however, there is a bug in Android which will sometimes make auto connect do a direct connect instead (and cancel after 30 seconds or so). See https://code.google.com/p/android/issues/detail?id=69834. You need to use reflection to avoid this bug.
I think I found a solution.
First, the solution for my question: Android smartphone seems to detect state changes of nearby bluetooth devices from the hardware sides. When the paired bluetooth headset starts to advertise, a callback in HAL (I think) is called.
So I made my app to connectGatt with autoConnection=True to the device that I want, by using MAC address, when the activity is started (in onResume() of MainActivity).
The connection would fail if my BLE peripheral device is not advertising. But the device auto-connects when it starts advertising, because the autoConnection parameter was set to true.
I've done a similar app and i didn't have problem with that. As a last resort for your problem, i would suggest writing the BLE Device Address in a simple DB table and manually connect to it. I've made something like this here
. It is no best practice code, but i hope you can find ideas for you solution.
Using both Android 4.3/Samsung BLE 2.0 SDK, it is observed that when a peripheral is turned off, the SDK will receive onConnectionStateChange (DEVICE_DISCONNECTED) either immediately or after ~20s delay. From my experience this depends on the peripheral implementation, some of them will tried to report they are being turned off and some just doesn't, so the SDK have to wait for ~20s for the timeout.
To remove this behaviour, I tried to use a Timer to check if I can read a certain characteristic. If the read timed out, I will call disconnect(Android 4.3)/cancelConnection(Samsung) to terminate the connection. The call itself is successful and the onConnectionStateChange callback return a status GATT_SUCCESS. Then I turned the peripheral on and connect to it immediately, discover the services , and encounter problem when I tried to read/write/notify any notification. By using LightBlue in iOS I can confirm that the peripheral is not connected.
After exactly 20s from turning off the peripheral, I will receive a DEVICE_DISCONNECTED callback. I connect again afterwards, and everything operates just fine.
There are two question :
1. Are we supposed to connect to the peripheral during the 20s delay?
2. Is there any way to get notified when a peripheral is turned off?
Thanks in advance.
Are we supposed to connect to the peripheral during the 20s delay?
No, It seems from you result itself that OS is doing some actions within the phone to control DEVICE_CONNECTED or DEVICE_DISCONNECTED events, This may be due to several reasons like device architecture, security reasons or callback itself is delayed to save energy
Is there any way to get notified when a peripheral is turned off?
No I dont think so, The event received for any action is broadcasted so its waiting for a signal from the device it self but its not getting one, the only thing that you can do here is to send it via some other broadcast(or HTTP request) from the other end of the device to achieve it
I am no Bluetooth specialist and wondering what possibilities are available to find already paired Bluetooth devices automatically when they are range of each other.
Background:
In our case an Android application needs to connect to a dedicated accessory via Bluetooth (Rfcomm). Both devices are known to each other (they are paired). The Android application registers a broadcast receiver. During the startup of the application, the app initiates a discovery to find the dedicated accessory. If the accessory is in range everything works great.
Problem:
The user starts the application outside the range of the dedicated accessory. The Android application tries to discover the accessory without success. Then the user goes into the range of the Bluetooth accessory. The broadcast receiver won’t get notified about the accessory that is in range now.
Similar Thread / Possible Solutions
Similar questions were already asked on stackoverflow (e.g. autoconnect to bluetooth device when in range).
But continuously trying to discover Bluetooth devices in range isn't what I am looking for because this would cause too much battery drain of the Android device.
Another solution would be to try to connect to the paired device in the onResume method of the Activity. This would work but has the disadvantage that the application can’t run in the background. So the user had to bring the application at least once to the foreground to initiate the connection.
A third idea I thought about is to implement a server socket into the Android application too. When the android application is started and the discovery finished without success, the Android application could create server socket and to listen to incoming notifications of the accessory. This would help in some scenarios (e.g. the user starts his application, approaches the accessory, activates the accessory and the accessory notifies the application on startup that it is in range now). But this is still no 100% solution because both devices can start outside the range of each other. Also it would be mandatory to implement additional functionality (Bluetooth server socket in the Android device…).
So I am wondering if better solutions exist. I am looking for a solution where no additional ServerSockets are required and I always get the notification that the two already paired devices are in range of each other :-)
Thanks for any help!
After connecting the device for the first time, keep the mac address in a local list.
On disconnect, use connectGatt with autoconnect set to true to automatically re-connect when you are in range.
Not a full solution, but maybe it's sufficient for your app to poll the accesory's presence whenever the screen is turned on? In that case, this may be helpful: Start Activity on wake up/sleep in Android