I want to transmit iBluetooth over android with the BluetoothLeAdvertiser class from android.bluetooth.le. But it seems I can't set some of the Manufacturer spec data fields to match those of the iBeacon.
iBeacon structure:
This and this tell you what an iBeacon's Manufacturer spec data values need to contain. Basically:
Apple Company Identifier (Little Endian), 0x004c = 76
data type, 0x02 => iBeacon
data length, 0x15 = 21
uuid: 585CDE931B0142CC9A1325009BEDC65E
major: [0000]
minor: 0000
meaured power at 1 meter: 0xc5 = -59
Create the AdvertiseData object using AdvertiseData.Builder.addManufacturerData() (which I believe should be the Manufacturer spec data, see below for the issues with that)
protected void setAdvertiseData() {
AdvertiseData.Builder mBuilder = new AdvertiseData.Builder();
ByteBuffer mManufacturerData = ByteBuffer.allocate(24);
byte[] uuid = getIdAsByte(UUID.fromString("0CF052C2-97CA-407C-84F8-B62AAC4E9020"));
mManufacturerData.put(0, (byte)0xBE); // Beacon Identifier
mManufacturerData.put(1, (byte)0xAC); // Beacon Identifier
for (int i=2; i<=17; i++) {
mManufacturerData.put(i, uuid[i-2]); // adding the UUID
}
mManufacturerData.put(18, (byte)0x00); // first byte of Major
mManufacturerData.put(19, (byte)0x09); // second byte of Major
mManufacturerData.put(20, (byte)0x00); // first minor
mManufacturerData.put(21, (byte)0x06); // second minor
mManufacturerData.put(22, (byte)0xB5); // txPower
mBuilder.addManufacturerData(224, mManufacturerData.array()); // using google's company ID
mAdvertiseData = mBuilder.build();
}
Create AdvertiseSettings using the AdvertiseSettings.Builder
protected void setAdvertiseSettings() {
AdvertiseSettings.Builder mBuilder = new AdvertiseSettings.Builder();
mBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
mBuilder.setConnectable(false);
mBuilder.setTimeout(0);
mBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
mAdvertiseSettings = mBuilder.build();
}
Broadcast the AdvertiseSettings and AdvertiseData using BluetoothLeAdvertiser. (This also takes a callBack that tells if Bluetooth transmission worked, no problems there)
mBluetoothLeAdvertiser.startAdvertising(mAdvertiseSettings, mAdvertiseData, mAdvertiseCallback);
The problem with these steps are:
It's not clear where AdvertiseData.Builder.addManufacturerData() is putting its second Byte[] manufacturerSpecificData parameter in terms of the Manufacturer spec data, especially because the first parameter of AdvertiseData.Builder.addManufacturerData() is also the Manufacturer Type field in the Manufacturer spec data
The following BluetoothLeAdvertiser constructor parameters seem to write bytes to the Manufacturer spec data, overwriting the Bytes added in addManufacturerData
other resources:
updated code
public AdvertiseData getiBeaconData() {
AdvertiseData.Builder mBuilder = new AdvertiseData.Builder();
ByteBuffer mManufacturerData = ByteBuffer.allocate(24);
byte[] uuid = getIdAsByte(UUID.fromString(thisCont.getString( R.string.ble_uuid )) );
ParcelUuid parced = new ParcelUuid(UUID.fromString(thisCont.getString( R.string.ble_uuid )) );
/*
R.string.ble_uuid == CDB7950D-73F1-4D4D-8E47-C090502DBD63 this is about the iBeacon structure http://smlie-blog.blogspot.com/2014/06/bluetooth-ibeacon-packet-format.html */
//mManufacturerData.put((byte)0x00); //Manufactor Id 1
//mManufacturerData.put((byte)0x4C); //Manufactor Id 2
// mManufacturerData.put((byte)0xBE); //ibeconid1
mManufacturerData.put((byte)0x02); //ibeconid2
mManufacturerData.put((byte)0x15); //Data length
for (byte i:uuid) {
mManufacturerData.put(i); // adding the UUID
}
mManufacturerData.put((byte)0x00); //major first
mManufacturerData.put((byte)0x00); //major second
mManufacturerData.put((byte)0x00); //minor first
mManufacturerData.put((byte)0x00); //minor second
//mManufacturerData.put((byte)0xC5); //TX power
/*
mManufacturerData.put((byte)0x1A); // Beacon Identifier was BE iBeacon = 00
mManufacturerData.put((byte)0xFF); // Beacon Identifier was AC iBeacon = 02
/* the above 2 lines are the ibeacon AD Indicator
for (byte i:uuid) {
mManufacturerData.put(i); // adding the UUID
}
mManufacturerData.put((byte)0x00); // first byte of Major
mManufacturerData.put((byte)0x00); // second byte of Major
mManufacturerData.put((byte)0x00); // first minor
mManufacturerData.put((byte)0x00); // second minor
mManufacturerData.put((byte)0xC5); // txPower (RSSI) (from tx)
mBuilder.addManufacturerData(16, mManufacturerData.array()); // using google's company ID
*/
mBuilder.addManufacturerData(76, mManufacturerData.array()); // using google's company ID
//mBuilder.addServiceUuid(parced);
return mBuilder.build();
}
I think your code is quite good. Some remarks:
mBuilder.addManufacturerData(76, mManufacturerData.array())
mManufacturerData.put(0, (byte)0x02); // Beacon Identifier
mManufacturerData.put(1, (byte)0x15); // Beacon Identifier
UUID.fromString("0CF052C297CA407C84F8B62AAC4E9020")
. Usually the fromString()
method expects something like: UUID.fromString("F0018B9B-7509-4C31-A905-1A27D39C003D")
to sum up:
protected void setAdvertiseData() {
AdvertiseData.Builder mBuilder = new AdvertiseData.Builder()
ByteBuffer mManufacturerData = ByteBuffer.allocate(23);
byte[] uuid = getIdAsByte(UUID.fromString("0CF052C297CA407C84F8B62AAC4E9020"));
mManufacturerData.put(0, (byte)0x02);
mManufacturerData.put(1, (byte)0x15);
for (int i=2; i<=17; i++) {
mManufacturerData.put(i, uuid[i-2]); // adding the UUID
}
mManufacturerData.put(18, (byte)0x00); // first byte of Major
mManufacturerData.put(19, (byte)0x09); // second byte of Major
mManufacturerData.put(20, (byte)0x00); // first minor
mManufacturerData.put(21, (byte)0x06); // second minor
mManufacturerData.put(22, (byte)0xB5); // txPower
mBuilder.addManufacturerData(76, mManufacturerData.array());
mAdvertiseData = mBuilder.build();
}