I have a problem here right now as I'm trying to read the tx-Power from my Onyx iBeacon. I've read multiple suggestions and tried so many code on the internet, but I still can't figure this out.
I'm using android lollipop and tried the iBeacon tutorial from https://github.com/devunwired/accessory-samples/tree/master/BluetoothGatt
for a Bluetooth application which extracts the BLUETOOTH_THERM_SERVICE.
I found out two values for tx-Power from advertisement: Bluetooth LE TX Power reading
0x1804 and its characteristic 0x2A07, so I adapted the Parcel UUID to filter my ScanResults with 0x1804:
public static final ParcelUuid THERM_SERVICE = ParcelUuid.fromString("00001809-0000-1000-8000-00805f9b34fb");
public static final ParcelUuid TX_POWER_SERVICE = ParcelUuid.fromString("00001804-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid ONYX_STANDARD_SERVICE = ParcelUuid.fromString("0000180a-0000-1000-8000-00805f9b34fb");
As described in the tutorial:
byte[] data = record.getServiceData(TX_POWER_SERVICE);
Should store a byte array but it always returns null! The only thing that stores a byte array, but I think it stores the whole record is:
byte[] data = record.getServiceData(ONYX_STANDARD_SERVICE);
Trying to move on and use the data I could get with ONYX_STANDARD_SERVICE I can see a scanResult:
onScanResult() - ScanResult{mDevice=78:A5:04:07:AE:96, mScanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={76=[2, 21, 32, -54, -24, -96, -87, -49, 17, -29, -91, -30, 8, 0, 32, 12, -102, 102, 0, 7, -82, -106, -78]}, mServiceData={0000180a-0000-1000-8000-00805f9b34fb=[120, -91, 4, 7, -82, -106, -78, 32, -54, -24, -96, 0, 7, -82, -106]}, mTxPowerLevel=-2147483648, mDeviceName=OnyxBeacon], mRssi=-63, mTimestampNanos=174691272168825}
I think the important part of my record is:
mServiceData={0000180a-0000-1000-8000-00805f9b34fb=[120, -91, 4, 7, -82, -106, -78, 32, -54, -24, -96, 0, 7, -82, -106]}
How do I extract my txPower value from this ServiceData? I checked on several applications that my tx-power is -78.
By checking the result with ONYX_STANDARD_SERVICE I can get this value with scanResult[6] = -78.
Is this the right way to do it, and if so why?
I also found some interesting examples on this issue where another value is mentioned to read from record:
https://github.com/google/uribeacon/blob/master/android-uribeacon/uribeacon-library/src/main/java/org/uribeacon/scan/compat/ScanRecord.java
If you look in here you'll find the code:
private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
Which one of these (0x1804,0x2A07,0x0A) is the right one?
It would also be nice but not necessary that someone could explain me how the example code from getting THERM_SERVICE works:
private float parseTemp(byte[] serviceData) {
/*
* Temperature data is two bytes, and precision is 0.5degC.
* LSB contains temperature whole number
* MSB contains a bit flag noting if fractional part exists
*/
float temp = (serviceData[0] & 0xFF);
if ((serviceData[1] & 0x80) != 0) {
temp += 0.5f;
}
return temp;
}
Please help me on reading advertisement data and extracting tx-Power.
The transmit (TX) power of the OnyxBeacon, and as of any other BLE device that is iBeacon compatible, can be obtained from the advertising packet. This does not require connecting to the device. Also, the TX power value will vary depending on the power level, as the TX power value is calibrated for each power level of the device. The TX power is the last byte of the advertising payload. More information on the structure of the iBeacon packet can be found here: http://www.havlena.net/en/location-technologies/ibeacons-how-do-they-technically-work/
Here is a sample of how to parse an iBeacon packet and obtain the TX power:
BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice bluetoothDevice, final int rssi, final byte[] scanData) {
if (scanData[7] == 0x02 && scanData[8] == 0x15) { // iBeacon indicator
System.out.println("iBeacon Packet: %s", bytesToHexString(scanData));
UUID uuid = getGuidFromByteArray(Arrays.copyOfRange(scanData, 9, 25));
int major = (scanData[25] & 0xff) * 0x100 + (scanData[26] & 0xff);
int minor = (scanData[27] & 0xff) * 0x100 + (scanData[28] & 0xff);
byte txpw = scanData[29];
System.out.println("iBeacon Major = " + major + " | Minor = " + minor + " TxPw " + (int)txpw + " | UUID = " + uuid.toString());
}
}
};
public static String bytesToHexString(byte[] bytes) {
StringBuilder buffer = new StringBuilder();
for(int i=0; i<bytes.length; i++) {
buffer.append(String.format("%02x", bytes[i]);
}
return buffer.toString();
}
public static UUID getGuidFromByteArray(byte[] bytes)
{
ByteBuffer bb = ByteBuffer.wrap(bytes);
UUID uuid = new UUID(bb.getLong(), bb.getLong());
return uuid;
}
1. How do I extract my txPower value from this ServiceData? I checked on several applications that my tx-power is -78
Well, from practical point of view there are 2 types of advertised data in iBeacon profile.
The first is the Manufacturer data. Thanks to these type you can read what the Proximity UUID, Major, Minor or transmission Power value of your beacon is. This type can be read from any iBeacon's advertising packet regardless of the ibeacon device manufacturer. You can see how the layout of the data looks like and parse it in order to extract the proximity-specific values (you can as well find information about it here at page no 3).
The second one is the Service data. Contrary to the first type this one can be customizable, for example the kontakt.io iBeacon provides its unique ID consisting of 4 ASCII symbols firmware version and the transmission power level (from 0 to 7).
2. By checking the result with ONYX_STANDARD_SERVICE I can get this value with scanResult[6] = -78 Is this the right way to do it, and if so why?
I haven't seen the Onyx Beacon specification but if the tx Power is included in the Service Data then I suggest finding out in which position the byte/bytes describing Tx Power is/are. Your iBeacon provider should know it.
3. How do I extract my txPower value from this ServiceData? I checked on several applications that my tx-power is -78
As aforementioned, you can read calculated transmission power from manufacturer data by default. If, however, Onyx beacon provides any additional information regarding the tx power in service data, then you should get the information from Onyx spec-or-sth-like-that file.
4. By checking the result with ONYX_STANDARD_SERVICE I can get this value with scanResult[6] = -78 Is this the right way to do it, and if so why?
The URI Beacon specification states clearly in which position each piece of information including txPower can be found. I don't think the Onyx Beacon has much in common with the Uri Beacon.
5. It would also be nice but not necessary that someone could explain me how the example code from getting THERM_SERVICE works:
There is nothing difficult here. From the code snippet I assume that in the service data at the position 0 there is a value (ranging from 0 to 255) describing temperature. The next byte specifies decimal position. Is this what you are asking about, btw?
6. Please help me on reading advertisement data and extracting txPower :)
In order to parse any iBeacon-related data here is a simple code returning SparseArray where keys representing data types and byte frames bound with the types. The code works under the assumption that you parse entire byte frame read from the remote IBeacon device.
private static SparseArray<byte[]> extractMetaData(final byte[] scanRecord) {
int index = 0;
final SparseArray<byte[]> map = new SparseArray<byte[]>();
final int scanRecordLength = scanRecord.length;
while (index < scanRecordLength) {
final int length = scanRecord[index++];
if (length == 0) {
break;
}
final int type = Converter.asInt(scanRecord[index]);
if (type == 0) {
break;
}
final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length);
map.put(type, data);
index += length;
}
return map;
}
I suggest diving deeper into the Bluetooth-LE--Android project. I learned valuable information by examining the code. And don't forget to star ofc.
Hope that I clarified your questions at least a bit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With