I am developing an Android app that scans for BLE devices periodically, and use the beacon information for further processes. I need to catch the Beacon which is closest.
// Scan for bluetooth devices and parse results
private void scanLeDevice() {
// Stops scanning after a pre-defined scan period.
if(!mScanning){
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d(TAG, "stop scanning");
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
Log.v(TAG, "closing scan. beacon found:" + beaconFound);
broadcastLocalUpdate();
beaconFound = false;
stopSelf();
}
}, SCAN_PERIOD);
proximity=null;
totalDevices=0;
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
Log.d(TAG, "start scanning");
}
}
The beacons I use:
Emit an advertisement 3x each second
Emit over 3 different channels
I have over 5 in a room
On one of my test devices, the Sony Xperia z2, this works. I get a long list of devices with Beacon information and RSSI values. each Beacon is discovered multiple times each scan.
On my other test device, the Motorolla Moto G(2nd generation), this scanning behaves WILDLY UNPREDICTABLE. On most scans only 2 or 3 devices are discovered. Other occasions it discovers about 10 devices (includes duplicates). It does not matter whether these devices are close or far.
Is this a software issue or a hardware issue?
How can I improve the results of a BLE scan?
Your beacons are not advertising frequently enough, creating a very real chance that no transmission occurs while the receiver is monitoring on its frequency. Try for at least 10 transmissions a second, but preferably more if you have an external power source.
Of course it is not all in the transmitter - Android phones vary drastically in how much they receive to detect BLE advertisements, and may report drastically different signal levels under the same conditions.
You may want to collect results over several scans in a short period, and look at the aggregate data, not just that of a single scan.
The scan result is for each received adv. No matter what rssi.
The antenna is a shared resource on most phones.
Wifi, bt classic, ble. So the phone only listens for adv in windows. Only if the adv falls into this window will get callback. The phone must listen on 3 channels.
So try playing with your adv interval and adv package size. If you have many in the same room you should not use too fast adv interval as the adv channels will be filled and collisions will occur.
Try turning off wifi and retest.
Avoid having classic bt connection.
Put name in scanresponse if you can to keep adv package short.
Related
With Android 4.3, Android implemented the idea of always-on WiFi where, even if you had Wi-Fi toggled off, the device and apps could still scan for WiFi networks to improve the location's accuracy. Along with using network triangulation, it's another way of getting your current position as quickly as possible without having to rely too much on GPS signals.
Android M is taking the idea further, adding Bluetooth scanning to the equation. Under the Location settings on M, you'll find a Scanning option in the menu, where both Wifi and Bluetooth scanning can be toggled on and off. When enabled, Bluetooth scanning will presumably look for BLE devices like beacons to get a quicker location fix.
Image resized. Click to view in full size
This may be very useful in the future inside malls, airports, and various indoor or underground locations where the reach and dispersion of Bluetooth beacons can outweigh a slow or impossible GPS signal lock. And the fact that it's always on, accessible whenever apps need a location fix, will make it even handier than if you had to remember to manually turn on Bluetooth.
Can anyone help in providing some insights or sample code for scanning for beacons with BLE without the main Bluetooth settings turned on?
I figured it out.
We have to write a system application and use the
BluetoothAdapter.enableBLE()
method.
This method is for special/system apps which use Bluetooth Low energy to scan for nearby devices which is mostly used for location accuracy.Even if the Bluetooth is turned off in the device settings.
Then we can use
BluetoothAdapter.LeScanCallback
callback to get the device details.
Sample:
for calling the method:
mBluetoothAdapter.enableBLE())
for callback:
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if( device == null ){
System.out.println("-------onLeScan "+device);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
Thanks
I am using android beacon library to detect beacons.I have created a service which implements Bootstrap Notifier and It got two methods :
#Override
public void didEnterRegion(Region arg0) {
Log.i(TAG, "A beacon has enter the region .........");
}
#Override
public void didExitRegion(Region arg0) {
Log.i(TAG, "A beacon has exit the region .........");
}
and also :
mAllBeaconsRegion = new Region("all beacons", Identifier.parse(UUID),null, null);
mBeaconManager = BeaconManager.getInstanceForApplication(this);
new BackgroundPowerSaver(this);
bootstrap = new RegionBootstrap(this, mAllBeaconsRegion);
mBeaconManager.setBackgroundScanPeriod(1000l);
mBeaconManager.setBackgroundBetweenScanPeriod(1000l);
My problem is that in some devices beacon exits the region automatically and didExitRegion called and then re-enters in region and didEnterRegion called.It again disconnects automatically.
This is happening in loop. I am testing it with Lollipop.
How can i get rid of this?
Please help.
It is important to understand what causes a Region exit. A region exit fires when no advertising packets from a beacon matching a Region object are detected in a 10 second period. There are a number of things that can cause this:
Hardware beacons that do not advertise frequently enough. For best performance, beacons should advertise at a rate of 10 Hz or more. Some manufacturers slow down the advertising rate to save battery, making beacons advertise only every 5 seconds or so. This can cause region exits if some of the packets are missed by the receiver due to radio noise or other issues. To solve this problem, configure your beacon to advertise at least once per second (1 Hz.)
Beacons being on the edge of the radio range of the mobile device. Bluetooth beacons have a varying range of 2-50 meters, depending on the manufacturer, settings, placement and the antenna on the receiving mobile device. If the signal is very weak, detections an be intermittent, causing exits if no packets have been seen in 10 seconds. To fix this, increase the transmitter power of your beacon, or add a software filter to ignore intermittent exit events.
Some mobile devices (e.g. Moto X, Moto G, Nexus 4, Nexus 7) have a hardware flaw where bluetooth and WiFi do not work simultaneously. This can cause the symptoms you describe because it makes beacon detections intermittent. To solve this problem, disable WiFi when looking for beacons on one of these devices.
I am implement the connection between Android and BLE? like Anti-lost or finder , After android phone has connected to the BLE device , phone read the RSSI of BLE device every second.
If the RSSI of BLE device is lower than RSSI threshold , it deem Out of Range. For example: Threshold is -70 , and the current RSSI of device is -80.
When app is deem Out of Range. it send the message to the BLE every 5 second. But it always disconnect after few times. I uses the following code to send the message to the BLE device.
BluetoothGattService HelloService = Gatt.getService(HELLO_SERVICE_UUID);
if(HelloService == null) {
Log.d(TAG, "HelloService not found!");
return;
}
//If the Service is not null , try to get the characteristic.
BluetoothGattCharacteristic Characteristic = HelloService.getCharacteristic(UUID_HELLO_CHARACTERISTIC);
if(Characteristic == null) {
Log.d(TAG, "Characteristic not found!");
return;
}
Gatt.setCharacteristicNotification(Characteristic, true);
Characteristic.setValue(text, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
Gatt.writeCharacteristic(Characteristic);
Log.d(TAG, "StepCount Characteristic End!");
The above code is correct , the BLE can receive the message. But the BLE device will disconnect after few second. It seems do more than one thing in a short time is burden to BLE device.
The question is: How to make the connection more stable between Android and BLE ?.
Some suggestions:
Don't use notifications if you can avoid it. Based on personal experience with some phones in some environments notifications can stop working and appear to cause general instability. Try to do periodic reads instead.
Only do a read or write once you have received a callback to BluetoothGattCallback.onCharacteristicWrite() or BluetoothGattCallback.onCharacteristicRead() for the previous read or write.
More generally, never do two things at once, whether that be scanning, connecting, reading, writing, whatever. You should serialize all operations using a job queue, only popping from that queue when the previous job completes (or fails).
In almost-out-of-range scenarios like you're talking about, operations can take a long time to complete, longer than 5 seconds sometimes. So doing another operation in 5 seconds or less you're effectively "stomping" the previous operation. However, operations can also never return with a callback in these cases, so you do have to implement a timeout. I use 10 seconds. Beyond that, the operation failed.
I'm developing an app to keep scanning certain smart bluetooth LE tags to check when a tag is out of range.
I'm getting one signal from each tag per second when I'm running my code in foreground. But in background (like scanning in a service) this rate keeps decreasing down to 1 signal per 10 seconds!
I'm almost sure that the frequency of the smart tag is the same in both cases, but maybe the Android operating system slows down discovering bluetooth devices due to battery issues when the scan happens in the background.
I'm afraid that this will be not satisfying, especially when you need to detect a bag is being stolen or something critical that forces me to keep the rate at 1 signal per 3 seconds or so.
UPDATE:
For the code I used, first:
private BluetoothAdapter bluetoothLeAdapter;
this for starting scanning:
bluetoothLeAdapter.startLeScan(LeScanCallback);
and this is the callback:
private BluetoothAdapter.LeScanCallback LeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
Log.i("Found: ", device.getName() + " - " + device.getAddress());
}
};
Android 4.3 and 4.4 do not slow down discovery in the background. In the Android Beacon Library, I had to put in extra code to slow down scans to save battery when apps are in the background.
I cannot explain what you say you are seeing. Perhaps you should try the library mentioned above and see if you see the same behavior. It is open source, so you can look at the code, too. It scans the same way as in your snippets in your question.
While running on Nexus 7 with Android 4.4, the onScanResult throws a NullPointerExceptionas as seen in the log below:
03-18 17:59:34.170: D/BluetoothAdapter(5092): onScanResult() - Device=78:4B:08:02:7C:91 RSSI=-77
03-18 17:59:34.170: W/BluetoothAdapter(5092): Unhandled exception: java.lang.NullPointerException
However, other devices have no such problems.
I've found this on googlesource
public void onScanResult(String address, int rssi, byte[] advData) {
if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
// Check null in case the scan has been stopped
synchronized(this) {
if (mLeHandle <= 0) return;
}
try {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter == null) {
Log.d(TAG, "onScanResult, BluetoothAdapter null");
return;
}
mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
}
Which has a number of potential culprits, but of course I cannot set values for these variables.
Any why does this fail for Nexus 7 but not for other devices? Any ideas for workarounds?
Android 4.3, 4.4: BLE filtering in startLeScan(UUIDs, callback) doesn't work for 128-bit UUIDs
It works on Samsung s5, tested with Android 4.4.2 but for some reason, it fails on Nexus. Waiting for this fix from Google stack.
In case you want to search for specific address only, you could use this solution. Basically, you would have to use scanRec[], take an extra effort to parse it and then add device with matching address into a list adapter.
[I know, .... wish that simple API would work ! :P ]
Turning WIFI OFF:
I can confirm too, that turning WIFI OFF makes Bluetooth 4.0 more stable especially on Google Nexus (I have a Nexus 7).
The problem
is that the application I am developing needs both WIFI and continous Bluetooth LE scanning. So turning WIFI OFF was no option for me.
Moreover I have realised is that continous Bluetooth LE scanning can actually kill WIFI connection and make the WIFI adapter unable to re-connect to any WIFI network until BLE scan is ON. (I'm not sure about mobile networks and mobile internet).
This definitely happened on the following devices:
Nexus 7
Motorola Moto G
However BLE scanning with WIFI on seemed pretty stable on:
Samsung S4
HTC One
My workaround
I scan BLE for a short period of time 3-4 seconds then I turn scan OFF for 3-4 seconds. Then ON again.
Obviously I always turn BLE scan OFF when I'm connecting to a BLE device.
When I disconnect from a device I restart BLE (turn adapter OFF and then ON) to reset the stack before starting scan again.
I also reset BLE when discovering services or characteristics fails.
When I get advertisement data from a device that the app should connect to (lets say 500 times without being able to connect - thats about 5-10 seconds of advertising) I reset BLE again.