I'm new to android development and I'm writing a APP as proof of concept for a research project using the BLE Google API. I have two android phones, one of which is advertising some beacon X that carries a scan response Y, while the second phone is scanning for advertisements. I would like to access the RSSI values corresponding to the ScanRequest and ScanReply packets that follow the advertisement.
During device to device communication, when the overwritten onScanCallback method is called on the second phone, I can only obtain a single RSSI value by calling result.getRssi() on the passed ScanResult. Furthermore, as expected, the corresponding ScanRecord provides two ServiceUuids by calling the record.getServiceUuids, where the first UUID corresponds to the advertisement itself, and the second one to the programmed scan reply.
Until now I haven't found any public methods to recover the RSSI of the ScanReply, is there some other way of accessing this second RSSI? And the same applies to the RSSI of the ScanRequest that is automatically sent by the listening device, is there a way of recovering it?
Thanks a lot in advance,
Ivan Morales
The RSSI in the scan callback already corresponds to the RSSI in the scan response packet, not the advertising packet. Android's Bluetooth stack throws away the RSSI value for the first packet. Why do you need it anyway? It should be more or less the same as the first packet since both packets are sent within such a short interval.
You can't get RSSI for a scan request. This info is not sent from controller to host if you read the HCI standard. Even if the Bluetooth stack would listen to "LE Scan Request Received event", that event doesn't include RSSI.
Related
The documentation around ScanSettings is very laconic and I'm having a hard time coming up with an optimal configurations.
My requirements dictate I should report a new advertisement packet that matches my filter criteria (filtered on service UUIDs) as soon as possible. Duplicate advertisement packets are of no use to me. The advertisement packets will change when the peripheral will register an event on its side and the advertisement packet is thus an opportunity for me to detect changes and act accordingly. I also need to know when the peripheral stopped advertising (powered off or not in range anymore).
I've tried to set the callback type like following:
setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST)
The documentation for this parameters says:
int CALLBACK_TYPE_ALL_MATCHES
Trigger a callback for every Bluetooth advertisement found that matches the filter criteria. If no filter is active, all advertisement packets are reported.
int CALLBACK_TYPE_FIRST_MATCH
A result callback is only triggered for the first advertisement packet received that matches the filter criteria.
int CALLBACK_TYPE_MATCH_LOST
Receive a callback when advertisements are no longer received from a device that has been previously reported by a first match callback.
I was hoping with this to get notified when the peripheral becomes available and then get notified when the peripheral is lost. However this doesn't trigger any scan results, at least not on my Pixel 2. So I just switched to use CALLBACK_TYPE_ALL_MATCHES and now I get all the advertisement packets. This is probably what I want anyways if I want to get updated packets from the same peripheral.
I looked at the setMatchMode parameters and it appears that this only configures the callbacks rate based on the signal strength:
int MATCH_MODE_AGGRESSIVE
In Aggressive mode, hw will determine a match sooner even with feeble signal strength and few number of sightings/match in a duration.
int MATCH_MODE_STICKY
For sticky mode, higher threshold of signal strength and sightings is required before reporting by hw
The setNumOfMatches claims to handle the number of advertisements per filter, but none of the documented parameters seem to make a difference, I still get a burst of packets from the same peripheral that didn't change, seemingly at about the same rate no matter the parameter.
To meet the part of my requirement where I need to determine if the peripheral stopped advertising (powered off or not in range) I came up with a timestamp of last seen packet from the peripheral and determine if the peripheral is lost or not when I don't see a packet for x amount of seconds. This was necessary because the CALLBACK_TYPE_FIRST_MATCH and CALLBACK_TYPE_MATCH_LOST don't seem to work at all.
This unfortunately means I get bursts of similar packets triggering a chain reaction in my code that I now have to deal with.
How can I configure the scanning so that I get notified only when the advertisement packet changes or the peripheral stopped advertising? Am I missing something here, or that's just not possible.
I've recently had to implement reacting to a device that stops advertising (updating a list of available BLE devices). The majority of my testing was also done on the Pixel 2.
I'm not sure you can receive a callback upon advertisement package change for a peripheral AND receive a callback when a peripheral stops advertising, although I have not tested this.
To receive a callback when the device stops advertising and stop the bombardment of callbacks for every packet, use :
Kotlin:
private val bleScanSettings =
ScanSettings.Builder().setCallbackType((ScanSettings.CALLBACK_TYPE_FIRST_MATCH or ScanSettings.CALLBACK_TYPE_MATCH_LOST)).build()
Java:
ScanSettings bleScanSettings = new ScanSettings().Builder.setCallbackType((ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST)).build();
My code is in Kotlin, the Java code is what I think it should be and looks identical to yours. This configuration will trigger an error callback when scanning without a ScanFilter, could that be your original issue?
This configuration will trigger a callback upon receiving the first matching advertisement packet for a device as well as when a matched device stops advertising.
You could try to see if the matching callback is also triggered when the advertisement packet for a (previously matched) device changes. Otherwise, it seems impossible.
Set code like this:
new ScanSettings.Builder ()
.setScanMode (ScanSettings.SCAN_MODE_BALANCED)
.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build (),...
This setting works on all devices. But each smartphone gives you a different error and this causes confusion.
In the application I am looking forward Bluetooth in Beacon is very good option as I want to collect PH from different sources to the application so I can't pair my android device with all the BLE devices at a time as it will be around 20-30.
But I can see that if there is Beacon then Android can scan all of them and also get the RSSI of all devices without being paired with them.
So is it possible that we add few other parameter for example PH, Temperature, Humidity and 3-4 other parameters so Beacon is going to broadcast all these parameter along with RSSI and in Android app I can collect all the information?
I am not sure if this is feasible solution or not and if it is then how to achieve this in beacon?
Bluetooth beacons generally rely upon advertisement packets to send data, which are limited in the number of bytes available. For manufacturer advertisements, you basically have 24 usable bytes to work with, although you need to reserve some of these as a flag to indicate it is your beacon format, and not somebody else's beacon format.
You can look at the AltBeacon spec as an example. This format uses two bytes to identify itself (the "beacon code"), 20 bytes of beacon identifiers, one byte of data and one byte for reference RSSI. You probably still want a unique identifier for each beacon so you know which beacon sent you the information. But you might be able to cut this down to four bytes for your purposes, which would allow you to have 2^32 different beacons sending this information.
The Android Beacon Library lets you both transmit and receive beacons using arbitrary formats you can define using the BeaconParser class. A beacon format that uses a four byte identifier, two bytes each for PH, Temperature and Humidity data fields, and two bytes each for five other data fields might look like this:
m:2-3=abcd,i:4-7,d:8-9,d:10-11,d:12-13,d:14-15,d:16-17,d:18-19,d:20-21,d:22-23,p:24-24
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;
Recently I got a iBeacon device with the intention of creating Android Apps than can recognize it and use it. I'm new using bluetooth in Android Apps and there many things than I still don't know. Looking in the Internet and in this forum I found recommendations to use the Radius Networks' Android IBeacon Library but, alas, now it's no longer available:
https://github.com/RadiusNetworks/android-ibeacon-service
So I started by using the code shown in Android Developers' guide about Bluetooth Low Energy:
https://developer.android.com/guide/topics/connectivity/bluetooth-le.html
Using this code I can detect the device, even connect to it, but I don't know how to get the Proximity Uuid and the Major and Minor values: the app shows a lot of Uuids from services and characteristics of the device, but none is the Proximity Uuid of the device.
Anyone could tell me how to get that data using the Android Bluetooth LE API, or help me to get the Radius Networks' Android iBeacon library for Eclipse and a guide to use it or sample showing how to use it?
Thank you.
EDIT/UPDATE:
It gets better.. AltBeacon !
Check the AltBeacon specifications
AltBeacon brings greater transparency to what a beacon transmits and
how that data can be used by Android, Windows and other devices.
Why the Android iBeacon Library by RadiusNetworks is no longer available:
Vendors have started complying with guidelines set by Apple and have,
as a result, been forced to ‘scrub’ their products of any references
or connection between Android devices and their detection of iBeacon
protocols.
Read more:
Apple cracks down on iBeacon for Android
I think the best bet seems to be from somebody who has been using it, already has it, can share it with you, as from previous commits, you may not get every component - library, sample, service
Also: A note from the CEO for Android iBeacon Lib, RadiusNetworks
Now, coming to Proximity UUID and major, minors:
I have not found a direct way to get it, in terms of a parameter, though you can have a look at read major, minor, uuid of beacons in android and SensorTag using iBeacon Technology. In the latter, there is an indication of major, minor, uuid after iBeacon Service, however TI instruments might be the restriction.
In android, as an identifier.. you can recover the device addressby device.getAddress() of the beacon/for each BluetoothDevice device;.
The following two are totally different things, although both are called UUID.
UUIDs of GATT services which are hosted on a BLE peripheral device.
Proximity UUID of iBeacon.
What you should know about "UUIDs of GATT services":
BLE peripheral devices may implement a GATT server.
A GATT server hosts GATT services.
What the API "android.bluetooth.BluetoothGatt.getServices()" returns is a list of GATT services (List<BluetoothGattService>).
BluetoothGattService.getUuid() returns the ID of the service.
What you should know about "Proximity UUID of iBeacon":
BLE peripheral devices broadcast advertising packets.
The payload part of an advertising packet contains a list of AD structures.
An AD structure consists of (1) Length (1 byte), (2) AD Type (1 byte) and (3) AD Data. The AD structure format is described in "11 ADVERTISING AND SCAN RESPONSE DATA FORMAT" of "Bluetooth Core Specification 4.2".
iBeacon is a kind of AD structures.
AD Type of iBeacon is 0xFF (which means Manufacturer Specific Data).
The first 4 bytes of AD Data of iBeacon are 0x4C, 0x00, 0x02 and 0x15. The first 2 bytes (0x4C, 0x00) mean "Apple, Inc." and the next 2 bytes (0x02, 0x15) mean "iBeacon format".
Proximity UUID (16 bytes), major number (2 bytes in big endian), minor number (2 bytes in big endian), and power (1 byte) follow the first 4 bytes.
So, what you have to do to get iBeacon information (Proximity UUID, major, minor, power) are as follows.
Parse a payload of an advertising packets as a list of AD structures.
For each AD structure, check if AD Type is 0xFF and the first 4 bytes of AD Data are 0x4C, 0x00, 0x02 and 0x15.
When the conditions of 2. are satisfied, parse the remaining bytes as Proximity UUID, major number, minor number, and power.
If you use nv-bluetooth, you can extract iBeacon from an advertising packet like the following:
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
// Parse the payload of the advertising packet.
List<ADStructure> structures =
ADPayloadParser.getInstance().parse(scanRecord);
// For each AD structure contained in the advertising packet.
for (ADStructure structure : structures)
{
if (structure instanceof IBeacon)
{
// iBeacon was found.
IBeacon iBeacon = (IBeacon)structure;
// Proximity UUID, major number, minor number and power.
UUID uuid = iBeacon.getUUID();
int major = iBeacon.getMajor();
int minor = iBeacon.getMinor();
int power = iBeacon.getPower();
........
See "iBeacon as a kind of AD structures" for details.
RadiusNetworks has "re-released" the libraries and examples in conjunction with a new cross platform beacon proximity spec.
New AltBeacon Standard Allows Cross-Platform Proximity Apps
Check out altbeacon.org.