Saturday, January 2, 2016

Finding iBeacons without ProximityUUID Xamarin Android

            The iBeacon is the small Low Energy Bluetooth device which constantly broadcasts the radio signals to phones and tablets using Bluetooth. Mobile devices can listen to these signals and trigger an action. User's mobile device identifies the concrete iBeacon via it’s ProximityUUID  which is the unique hexadecimal number. ProximityUUID is essential to get the radio signals from the iBeacon. 

I was developing my first iBeacon app on iOS, I was unable to connect the iBeacon and finally came to conclusion that, the ProximityUUID which I was using was, unfortunately incorrect. Despite the fact that I was checking the ProximityUUID with various iOS apps such as Locate and eBeacon, the ProximityUUID still seemed incorrect. After a day research and posting the question to stackoverflow, I was suggested to use Android app called Locate to find the correct ProximityUUID of the iBeacon. The android Locate app worked like a charm, it gave the correct UUID and finally I connected. The thing what I have understood is that iOS restricts users to read iBeacons identifiers unless you know the ProximityUUID.

This experience has urged me to search and  write the post to help other developers solve the problem. In this post I will explain how we can find the UUID-s of nearby beacons with Xamarin.Android and monitor or range it.

First of all add the permissions of  Bluetooth and BluetoothAdmin in your manifest file.

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />    

We are going to use the BluetoothAdapter class for bluetooth connection. First implement the BluetoothAdapter.ILeScanCallback for your activity, create the instance of BluetoothAdapter class in your OnCreate method and start scanning.

    adapter = BluetoothAdapter.DefaultAdapter;
    adapter.StartLeScan (this);

Than implement the OnLeScan method in your activity. Here we will have a list of beacons, which will finally hold the result of the nearby iBeacons.
   
private List<iBeacon> _beacons;
public void OnLeScan (BluetoothDevice device, int rssi, byte[] scanRecord)
          { }

The main part starts here, the parameter scanRecord which is the array of bytes is holding all information about the nearable bluetooth device. The OnLeScan method is called whenever your app receives the signal from any bluetooth device, it may include bluetooth peripherals, iBeacons, eddystones and etc. Here we are trying to find the pattern, whether the device which is sending the signal is iBeacon or not.

Since the OnLeScan is called whenever your app receives the bluetooth signal, we can add a timer which will handle how many seconds will the scanning be running.

timer = new Timer ();
timer.Elapsed += Timer_Elapsed;
             timer.Interval = 1000;

If we want to identify the iBeacon, we are going to skip  2 – 5 bytes since they are providing the information of vendor company. We should check next bytes:
  • 0x02 means iBeacon
  • 0x15 is the data length
All these allows us to be assured that this device is the iBeacon.
Next step is to take 16 byte length part from the source array(scanRecord for our case), this number is the ProximityUUID of the iBeacon.
    The same is for Minor and Major values, but the length of both is 2 bytes, we should multiply first byte by 100 and add the second byte to find the Major value and the same technique is used for the Minor value.

    You can read more about the bytes information here and here.

Let’s have a look on OnLeScan method.
public void OnLeScan (BluetoothDevice device, int rssi, byte[] scanRecord)
       {
//checking the beacon not to be duplicated
if (!_beacons.Any (x => x.MacAddress == device.Address)) {
               int startByte = 2;
               var patternFound = false;
               while (startByte <= 5) {
                   if (((int)scanRecord [startByte + 2] & 0xff) == 0x02 &&
                       ((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];
                   Array.Copy (scanRecord, startByte + 4, uuidBytes, 0, 16);
                   var hexString = BitConverter.ToString (uuidBytes).Replace ("-", "");

                   //Here is your UUID
                   var uuid = hexString.Substring (0, 8) + "-" +
                              hexString.Substring (8, 4) + "-" +
                              hexString.Substring (12, 4) + "-" +
                              hexString.Substring (16, 4) + "-" +
                              hexString.Substring (20, 12);

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

                   if (!_beacons.Any (b => b.UUID == uuid && b.Major == major && b.Minor == minor))
                       _beacons.Add (new iBeacon () {
                           UUID = uuid,
                           Minor = Convert.ToUInt16 (minor),
                           Major = Convert.ToUInt16 (major),
                           Name = device.Name,
                           MacAddress = device.Address
                       });
               }
}

       After 10 seconds the timer finishes, and we are holding the result in the variable called _beacons, all nearby iBeacons with their ProximityUUID, Minor and Major values.

private void Timer_Elapsed (object sender, ElapsedEventArgs e)
       {
           elapsedSeconds++;
           if (elapsedSeconds > 10) {
               timer.Stop ();
               adapter.StopLeScan (this);
               elapsedSeconds = 0;
               var myResult = _beacons;
           }
       }

The sample project can be seen on my github, check it out:

Finding beacons without UUID

Using soft hyphens in Swift 3.0

There can be cases when we will need to support hyphens in the iOS app. But it can be crucial thing to detect at which place we should put h...