What I need is to get a list of every devices in my range so I can call a certain method on them one after another.
What I have tried so far is to use the startDiscovery() method of the BluetoothAdapter and while it returns a list of devices it takes about 12 seconds to complete which is a lot for my task to complete.
I also managed to user the LeScanCallback succesfully every 2-3 seconds and I do discover devices, but on every scan it returns only one device at a time.
I'm trying to search for Beacons so this type of method could return every call a single other device that uses BlutoothLE when it's found ,while not returning my Beacon's info at all.
So, is there a way to discover a list of all devices in a range without waiting for the startDiscovery() to finish after 12 seconds ?
A few points:
startDiscovery is intended for bluetooth classic devices, not Bluetooth LE.
Android does not contain any APIs specific to parsing beacons, only for dealing with raw Bluetooth LE devices.
You can use the free and open source Android Beacon Library add-on to detect and parse beacons. It does not have a 12 second delay and wraps the LeScanCallback method and combines the results so you can get callbacks every second like:
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon : beacons) {
Log.i(TAG, "I see a beacon:"+beacon+" that is "+beacon.getDistance()+" meters away.");
}
}
You need to call startScan(ScanCallback) api of BluetoothLeScanner to start scanning, this has support on Android 21(5.0).
Also look below question, it has sample code you can use to start/stop Le Scanning.
How to restart BluetoothLeScanner?
Related
I created an android (java) application that uses the altbeacon library (github page) to detect beacons via the Bluetooth module.
The Beacons are configured with Eddystone UID, protocol with an advertising interval of 100ms and transmit power level of strong (10dBm).
What I would like to be able to do is to detect the RSSI value of the beacons with a frequency of 10Hz (i.e. 100ms).
I've already prepared a service that implements the altbeacon library, the relevant part are showed below:
mBeaconManager = BeaconManager.getInstanceForApplication(this);
beaconRegion = new Region("beacon_region", null, null, null);
// use Eddystone protocol
mBeaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT));
mBeaconManager.setAndroidLScanningDisabled(true);
mBeaconManager.setForegroundScanPeriod(100l); // scan frequency
mBeaconManager.setForegroundBetweenScanPeriod(0);
and the callback didRangeBeaconsInRegion from which I see the beacons and get the RSSI value is this:
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
Log.i(BeaconDataCollectorService.class.getSimpleName(), String.format("Found %s beacons in range", beacons.size()));
}
What's my problem?
Running the app, I notice that within didRangeBeaconsInRegion, I often don't see all the beacons.
I ran the data collection for a few seconds, creating a csv format of the data obtained, where you can see that I often have 0s (indicating that the beacon was not detected); I put the sharing of this csv on pastebin (https://pastebin.com/zkUZC5R4)
How can I improve the scan frequency by always being able to detect all the beacons?
altbeacon version used:
implementation 'org.altbeacon:android-beacon-library:2.16.3'
Android version: 9
Thanks
Unfortunately, the library is not designed to do this, as the ranging APIs are modeled after iOS equivalents which give aggregate detections at fixed intervals as opposed to access to individual packets when they arrive.
The main reason you often get 0 beacons detected with such a short scan period is because the scan is turned on and off every 100ms, and it takes 10ms or so to start and stop. This gives a good probability of missing the detection.
Here's the good news: If the library ever detects two beacon packets in the same scan period then it will stop turning the scan off and your detection rate will improve. But getting it to detect two beacons in 100ms is nearly impossible of your beacon advertises at a nominal 10Hz.
One thing you might try is to start by setting the scan period to 1 second. After you have detected a finite number of your beacons for 10 secs or so, there is a very good chance you will have detected two beacons in one scan cycle and then you can switch to a scan period of 100ms and get a higher detection rate.
You will never get a 100% detection rate for two reasons:
Not all beacon packets transmitted are received due to collisions and radio noise. At close range 80-90% is typical.
"BLE advertisers do not advertise at regular intervals. They randomize when their packets are sent to avoid collisions. A nominal 10Hz transmitter might have anywhere between 70ms and 140ms between individual packets, so for fixed scan cycles of 100ms sometimes there will be 0, 1 or 2 Packers eligible to be received.
If you really need to get callbacks on every detection, you might try setting up a NonBeaconBleScanCallback in the BeaconManager, then calling BeaconManager.clearBeaconParsers(). This will cause all beacons detections to be sent to that callback immediately when detected. You will then have to construct a new BeaconParser for use inside that callback to decode the beacon from the raw packet. Use beaconParser.parse(...)
I use android phone to detect a beacon using startScan method, the interval time of beacon adversing is 800ms, but I got the beacon data from callback of startScan every 3~10 seconds. My expectation is I can get the data every 1 second.
Below is my codes. So is there any other ways to get my expectation. Or someone can explain why the interval time is not the same as the adversing time ?
ScanSettings.Builder builder = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
// scan filters has been set the right value.
bluetoothLeScanner.startScan(scanFilters, builder.build(), scanCallback);
Nothing about the code shown is a problem. I can confirm your expectation is met with equivalent code in other apps on other devices. This suggests a problem with :
Your BLE device not advertising as often as you expect
Your Android phone (possible but unlikely)
Some other aspect of your code that you did not show in your question.
Is you BLE device a beacon? If so, I suggest:
Install BeaconScope on the same phone, scan for your beacon, then check the PPS rating (Packets Per Second -- scan for at least 30 seconds until the measurement stabilizes). If you see a value of 0.1-0.3 then the problem is NOT with your code and must be (1) or (2). If you see a much higher PPS value, the problem is with some other aspect of your code not shown.
Use a second Android phone with BeaconScope as a transmitter and transmit a beacon at the 10 Hz default rate. Does your app get a packet every 100-200ms? If so, you have confirmed the problem is with your BLE device not transmitting as much as you expect.
If your BLE device is not a beacon you can at least perform the second test.
It's recommended to use 'SCAN_MODE_LOW_LATENCY' mode only hen the application is running in the foreground.
Incase you are in background, you can run a sticky foreground service and set a periodic frequency of 1 sec for your service.
Also scanning Ble for every 1 sec might be extremely heavy for the app.
I am developing an Android app to record the RSSI fluctuations from a BLE beacon. I use the startLeScan(BluetoothAdapter.LeScanCallback callback) method and from the callback get the RSSI value every time the beacon is discovered (till I have some MAX_NUM values). Then I call stopLeScan().
However, I also want to know whenever the app performs a scan but is unable to discover the beacon during this period. As I understand, this will result in the onLeScan() callback not being invoked. How can I do this?
I'm writing an BLE application, where need to track if peripherals device is advertising or has stop.
I followed getting peripherals without duplications this and BLE Filtering behaviour of startLeScan() and I completely agree over here.
To make it feasible I kept timer which re-scan for peripherals after certain time (3 sec). But with new device available on market(with 5.0 update), some time re-scan take bit time to find peripherals.
Any suggestion or if anyone have achieved this?
Sounds like you're interested in scanning advertisements rather than connecting to devices. This is the "observer" role in Bluetooth Low Evergy, and corresponds to the "broadcaster" role more commonly known as a Beacon. (Bluetooth Core 4.1 Vol 1 Part A Section 6.2)
Typically you enable passive scanning, looking for ADV_IND packets broadcast by beacons. These may or may not contain a UUID. Alternatively, you can active scan by transmitting SCAN_REQ to which you may receive a SCAN_RSP. Many devices use different advertising content in ADV_IND and SCAN_RSP to increase the amount of information that can be broadcast - you could, for instance, fit a UUID128 into the ADV_IND followed by the Device Name in the SCAN_RSP. (Bluetooth Core 4.1 Vol 2 Part E Section 7.8.10)
Now you need to define "go away" - are you expecting the advertisements to stop or to fade away? You will get a Receive Signal Strength Indication "RSSI" with each advertisement (Bluetooth Core 4.1 Vol 2 Part E Section 7.7.65.2) - this is how iBeacon positioning works and there's plenty of support for beacon receivers in Android.
Alternatively you wait for N seconds for an advertisement that should be transmitted every T seconds where N>2T. The downside of the timed approach is that probably not receiving a beacon isn't the same as definitely receiving a weak beacon; to be sure you need N to be large and that impacts the latency between the broadcaster being switched off or moving out of range and your app detecting it.
One more thing - watch out that Advertising stops if something connects to a Peripheral (if you really are scanning for peripherals) another good reason to monitor RSSI.
First scenario: Bonded Devices
We know that if a bond is made, then most of the commercially available devices send directed advertisements in during re-connection. In situations such as this, according to BLE 4.0 specification, you cannot scan these devices on any BLE sniffer.
Second scenario: Connectable Devices
Peripheral devices are usually in this mode when they are initially in the reset phase. The central sends a connect initiator in response to an advertisement packet. This scenario offers you a lot of flexibility since you can play around with two predominant configuration options to alter connection time. These are: slavelatency on the peripheral and conninterval on the central. Now, I don't know how much effort it's going to take get it working on the Android platform, but if you use the Bluez BLE stack and a configurable peripheral such as a TI Sensor tag, then you can play around with these values.
Third scenario: Beacon devices
Since this is what your question revolves around, according to the BLE architecture, there are no parameters to play with. In this scenario, the central is just a dumb device left at the mercy of when a peripheral chooses to send it's beaconing signal.
Reference:
http://www.amazon.com/Inside-Bluetooth-Communications-Sensing-Library/dp/1608075796/ref=pd_bxgy_14_img_z
http://www.amazon.com/Bluetooth-Low-Energy-Developers-Handbook/dp/013288836X/ref=pd_bxgy_14_img_y
Edit: I forgot, have you tried setting the advertiser to non-connectable? That way you should be able to get duplicate scan results
I am dealing with a similar issue, that is, reliably track the RSSI values of multiple advertising devices over time.
It is sad, the most reliable way i found is not nice, rather dirty and battery consuming. It seems due to the number of android devices that handle BLE differently the most reliable.
I start LE scan, as soon as i get a callback i set a flag to stop and start scan again. That way you work around that DUPLICATE_PACKET filter issue since it resets whenever you start a fresh scan.
The ScanResults i dump into a sqlite db wich i shrink and evaluate once every x seconds.
It should be easy to adapt the shrinking to your use case, i.e. removing entries that are older than X, and then query for existance of a device to find out if you received a ScanResult in the last X seconds. However dont put that X value too low, as you must take into account that you still lose alot of advertisement packets on android LE scan, compared to a BLE scan on i.e. bluez..
Edit:
I can add some information i already found for speeding up the performance on Advertisement discovery. It involves modifying and compiling the bluedroid sources and root access to the device. Easiest would be building a full android yourself, i.e. Cyanogenmod.
When a LE scan is running, the bluetooth module sends the scan sesponse via HCI to the bluedroid stack. There various checks are done until it finally gets handed to the Java onScanResult(...) which is accessed via JNI.
By comparing the log of the hci data sent from the bluetooth module (can be enabled in /etc/bluetooth/bt_stack.conf) with debug output in the bluedroid stack aswell as the Java side i noticed that alot of advertisement packets are discarded, especially in some check. i dont really understand, beside that it has something to do with the bluedroid inquiry database
From the documentation of ScanResult we see that the ScanRecord includes the advertisement data plus the scan response data. So it might be that android blocks the report until it got the scan response data/ until it is clear there is no scan response data. This i could not verify, however a possibility.
As i am only interested in rapid updates on the RSSI of those packets, i simply commented that check out. It seems that way every single packet i get from the bluetooth moduly by hci is handed through to the Java side.
In file btm_ble_gap.c in function BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p)
comment out to_report = FALSE; in the following check starting on line 2265.
/* active scan, always wait until get scan_rsp to report the result */
if ((btm_cb.ble_ctr_cb.inq_var.scan_type == BTM_BLE_SCAN_MODE_ACTI &&
(evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_DISCOVER_EVT)))
{
BTM_TRACE_DEBUG("btm_ble_update_inq_result scan_rsp=false, to_report=false,\
scan_type_active=%d", btm_cb.ble_ctr_cb.inq_var.scan_type);
p_i->scan_rsp = FALSE;
// to_report = FALSE; // to_report is initialized as TRUE, so we basically leave it to report it anyways.
}
else
p_i->scan_rsp = TRUE;
Does anyone know if the collection given by the interface RangeNotifier is ordered by approximate distance from the device, with the closest beacons at the beginning of the array like in iOS?
didRangeBeaconsInRegion(java.util.Collection iBeacons, Region region)
Thanks
No, the list is ordered by the time when the iBeacon Bluetooth LE advertisement was detected at the Android device. This may be inconsistent between each scan cycle (i.e. each call to the RangeNotifier), so you probably want to sort the list yourself if you want a specific order.