AltBeacons with Android

I was reading my good friends Juhana’s Blog about BLE BEACONS ON WINDOWS 10 and was wondering on how would I do these beacons with Android.

Juhana did his BLE work alongside my another friend Tomi, thus I of-course did check Tomi’s Blog, and got his example for Advertising & scanning for Altbeacons from his WORKING WITH CONSPICUOUS DEVICES article. This example implementation was then used later on to check that it can see my android advertisements in right way, as well as that my android implementation would see the windows device Altbeacons  as well as could decode the scan record right way.

Juhana had nice illustrator showing the advertisement package used with AltBeacons, and it looks like this:

AltBeaconScanRecord

Juhana also mentioned in his article that “DataType value should be a constant 0xFF as specified in the AltBeacon protocol specification”, and after seeing this I went to check what the Generic Access Profile says for the type 0xFF.

As the 0xFF type means ‘Manufacturer Specific Data’, and it’s the first type on the package, we simply need to get an advertisement scan record, which only has the manufacturer segment and we are done.

With the BluetoothLeAdvertiser API, to take out any unnecessary data from the scan record, we need to:

  • With AdvertiseSettings call setConnectable with false. This makes the beacon not being connectable, also it makes sure the flags part in scan record is not added.
  • With AdvertiseData call both setIncludeDeviceName and setIncludeTxPowerLevel with false value to make sure they are not included in the scan record either

The to set the data for the scan record, we can use the AdvertiseData::addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) function. Note that the manufacturer id is encoded to the scan record automatically by the API, and it takes 2 bytes. Rest of the data is supplied by byte buffer and its written directly to the scan record, thus you can use your own encoding inside it.

Following picture illustrated how the data is written into the scan record. The Length & Type fields are set by the API and are both taking 1 byte. The manufacturerId is set from the value you supplied with addManufacturerData call and it takes 2 bytes. The green part was also supplied alongside the call for addManufacturerData function, and it can be up to 27 bytes long (31-1-1-2).

AltBeaconScanRecordAndroid

I did also quick search for any library that could be used with altbeacons and did find some. In general they looked a bit too big for the simple task needed here, and I wanted to have code that is really minimalistic.  Also wanted to have codebase that could easily be modified to support different data schemes used for the 27 byte payload.

In general, only codes that would be needed here, are involved on encoding & decoding the scan record data accordingly.

The resulting example is called AltBeacon  and you can find it from github under my BLETestStuff repo 

I implemented both encoding & decoding in the AltBeaconFactory.

Generally the code is rather strait forward, for example the encoding part simply puts the variables stored in AltBeacon  class instance into a byte array according to the type and encoding used in the scan record.

public static  byte[]  getAdvertDataFromBeacon(AltBeacon beacon) {
    byte[] ret = new byte[24];
    //beaconCode
    int beaconCode = Integer.decode("0x"+ beacon.getBeaconCode());
    ret[0] = (byte)((beaconCode >> 8) & 0xff);
    ret[1] = (byte)(beaconCode & 0xff);
    // id1 2 - 17
    String tmpString = beacon.getId1().replaceAll("[\\s\\-]", "");
    int index = 2;
    for(int i = 2; i <= tmpString.length(); i = i + 2){
        if(i <= 32){
            ret[index] = (byte) (Integer.decode("0x" + tmpString.substring((i - 2), i)) & 0xff);
            index++;
        }
    }
    //id2
    int id2 = beacon.getId2();
    ret[18] = (byte)((id2 >> 8) & 0xff);
    ret[19] = (byte)(id2 & 0xff);
    //id3
    int id3 = beacon.getId3();
    ret[20] = (byte)((id3 >> 8) & 0xff);
    ret[21] = (byte)(id3 & 0xff);
    //refRSSI
    ret[22] = (byte)beacon.getRefRSSI();
    ret[23] = (byte)beacon.getManufacturerReserved();
    return ret;
}

The BeaconScanner is implemented in a way that it is using BluetoothLeScanner with devices having Lollipop or never, and with older devices its using the BluetoothAdapter::startLeScan() API.

One notable issue I run into with Marshmallow was the new security scheme google implemented with it. With BLE beacons, when the code is run with device shaving the Android 6.0 (i.e. Marshmallow) you are required to add ACCESS_COARSE_LOCATION into then manifest file.

On top of having the permission defined in the manifest, you also need to ask users permission for the capability. For implementation details check the MainActivity  class implementation.

The code was tested with API level 18 (Jellybean), 19 (KitKat), 21(Lollipop) as well as 23(Marshmallow) devices. Here’s video showing how the app runs on these different devices:

The distance shown on the screen is of course a bit off because I did not have any good way on getting really good value for the measured power sent with the scan record.

But the video does show clearly the fluctuations on the distance calculated, and this is caused by the measured Rssi, thus to get the distance fluctuation out, we should use some kind of averaging the Rssi over time, instead of the last measured value alone.

Thirdly, it can be seen that the values are not really accurate, thus the measured distance should not be shown as meters, instead we should use zoning.

Good example of the zoning can be seen with splitting the distance into three zones with iBeacons:

  • Immediate: Within a few centimeters
  • Near: Within a couple of meters
  • Far: Greater than 10 meters away