Search code examples
androidbluetooth-lowenergyibeacon-android

How to broadcast iBeacon with the BluetoothLeScanner class?


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: iBeacon data packet

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




I've been following this tutorial that has these steps:

  1. 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();
    }
    
  2. 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();
    }
    
  3. 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:




At first I was following this tutorial, and was transmitting an Alt-Beacon. Perhaps the BluetoothLeAdvertiser class is assuming I'm still transmitting Alt-Beacon which is differently structured than iBeacons.

Alt-Beacon structure:Alt-Beacon Data packet



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();

}

parsed data nrfBeacon raw packet data


Solution

  • I think your code is quite good. Some remarks:

    • Manufacture ID for Apple is 76: mBuilder.addManufacturerData(76, mManufacturerData.array())
    • iBeacon type is 0x02: mManufacturerData.put(0, (byte)0x02); // Beacon Identifier
    • length is 0x15: mManufacturerData.put(1, (byte)0x15); // Beacon Identifier
    • I am not sure if that works: 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();
    }