I am new to Beacon and trying to develop proximity Android app using iBeacon.
I have purchased iBeacon from one of the manufacturer in China so I am trying to develop the proximity app where if I found the beacon in the range it show send some notification to the customer. I have tried Android Beacon Library
but it is not giving any result .
If anybody could help me how can I use it if I am doing something wrong. I have used MonitoringActivity sample code to test .. but nothing worked .
Please help me .
Here is the code
Blockquote
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
ArrayList<BeaconParser> beaconParsers = new ArrayList<BeaconParser>();
beaconParsers.add(new BeaconParser().setBeaconLayout(ALTBEACON_LAYOUT));
Log.d("Scanned count ====",scanRecord.length+"");
String lRawdata="";
BLE ble=new BLE();
temp_flag=0;
//int url_flag=0;
String deviceName=device.getName();
String deviceAddress=device.getAddress();
Log.d("DEVICE NAME",device.toString());
Log.d("Address",deviceAddress);
int startByte = 2;
boolean patternFound = false;
while (startByte <= 5)
{
if ( ((int) scanRecord[startByte + 2] & 0xff) == 0x02 && //Identifies an iBeacon
((int) scanRecord[startByte + 3] & 0xff) == 0x15)
{ //Identifies correct data length
patternFound = true;
break;
}
startByte++;
}
if (patternFound)
{
//Convert to hex String
byte[] uuidBytes = new byte[16];
System.arraycopy(scanRecord, startByte + 4, uuidBytes, 0, 16);
String hexString = bytesToHex(uuidBytes);
//UUID detection
uuid = hexString.substring(0,8) + "-" +
hexString.substring(8,12) + "-" +
hexString.substring(12,16) + "-" +
hexString.substring(16,20) + "-" +
hexString.substring(20,32);
// major
int majorValue = (scanRecord[startByte + 20] & 0xff) * 0x100 + (scanRecord[startByte + 21] & 0xff);
// minor
int minorValue = (scanRecord[startByte + 22] & 0xff) * 0x100 + (scanRecord[startByte + 23] & 0xff);
major = majorValue+"";
minor = minorValue+"";
Log.i("DATA OF DEVICE","UUID: " +uuid + "nmajor: " +major +"nminor" +minor);
}
// if(tcz_flag1==1 || tcz_flag2==1 || tcz_flag3==1) {
ble.setDeviceName(deviceName);
ble.setDeviceAddress(deviceAddress);
ble.setRssi(String.valueOf(rssi));
ble.setUuid(uuid);
ble.setMajor(major);
ble.setMinor(minor);
// ble.setNamespaceid(namespaceid);
// ble.setInstanceid(instanceid);
int flag = 0;
int index = 0;
if (bleArrayList.size() > 0) {
for (BLE b : bleArrayList) {
if (deviceName != "TCZ") {
if (b.getDeviceAddress().equals(ble.getDeviceAddress())) {
flag = 1;
bleArrayList.set(index, ble);
}
index++;
}
}
}
if (flag == 0) {
bleArrayList.add(ble);
}
if (bleAdapter == null) {
bleAdapter = new BLEAdapter(getApplicationContext(), bleArrayList);
listView.setAdapter(bleAdapter);
}
Comparator<BLE> bleArraylistComparator = new Comparator<BLE>() {
#Override
public int compare(BLE lhs, BLE rhs) {
String strRssi = lhs.getRssi();
String strRssi2 = rhs.getRssi();
return strRssi.compareToIgnoreCase(strRssi2);
}
};
Collections.sort(bleArrayList, bleArraylistComparator);
bleAdapter.notifyDataSetChanged();
// }
}
});
}
};
Related
I'm working on a solution that advertises and scans in the iBeacon format using the AltBeacon library. The concern that i have is that the library scans all the devices which is fine but after parsing through the scanned devices it also tracks the advertising devices that are not advertising from my application. Is there anyway to solve this through using the library? If not what could be the alternate solution to this.
It is very important for me to track the advertising beacons that are only advertising from my application.
This is the code is use while advertising in iBeacon format through the AltBeacon library:
BluetoothManager bluetoothManager =
(BluetoothManager) applicationContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
BluetoothLeAdvertiser mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothLeAdvertiser != null) {
beacon = new Beacon.Builder()
.setId1(userId)
.setId2("1")
.setId3("1")
.setManufacturer(0x004C)
.setTxPower(-75)
.setDataFields(Arrays.asList(new Long[]{0l}))
.build();
beaconParser = new BeaconParser()
.setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
beaconTransmitter = new BeaconTransmitter(InventaSdk.getContext(), beaconParser);
beaconTransmitter.setBeacon(beacon);
}
}
Edit:
Parsing Beacon code:
/**
* Construct a Beacon from a Bluetooth LE packet collected by Android's Bluetooth APIs,
* including the raw Bluetooth device info
*
* #param scanData The actual packet bytes
* #param rssi The measured signal strength of the packet
* #param device The Bluetooth device that was detected
* #return An instance of a <code>Beacon</code>
*/
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) {
return fromScanData(scanData, rssi, device, new Beacon());
}
protected Beacon fromScanData(byte[] bytesToProcess, int rssi, BluetoothDevice device, Beacon beacon) {
BleAdvertisement advert = new BleAdvertisement(bytesToProcess);
boolean parseFailed = false;
Pdu pduToParse = null;
int startByte = 0;
ArrayList<Identifier> identifiers = new ArrayList<Identifier>();
ArrayList<Long> dataFields = new ArrayList<Long>();
for (Pdu pdu: advert.getPdus()) {
if (pdu.getType() == Pdu.GATT_SERVICE_UUID_PDU_TYPE ||
pdu.getType() == Pdu.MANUFACTURER_DATA_PDU_TYPE) {
pduToParse = pdu;
LogHelper.d(TAG, "Processing pdu type: "+pdu.getType()+bytesToHex(bytesToProcess)+" with startIndex: "+pdu.getStartIndex()+" endIndex: "+pdu.getEndIndex());
break;
}
else {
LogHelper.d(TAG, "Ignoring pdu type %02X "+ pdu.getType());
}
}
if (pduToParse == null) {
LogHelper.d(TAG, "No PDUs to process in this packet.");
parseFailed = true;
}
else {
byte[] serviceUuidBytes = null;
byte[] typeCodeBytes = longToByteArray(getMatchingBeaconTypeCode(), mMatchingBeaconTypeCodeEndOffset - mMatchingBeaconTypeCodeStartOffset + 1);
if (getServiceUuid() != null) {
serviceUuidBytes = longToByteArray(getServiceUuid(), mServiceUuidEndOffset - mServiceUuidStartOffset + 1, false);
}
startByte = pduToParse.getStartIndex();
boolean patternFound = false;
if (getServiceUuid() == null) {
if (byteArraysMatch(bytesToProcess, startByte + mMatchingBeaconTypeCodeStartOffset, typeCodeBytes)) {
patternFound = true;
}
} else {
if (byteArraysMatch(bytesToProcess, startByte + mServiceUuidStartOffset, serviceUuidBytes) &&
byteArraysMatch(bytesToProcess, startByte + mMatchingBeaconTypeCodeStartOffset, typeCodeBytes)) {
patternFound = true;
}
}
if (patternFound == false) {
// This is not a beacon
if (getServiceUuid() == null) {
LogHelper.d(TAG, "This is not a matching Beacon advertisement. (Was expecting "+byteArrayToString(typeCodeBytes)
+ ".The bytes I see are: "+
bytesToHex(bytesToProcess));
} else {
LogHelper.d(TAG, "This is not a matching Beacon advertisement. Was expecting "+
byteArrayToString(serviceUuidBytes)+
" at offset "+startByte + mServiceUuidStartOffset+"and "+byteArrayToString(typeCodeBytes)+
" at offset "+ startByte + mMatchingBeaconTypeCodeStartOffset + "The bytes I see are: "
+ bytesToHex(bytesToProcess));
}
parseFailed = true;
beacon = null;
} else {
LogHelper.d(TAG, "This is a recognized beacon advertisement -- "+
byteArrayToString(typeCodeBytes)+"seen");
LogHelper.d(TAG, "Bytes are: "+ bytesToHex(bytesToProcess));
}
if (patternFound) {
if (bytesToProcess.length <= startByte+mLayoutSize && mAllowPduOverflow) {
// If the layout size is bigger than this PDU, and we allow overflow. Make sure
// the byte buffer is big enough by zero padding the end so we don't try to read
// outside the byte array of the advertisement
LogHelper.d(TAG, "Expanding buffer because it is too short to parse: "+bytesToProcess.length+", needed: "+(startByte+mLayoutSize));
bytesToProcess = ensureMaxSize(bytesToProcess, startByte+mLayoutSize);
}
for (int i = 0; i < mIdentifierEndOffsets.size(); i++) {
int endIndex = mIdentifierEndOffsets.get(i) + startByte;
if (endIndex > pduToParse.getEndIndex() && mIdentifierVariableLengthFlags.get(i)) {
LogHelper.d(TAG, "Need to truncate identifier by "+(endIndex-pduToParse.getEndIndex()));
// If this is a variable length identifier, we truncate it to the size that
// is available in the packet
int start = mIdentifierStartOffsets.get(i) + startByte;
int end = pduToParse.getEndIndex()+1;
if (end <= start) {
LogHelper.d(TAG, "PDU is too short for identifer. Packet is malformed");
return null;
}
Identifier identifier = Identifier.fromBytes(bytesToProcess, start, end, mIdentifierLittleEndianFlags.get(i));
identifiers.add(identifier);
}
else if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
parseFailed = true;
LogHelper.d(TAG, "Cannot parse identifier "+i+" because PDU is too short. endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex());
}
else {
Identifier identifier = Identifier.fromBytes(bytesToProcess, mIdentifierStartOffsets.get(i) + startByte, endIndex+1, mIdentifierLittleEndianFlags.get(i));
identifiers.add(identifier);
}
}
for (int i = 0; i < mDataEndOffsets.size(); i++) {
int endIndex = mDataEndOffsets.get(i) + startByte;
if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
LogHelper.d(TAG, "Cannot parse data field "+i+" because PDU is too short. endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex()+". Setting value to 0");
dataFields.add(new Long(0l));
}
else {
String dataString = byteArrayToFormattedString(bytesToProcess, mDataStartOffsets.get(i) + startByte, endIndex, mDataLittleEndianFlags.get(i));
dataFields.add(Long.decode(dataString));
}
}
if (mPowerStartOffset != null) {
int endIndex = mPowerEndOffset + startByte;
int txPower = 0;
try {
if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
parseFailed = true;
LogHelper.d(TAG, "Cannot parse power field because PDU is too short. endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex());
}
else {
String powerString = byteArrayToFormattedString(bytesToProcess, mPowerStartOffset + startByte, mPowerEndOffset + startByte, false);
txPower = Integer.parseInt(powerString)+mDBmCorrection;
// make sure it is a signed integer
if (txPower > 127) {
txPower -= 256;
}
beacon.mTxPower = txPower;
}
}
catch (NumberFormatException e1) {
// keep default value
}
catch (NullPointerException e2) {
// keep default value
}
}
}
}
if (parseFailed) {
beacon = null;
}
else {
int beaconTypeCode = 0;
String beaconTypeString = byteArrayToFormattedString(bytesToProcess, mMatchingBeaconTypeCodeStartOffset+startByte, mMatchingBeaconTypeCodeEndOffset+startByte, false);
beaconTypeCode = Integer.parseInt(beaconTypeString);
// TODO: error handling needed on the parse
int manufacturer = 0;
String manufacturerString = byteArrayToFormattedString(bytesToProcess, startByte, startByte+1, true);
manufacturer = Integer.parseInt(manufacturerString);
String macAddress = null;
String name = null;
if (device != null) {
macAddress = device.getAddress();
name = device.getName();
}
beacon.mIdentifiers = identifiers;
beacon.mDataFields = dataFields;
beacon.mRssi = rssi;
beacon.mBeaconTypeCode = beaconTypeCode;
if (mServiceUuid != null) {
beacon.mServiceUuid = (int) mServiceUuid.longValue();
}
else {
beacon.mServiceUuid = -1;
}
beacon.mBluetoothAddress = macAddress;
beacon.mBluetoothName= name;
beacon.mManufacturer = manufacturer;
beacon.mParserIdentifier = mIdentifier;
beacon.mMultiFrameBeacon = extraParsers.size() > 0 || mExtraFrame;
}
return beacon;
}
Scan callbacks:
private ScanCallback getNewLeScanCallback() {
if (leScanCallback == null) {
leScanCallback = new ScanCallback() {
#MainThread
#Override
public void onScanResult(int callbackType, ScanResult scanResult) {
LogHelper.d(TAG, "got record");
List<ParcelUuid> uuids = scanResult.getScanRecord().getServiceUuids();
if (uuids != null) {
for (ParcelUuid uuid : uuids) {
LogHelper.d(TAG, "with service uuid: "+uuid);
}
}
try {
LogHelper.d("ScanRecord", "Raw Data: " + scanResult.toString());
LogHelper.d("ScanRecord", "Device Data Name: " + scanResult.getDevice().getName() + "Rssi: " + scanResult.getRssi() + "Address: " + scanResult.getDevice().getAddress() + "Service uuid: " + scanResult.getScanRecord().getServiceUuids());
}catch (Exception e){
LogHelper.d("ScanRecord",e.getMessage());
e.printStackTrace();
}
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(), scanResult.getScanRecord().getBytes());
if (mBackgroundLScanStartTime > 0) {
LogHelper.d(TAG, "got a filtered scan result in the background.");
}
}
#MainThread
#Override
public void onBatchScanResults(List<ScanResult> results) {
LogHelper.d(TAG, "got batch records");
for (ScanResult scanResult : results) {
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(), scanResult.getScanRecord().getBytes());
}
if (mBackgroundLScanStartTime > 0) {
LogHelper.d(TAG, "got a filtered batch scan result in the background.");
}
}
#MainThread
#Override
public void onScanFailed(int errorCode) {
Intent intent = new Intent("onScanFailed");
intent.putExtra("errorCode", errorCode);
LocalBroadcastManager.getInstance(CycledLeScannerForLollipop.this.mContext).sendBroadcast(intent);
switch (errorCode) {
case SCAN_FAILED_ALREADY_STARTED:
LogHelper.e(TAG, "Scan failed: a BLE scan with the same settings is already started by the app");
break;
case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
LogHelper.e(TAG, "Scan failed: app cannot be registered");
break;
case SCAN_FAILED_FEATURE_UNSUPPORTED:
LogHelper.e(TAG, "Scan failed: power optimized scan feature is not supported");
break;
case SCAN_FAILED_INTERNAL_ERROR:
LogHelper.e(TAG, "Scan failed: internal error");
break;
default:
LogHelper.e(TAG, "Scan failed with unknown error (errorCode=" + errorCode + ")");
break;
}
}
};
}
return leScanCallback;
}
The general approach to filter for “your” beacons is to see an an identifier prefix that is common to all your beacons. You then tell if it is your beacon by filtering on beacons that match this identifier prefix.
Two ways to do the filtering:
A) Software filtering after scan results come in.
With this approach, you wait until you parse the beacons and then use an if statement to see if the beacon identifiers match your prefix. If not, do not process it. The Android Beacon Library has this as a built-in feature by using Region objects to provide matching patterns for “your” beacons.
// replace uuid with your own
beaconManager.startRangingBeaconsInRegion(new Region("matchOnlyMyBeacons", Identifier.parse(“2F234454-CF6D-4A0F-ADF2-F4911BA9”)), null, null));
beaconManager.addRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
// only beacons matching the identifiers in the Region are included here
}
});
Since you are mot using the library as a whole but copying some of its code, you may have to build similar logic yourself like this:
// replace the uuid with yours below
if (beacon.getID1().equals(Identifier.parse(“2F234454-CF6D-4A0F-ADF2-F4911BA9”)){
// only process matching beacons here
}
This is a simple approach as very flexible. It works well in cases where your app runs only in the foreground or in the background when usually there are few BLE devices around that are not of interest.
The disadvantage is that it can burn cpu and battery if lots of beacons are around that are not of interest.
B) Use hardware scan filters
Android 6+ APIs allow you to put similar matching functions into the Bluetooth chip itself so all scan callbacks you get already match the identifier prefix. This is us less taxing on CPU and battery but has disadvantages:
Not all devices support this, though most devices built since 2018 do.
Hardware filters are a limited resource. If other apps take all of them up, you will not get scan results.
Filters are inflexible. If even a single byte of the advertisement prefix doesn’t match (commonly due to a differing manufacturer code) you will not get a scan result.
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setServiceUuid(null);
byte[] filterBytes = new byte[]{
/* 0215 are the start of iBeacon. Use beac for AltBeacon */
(byte) 0x02, (byte) 0x15,
// These bytes are your 16 byte proximityUUID (ID1)
(byte) 0x2F, (byte) 0x23, (byte) 0x44, (byte) 0x54, (byte) 0xCF, (byte) 0x6D, (byte) 0x4A, (byte) 0x0F, (byte) 0xAD, (byte) 0xF2, (byte) 0xF4, (byte) 0x91, (byte) 0x1B, (byte) 0xA9, (byte) 0xFF, (byte) 0xA6
};
byte[] maskBytes = new byte[]{
/* Make this the same length as your filter bytes, and set every value to 0xff to match all bytes */
(byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff
};
builder.setManufacturerData((int) 0x004c /* apple for iBeacon, use 0x0118 for AltBeacon */, filterBytes, maskBytes);
ScanFilter[] scanFilters = new ScanFilter[] { builder.build() };
scanner.startScan(scanFilters, scanSettings, scanCallback);
I am trying to develop an Android App that connects to my NRF51822 based system using BLE. The aim is to write a 3 byte value (RGB) to a my custom characteristic.
Android is the GATT client and NRF51 based device is GATT server.
I am able to establish the ble connection and discover my characteristic successfully.
However the data sending part (setValue) is giving me troubles. No matter what 3 bytes I write, I get the same constant data on the NRF51 side
Below is my rellevant code (Android)
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d("BLENotifier", "BLE GAT : SERVICES DISCOVERED");
for(BluetoothGattService gs: gatt.getServices()) {
Log.d("BLENotifier", "SERVICE = " + gs.getUuid().toString());
}
//SELECT MY CHARACTERSTIC
ble_my_characterstic = gatt.getService(ble_service_uuid).getCharacteristic(ble_characterstic_uuid);
Log.d("BLENotifier", "BLE SELECTED CHARACTERSTIC " + ble_my_characterstic.getUuid().toString());
ble_connected = true;
}
public void writedata(String data){
//WRITE DATA TO MY CHARACTERSTIC
if(ble_my_characterstic != null && ble_connected == true){
my_gatt_handle.executeReliableWrite();
//ble_my_characterstic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
ble_my_characterstic.setValue(hexStringToByteArray(data));
my_gatt_handle.writeCharacteristic(ble_my_characterstic);
Log.d("BLENotifier", "BLE WRITE DATA " + data);
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
Log.d("BLENotifier", "hexStringToByteArray " + Integer.toString((int)data[0]) + " " + Integer.toString((int)data[1]) + " " + Integer.toString((int)data[2]));
return data;
}
I am invoking the writeData method as ble_handle.writedata("0000FF")
This is what I get on the NRF51 side
R = 4 | G = 239 | B= 1
Thanks
I did like this.Not exact answer but may help you.
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
List<BluetoothGattService> services = gatt.getServices();
Log.i("onServicesDiscovered", services.toString());
keepConnect=services.get(3).getCharacteristics().get(0);
if(keepConnect!=null){
writeCharacteristic(gatt,keepConnect);
}
}
private void writeCharacteristic(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
byte[] byteData = hexToBytes("6fd362e40ebcd0945bf58dc4");
byte[] writeData = new byte[byteData.length];
for (int i = 0; i < byteData.length; i++) {
writeData[i] = byteData[i];
}
characteristic.setValue(writeData);
gatt.writeCharacteristic(characteristic);
}
public static byte[] hexToBytes(String hexRepresentation) {
int len = hexRepresentation.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexRepresentation.charAt(i), 16) << 4)
+ Character.digit(hexRepresentation.charAt(i + 1), 16));
}
return data;
}
I'm unable to get UUID of a iBeacon on pre-Lollipop devices, but below my code is working in Marshmallow Nexus 5x.
I dont want to use any library like AltBeacon or related.
BluetoothManager bm = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bm.getAdapter();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
//Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
// scan for devices
scanner.startScan(new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
// get the discovered device as you wish
// this will trigger each time a new device is found
BluetoothDevice device = result.getDevice();
if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE) { //int the device type DEVICE_TYPE_CLASSIC, DEVICE_TYPE_LE DEVICE_TYPE_DUAL. DEVICE_TYPE_UNKNOWN if it's not available
if (device.fetchUuidsWithSdp()) {
System.out.println(device.getName());
List<ParcelUuid> uuids = result.getScanRecord().getServiceUuids();
System.out.println(uuids);
System.out.println(getMajor(result.getScanRecord().getBytes()));
System.out.println(getMinor(result.getScanRecord().getBytes()));
} else {
System.out.println("failed");
}
}
}
});
}
BluetoothManager bm = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bm.getAdapter();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
//Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
// scan for devices
scanner.startScan(new ScanCallback() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public void onScanResult(int callbackType, ScanResult result) {
// get the discovered device as you wish
// this will trigger each time a new device is found
BluetoothDevice device = result.getDevice();
if (device.getType() == BluetoothDevice.DEVICE_TYPE_LE)//int the device type DEVICE_TYPE_CLASSIC, DEVICE_TYPE_LE DEVICE_TYPE_DUAL. DEVICE_TYPE_UNKNOWN if it's not available
{
if (device.fetchUuidsWithSdp()) {
System.out.println(device.getName());
List<ParcelUuid> uuids = result.getScanRecord().getServiceUuids();
System.out.println(uuids);
System.out.println(getMajor(result.getScanRecord().getBytes()));
System.out.println(getMinor(result.getScanRecord().getBytes()));
} else {
System.out.println("failed");
}
}
}
});
} else {
// targetting kitkat or bellow
mBluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
// get the discovered device as you wish\
}
});
}
}
public void onLeScan(BluetoothDevice device, int rssi, final byte[] scanRecord) {
int startByte = 2;
boolean patternFound = false;
while (startByte <= 5) {
if ( ((int) scanRecord[startByte + 2] & 0xff) == 0x02 && //Identifies an iBeacon
((int) scanRecord[startByte + 3] & 0xff) == 0x15) { //Identifies correct data length
patternFound = true;
break;
}
startByte++;
}
if (patternFound) {
//Convert to hex String
byte[] uuidBytes = new byte[16];
System.arraycopy(scanRecord, startByte+4, uuidBytes, 0, 16);
String hexString = bytesToHex(uuidBytes);
//Here is your UUID
String uuid = hexString.substring(0,8) + "-" +
hexString.substring(8,12) + "-" +
hexString.substring(12,16) + "-" +
hexString.substring(16,20) + "-" +
hexString.substring(20,32);
//Here is your Major value
int major = (scanRecord[startByte+20] & 0xff) * 0x100 + (scanRecord[startByte+21] & 0xff);
//Here is your Minor value
int minor = (scanRecord[startByte+22] & 0xff) * 0x100 + (scanRecord[startByte+23] & 0xff);
System.out.println(String.format("%s\n%s\n%s",uuid,major,minor));
textView.setText(String.format("%s\n%s\n%s", uuid,major,minor));
}
}
/**
* bytesToHex method
* Found on the internet
* http://stackoverflow.com/a/9855338
*/
static final char[] hexArray = "0123456789ABCDEF".toCharArray();
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
Call the method like this and Enjoy :)
onLeScan(device,result.getRssi(),result.getScanRecord().getBytes());
I've been looking around and unfortunately the android ibeacon library has been deprecated, so I am attempting to do this native. I have implemented the BluetoothAdapter.LeScanCallback and the built in onLeScan() method that will fire when a device is picked up. I would like to read in that device's ProximityUUID, major and minor characteristics and identifier. I'm not sure how to get that information out of the Android object BluetoothDevice.
How do I extract that information (ProximityUUID, major, minor, & identifier characteristics) from the Android BluetoothDevice, or is there another way to do it?
Thanks!
you can refer this post to fully understand what those bytes means in LeScanCallback .
And this is my code to parse all information needed:
// an object with all information embedded from LeScanCallback data
public class ScannedBleDevice implements Serializable {
// public BluetoothDevice BLEDevice;
/**
* Returns the hardware address of this BluetoothDevice.
* <p>
* For example, "00:11:22:AA:BB:CC".
*
* #return Bluetooth hardware address as string
*/
public String MacAddress;
public String DeviceName;
public double RSSI;
public double Distance;
public byte[] CompanyId;
public byte[] IbeaconProximityUUID;
public byte[] Major;
public byte[] Minor;
public byte Tx;
public long ScannedTime;
}
// use this method to parse those bytes and turn to an object which defined proceeding.
// the uuidMatcher works as a UUID filter, put null if you want parse any BLE advertising data around.
private ScannedBleDevice ParseRawScanRecord(BluetoothDevice device,
int rssi, byte[] advertisedData, byte[] uuidMatcher) {
try {
ScannedBleDevice parsedObj = new ScannedBleDevice();
// parsedObj.BLEDevice = device;
parsedObj.DeviceName = device.getName();
parsedObj.MacAddress = device.getAddress();
parsedObj.RSSI = rssi;
List<UUID> uuids = new ArrayList<UUID>();
int skippedByteCount = advertisedData[0];
int magicStartIndex = skippedByteCount + 1;
int magicEndIndex = magicStartIndex
+ advertisedData[magicStartIndex] + 1;
ArrayList<Byte> magic = new ArrayList<Byte>();
for (int i = magicStartIndex; i < magicEndIndex; i++) {
magic.add(advertisedData[i]);
}
byte[] companyId = new byte[2];
companyId[0] = magic.get(2);
companyId[1] = magic.get(3);
parsedObj.CompanyId = companyId;
byte[] ibeaconProximityUUID = new byte[16];
for (int i = 0; i < 16; i++) {
ibeaconProximityUUID[i] = magic.get(i + 6);
}
if (uuidMatcher != null) {
if (ibeaconProximityUUID.length != uuidMatcher.length) {
Log.e(LOG_TAG,
"Scanned UUID: "
+ Util.BytesToHexString(
ibeaconProximityUUID, " ")
+ " filtered by UUID Matcher "
+ Util.BytesToHexString(uuidMatcher, " ")
+ " with length requirment.");
return null;
}
for (int i = 0; i < 16; i++) {
if (ibeaconProximityUUID[i] != uuidMatcher[i]) {
Log.e(LOG_TAG,
"Scanned UUID: "
+ Util.BytesToHexString(
ibeaconProximityUUID, " ")
+ " filtered by UUID Matcher "
+ Util.BytesToHexString(uuidMatcher,
" "));
return null;
}
}
}
parsedObj.IbeaconProximityUUID = ibeaconProximityUUID;
byte[] major = new byte[2];
major[0] = magic.get(22);
major[1] = magic.get(23);
parsedObj.Major = major;
byte[] minor = new byte[2];
minor[0] = magic.get(24);
minor[1] = magic.get(25);
parsedObj.Minor = minor;
byte tx = 0;
tx = magic.get(26);
parsedObj.Tx = tx;
parsedObj.ScannedTime = new Date().getTime();
return parsedObj;
} catch (Exception ex) {
Log.e(LOG_TAG, "skip one unknow format data...");
// Log.e(LOG_TAG,
// "Exception in ParseRawScanRecord with advertisedData: "
// + Util.BytesToHexString(advertisedData, " ")
// + ", detail: " + ex.getMessage());
return null;
}
}
Payloads of advertising packets should be parsed as a list of AD structures.
iBeacon is a kind of AD structures.
See "iBeacon as a kind of AD structures" for details. Also, see an answer to a similar question.
i'm developing an android application uses bluetooth LE, i need to get RR interval. I start form this example on android developer site:
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data
// parsing is carried out as per profile specifications.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
stringBuilder.toString());
}
}
sendBroadcast(intent);
}
How i can get RR interval? I just do it with iOS, but on java i don't know how to do..
this is my code on iOS, and works perfectly:
- (void) updateWithHRMData:(NSData *)datas {
const uint8_t *reportData = [datas bytes];
uint16_t bpm = 0;
uint16_t bpm2 = 0;
if ((reportData[0] & 0x04) == 0)
{
NSLog(#"%#", #"Data are not present");
}
else
{
bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[2]));
bpm2 = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[4]));
if (bpm != 0 || bpm2 != 0) {
self.deviceReady = true;
[lblNofascia setAlpha:0.0];
[btnMonitor setAlpha:1.0];
if (isRunning) {
[self.elencoBattiti addObject:[NSString stringWithFormat:#"%u", bpm]];
NSLog(#"%u", bpm);
if (bpm2 != 0) {
[self.elencoBattiti addObject:[NSString stringWithFormat:#"%u", bpm2]];
NSLog(#"%u", bpm2);
}
}
} else {
if (isRunning) {
totErrori++;
NSLog(#"Dato non trasmesso");
if (totErrori > 5) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Attenzione" message:#"Ho perso la connettività con la fascia. Ripetere la misurazione" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Continua", nil];
[alert show];
[self stopRunning];
}
}
}
}
}
thank you
I have the following.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
int format = -1;
int energy = -1;
int offset = 1;
int rr_count = 0;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Logger.trace("Heart rate format UINT16.");
offset = 3;
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Logger.trace("Heart rate format UINT8.");
offset = 2;
}
final int heartRate = characteristic.getIntValue(format, 1);
Logger.trace("Received heart rate: {}", heartRate);
if ((flag & 0x08) != 0) {
// calories present
energy = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset);
offset += 2;
Logger.trace("Received energy: {}", energy);
}
if ((flag & 0x10) != 0){
// RR stuff.
rr_count = ((characteristic.getValue()).length - offset) / 2;
for (int i = 0; i < rr_count; i++){
mRr_values[i] = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset);
offset += 2;
Logger.trace("Received RR: {}", mRr_values[i]);
}
}
Thanks Ifor. With some fixes the android code should work. The bit mask for RR data flag should be 16 according to specs and the array should be declared:
if ((flag & 0x16) != 0){
// RR stuff.
rr_count = ((characteristic.getValue()).length - offset) / 2;
Integer[] mRr_values = new Integer[rr_count];
for (int i = 0; i < rr_count; i++){
mRr_values[i] = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset);
offset += 2;
Logger.trace("Received RR: {}", mRr_values[i]);
}
}