I'm a french student in ingineering and I am learning Android language by myself. My friends and I have to create an Android app which is based on iBeacon technology. I discovered the AltBeacon library few days ago and I found it awesome but I have some questions to ask on it.
Firstly, you must understand I am a novice in programming, and my questions will be idiots for you. But please I really need help ;)
Android provides a Bluetooth.LE Api and I understood that I can use the method startLeScan() to get a BluetoothDevice.
But if I want to use the AltBeacon library which is the equivalent method who allow us to scan iBeacon devices and get an Beacon object?
Another question, If I use startLeScan() and get a BluetoothDevice, how can I transform it into Beacon in order to use AltBeacon methods ?
I am sorry for my english mistakes, I hope my questions will be understandable. Bye
This is what we use to detect iBeacons and get a beacon object in a Android service using the AltBeacon lib.
Setup the BeaconManager
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.setForegroundScanPeriod(5100);
beaconManager.setForegroundBetweenScanPeriod(2000);
beaconManager.setBackgroundScanPeriod(5100);
beaconManager.setBackgroundBetweenScanPeriod(2000);
//Parse IBeacon structure
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
beaconManager.bind(this);
Start Ranging the beacons
private void startBeaconRangeFinderService() {
beaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, org.altbeacon.beacon.Region region) {
try {
if (beacons.size() > 0) {
for (Beacon b : beacons) {
processYourBeaconInThisMethod(b);
}
}
} catch (Exception ex) {
Log.e(TAG_BEACON_ACTIVITY, "Error was thrown: " + ex.getMessage());
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) {
Log.e(TAG_BEACON_ACTIVITY, "Error was thrown: " + e.getMessage());
}
}
You can easily use the Android Beacon Library to scan for beacons and return results using the "Ranging APIs" as described here:
http://altbeacon.github.io/android-beacon-library/samples.html
If you want to directly call startLeScan() and use library code to convert the results to beacon objects, you can call the following method in the scan callback:
Beacon beacon = beaconParser.fromScanData(scanData, rssi, bluetoothDevice)
However, if using a proprietary beacon format (like from from Apple), you will need to construct a BeaconParser with the proper layout. This is proprietary info, but you can do a Google search to find out the proper way to construct a
BeaconParser for proprietary layouts.
I think nv-bluetooth is the easiest way to extract iBeacon from advertising packets. Below is a sample implementation of onLeScan method.
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.
Related
I am using the Android beacon library with,
version -
compile 'org.altbeacon:android-beacon-library:2.15.1'
I am trying to develop one APK for transmitting multiple beacons from my mobile device.
I need to perform this to test or POC to test, how many beacons a reader can read at a time.
I am using the below code to transmit the BLE messages with Android Beacon Library.
btn_transmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isBluetoothEnabled)
{
try
{
String customUuid = "";
for(int i=0;i<=50;i++)
{
if( i < 10){
customUuid = "99999999-b00"+i+"-4807-b747-9aee23508620";
} else if ( i < 999){
customUuid = "99999999-b0"+i+"-4807-b747-9aee23508620";
}
Thread.sleep(5000);
trasmitClick(customUuid);
beaconTransmitter = null;
}
}
catch(Exception e)
{
Toast.makeText(BeaconTransmitterActivity.this, "Something went wronggg", Toast.LENGTH_LONG).show();
}
}
else
Toast.makeText(BeaconTransmitterActivity.this, "Check your bluetooth connection", Toast.LENGTH_LONG).show();
}
});
Here above I am dynamically trying to create 50 new Id's to transmit the beacons.
Method to createBeacon and transmit its advertisements
public void trasmitClick(String customUuid) {
if (beaconTransmitter == null) {
String major, minor, uuid;
uuid = customUuid;
major = etMajorValue.getText().toString().trim();
minor = etMinorValue.getText().toString().trim();
if (TextUtils.isEmpty(uuid))
uuid = customUuid;
if (TextUtils.isEmpty(major))
major = "8";
if (TextUtils.isEmpty(minor))
minor = "2";
currentType=beaconLayout;
currentuuid=uuid;
currentmajorValue=major;
currentminorValue=minor;
beacon = new Beacon.Builder()
.setId1(uuid)
.setId2(major)
.setId3(minor)
//.setManufacturer(0x0118) // It is for AltBeacon. Change this for other beacon layouts
.setManufacturer(0x004C)
.setTxPower(-59)
//.setDataFields(Arrays.asList(new Long[]{6l, 7l})) // Remove this for beacon layouts without d: fields
.build();
// Change the layout below for other beacon types
beaconParser = new BeaconParser()
.setBeaconLayout(parserLayout[beaconLayout]);
beaconTransmitter = new BeaconTransmitter(getApplicationContext(), beaconParser);
beaconTransmitter.startAdvertising(beacon, new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
}
});
btn_transmit.setText("Stop Advertising");
btn_apply.setEnabled(false);
} else {
beaconTransmitter.startAdvertising();
beaconTransmitter = null;
btn_transmit.setText("Start Advertising");
btn_apply.setEnabled(false);
}
}
I am able to make this code work, but what is the result is I am able to transmit only 4 messages, the rest of the messages are not being visible in the simulator.
I am trying to find if the library has some limitations or I am wrong above.
Well I am novice in Android coding.
Below is the result that I can get in my simulator:
I would like to know how can I transmit 50 messages in one go.
This is most certainly a limitation of the Bluetooth chip on your mobile phone. Different device models have different advertising limits. The Huawei P9 Lite, for example can transmit only one advertisement at a time. The Nexus 5x can advertise 10 or more. It is unlikely that many phone models (if any) support 50 simultaneous advertisements.
There is no way to know the limit programmatically, as the OS provides no API to query this limit -- you just have to try. You can check when you get an error advertising by putting code in the onStartFailure callback.
You might also use the [BeaconScope]((https://play.google.com/store/apps/details?id=com.davidgyoungtech.beaconscanner) app to test this. But remember that transmission limits are device-wide. If one app is advertising a beacon, that takes one advertisement slot away from the next app. And no, there is no way to know if other apps are advertising.
I am implementing BLE in Android Studio. I have connected with the peripheral device ok. In my onServicesDiscovered method I want to analyze the services (and characteristics) and I get something like the following when I print out:
android.bluetooth.BluetoothGattService#41b6dd18
There is 4 services in the list and they all look similar except for the numbers at the end. How can I convert this to useful information. I have seen no reference to this format.
Thanks.
Try to read the uuid from the BluetoothGattService object.
You can find uuid of standard services on Bluetooth SIG website. If the uuid is not there (i.e. custom services), you should read the manual of the peripheral or reach out the peripheral maker.
That depends on what you consider "useful" information.
BLE works mostly like dictionary where you look up long numbers (characteristics) and get binary data, so without prior information about the device you're working on, there is not much you can see when you discover services.
That said, in the BLE docs, there is a method displayGattServices() which puts the discovered services info in an ExpandableListView, and here I changed it to print the UUIDs of services and characteristics to logcat instead.
Besides the UUIDs, you can use getProperties() to find out other characteristic properties such as the format of the characteristic data, or getPermissions() to see whether you can read or write the characteristic.
// Demonstrates how to iterate through the supported GATT
// Services/Characteristics.
private void displayGattServices(List<BluetoothGattService> gattServices) {
final String TAG = "BleServiceInfo";
if (gattServices == null) return;
String uuid;
String unknownServiceString = "Unknown service"
String unknownCharaString = "Unknown characteristic"
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
Log.d(TAG, "Service: " + gattService.getUuid().toString());
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
Log.d(TAG, "\tCharacteristic: " + gattCharacteristic.getUuid().toString());
}
}
}
Call this method from onServicesDisccovered() like this:
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
....
displayGattServices(gatt.getServices());
}
I configure android beacon library to detect Eddystone packets
beaconManager = BeaconManager.getInstanceForApplication(context);
// Detect the main identifier (UID) frame:
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19"));
// Detect the telemetry (TLM) frame:
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("x,s:0-1=feaa,m:2-2=20,d:3-3,d:4-5,d:6-7,d:8-11,d:12-15"));
// Detect the URL frame:
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-21v"));
beaconManager.bind(this);
Beacon in never detected in Android beacon library.
#Override
public void onBeaconServiceConnect() {
beaconManager.addMonitorNotifier(this);
beaconManager.addRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons,
Region region) {
if (beacons.size() > 0) {
Extra.log("Beacons detected", "info");
//Process beacons data...
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region(
"myRangingUniqueId", null, null, null));
} catch (RemoteException e) {
}
}
Test:
If beacon is configured in Eddystone-TML I can detect beacon telemetry data with manufacturer app.
If beacon is configured in Eddystone-TML I canĀ“t detect beacon with library.
If beacon is configured in Eddystone-UID I can detect correctly beacon with library and manufacturer app.
Two things to check to make sure you are not detecting at all:
Make sure onBeaconServiceConnect() gets called. Add a Log.d statement to be sure.
Make sure your app has obtained location permissions if you are testing on Android 6+. See here for more info.
EDIT: For Eddystone-TLM, the library does not provide a separate beacon instance in the ranging callback. The library intead treats this frame type as supplemental to a primary beacon frame like AltBeacon or Eddystone-UID. So it will only provide the info from Eddystone-TLM if another primary beacon frame is also detected coming from the same device.
The way it works is that when a beacon frame like AltBeacon or Eddystone-UID is detected, a Beacon object is created and passed to the ranging callback. When an Eddystone-TLM frame is detected coming from the same MAC address as the a primary beacon frame, the telemetry information is attached to the object of the primary beacon frame. To access this info you call:
// Do we have telemetry data?
if (beacon.getExtraDataFields().size() > 0) {
long telemetryVersion = beacon.getExtraDataFields().get(0);
long batteryMilliVolts = beacon.getExtraDataFields().get(1);
long pduCount = beacon.getExtraDataFields().get(3);
long uptime = beacon.getExtraDataFields().get(4);
Log.d(TAG, "The above beacon is sending telemetry version "+telemetryVersion+
", has been up for : "+uptime+" seconds"+
", has a battery level of "+batteryMilliVolts+" mV"+
", and has transmitted "+pduCount+" advertisements.");
}
I have seen some examples how to get the URL frame of the Eddystone beacon by ranging but not by monitoring
beaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (org.altbeacon.beacon.Beacon beacon: beacons) {
if (beacon.getServiceUuid() == 0xfeaa && beacon.getBeaconTypeCode() == 0x10) {
// This is a Eddystone-URL frame
String url = UrlBeaconUrlCompressor.uncompress(beacon.getId1().toByteArray());
Log.d("Eddystone", "I see a beacon transmitting a url: " + url +
" approximately " + beacon.getDistance() + " meters away.");
}
}
}
});
on didRangeBeaconsInRegion there is a org.altbeacon.beacon.Beacon parameter. But for monitoring the didEnterRegion only has the Region as the parameter.
#Override
public void didEnterRegion(Region region) {
}
So, how do I get the url of the eddysone beacon on monitoring mode ? Is it possible?
You must use ranging APIs to read the actual identifier. While it is possible to use monitoring to detect a Eddystone-URL beacon transmission, because the frame has only a single identifier (the URL), you must monitor based on knowing that URL identifier up front (not very useful), or monitor all identifiers.
In the latter case, this creates the problem of reading the identifier since the monitoring callback only has the region object as you describe.
The solution is to range at the same time as you monitor. The ranging callbacks will give you the full list of beacons detected and give you access to the URLs.
I'm trying to connect to an Arduino Uno via an android app using Bluetooth Low Energy (BLE).
I'm developing on Android Studio, testing with a Samsung Galaxy S4, and with an Android version 5.0.1
I followed this link: http://www.truiton.com/2015/04/android-bluetooth-low-energy-ble-example/
I'm scanning devices and when I found one, I would like to get it's UUID before connecting to it, to make sure that it's the right type of device:
mScanCallback = new ScanCallback() {
#Override
#TargetApi(21)
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice btDevice = result.getDevice();
ParcelUuid[] uuids = btDevice.getUuids(); //<-- this is always null!! :(
Log.d(TAG, ""+btDevice.fetchUuidsWithSdp()); //<-- prints true.
Log.d(TAG, "result : " + result.toString()); //<-- prints a bunch of relevant info that contains the UUID I want to check.
Log.d(TAG, "uuids : " + uuids); //<-- prints null.
/*
for (ParcelUuid u : uuids) {
//Compare with the UUID of my device, and connect if ok.
}
*/
}
However, btDevice.getUuids(); is always returning null with no error...
How can I get the UUID of the scanned device?
A brute force method would be to use regexp with the result.toString() to grab what I want but there must be a better way isn't it?
Thanks
BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
// scan for devices
scanner.startScan(new ScanCallback() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public void onScanResult(int callbackType, ScanResult result)
{
List<ParcelUuid> uuids = result.getScanRecord().getServiceUuids();
}
}
this is worked for me in Android 6.0.1
After hours of searching I find out that getUuid() is not the way to go if you want to retrieve the uuid of an iBeacon.
If that is the case, you need to get it directly from the Scan result result object.
I managed to work by using the answer provided by ADEBAYO OSIPITAN.
BLE obtain uuid encoded in advertising packet
Here is his code snipet:
//For APIs greater than 21, Returns Device UUID
public String getUUID(ScanResult result){
String UUIDx = UUID
.nameUUIDFromBytes(result.getScanRecord().getBytes()).toString();
return UUIDx;
}
From Java DOC:
public ParcelUuid[] getUuids ()
Added in API level 15
Returns the supported features (UUIDs) of the remote device.
This method does not start a service discovery procedure to retrieve the UUIDs from the remote device. Instead, the local cached copy of the service UUIDs are returned.
Use fetchUuidsWithSdp() if fresh UUIDs are desired.
Requires BLUETOOTH.
Returns
the supported features (UUIDs) of the remote device, or null on error
Your ScanResult probably is a error