Android BLE Scanning

Blog Post 2: BLE Scanning

In this series of posts I’m describing the implementation of an Android application that scans for BLE devices and displays the result in a list. The code for the application is here. Below is a screenshot of the application after scanning. In this screenshot the application has found three broadcasting BLE devices.

blescan

If you read my first post, you’ve got an understanding of the preparation code for scanning. Namely, you need the right permissions along with a device that supports BLE and has Bluetooth turned on.

All the code we are discussing lives in GitHub here.

In this post we are going to discuss scanning. Scanning for BLE devices is straightforward. Just get hold of a BluetoothAdapter and call startLeScan().  Here’s the code for startScan().  Notice that when the scan is started we also create a handler to stop the scan after 10 seconds.The 10 second limit is arbitrary. You can scan for longer or even continuously if you would like. But scanning takes energy so we normally limit it when we can.

private void startScan(){
  scanning = true;
  deviceScanList.clear();
  devicesAdapter.notifyDataSetChanged();
  updateViewFromState();
  // stop scanning after 7 seconds
  scanStopHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
      bluetoothAdapter.stopLeScan(leScanCallback);
      scanning = false;
      updateViewFromState();
    }
  }, 10000);

  bluetoothAdapter.startLeScan(leScanCallback);
}

BluetoothAdapter.startLeScan()  has a parameter which is a callback interface. Whenever the scanner detects a device broadcast it will call the callback. My implementation of the callback is below. There are a couple to items to be aware of in the callback. The first is that you may receive multiple callbacks for the same device. Initially, this seems wrong, but let me assure you its right. In addition to the device, the callback contains the RSSI (received signal strength indicator). The RSSI is will be different for each callback. Its even possible to roughy estimate position using the RSSI values. Over time, you might take an average of the RSSI’s to do this. Also, its less likely, but possible, the scanRecord data could change with each broadcast. Consider a beacon thermometer which broadcasts the temperature periodically. The data in the scanRecord could be different for each broadcast.

In my implementation, I’m not particularly concerned with the changing RSSI data or dealing with scanRecord data that changes. I only want to capture the first RSSI value and first scanRecord. So I check to see if this is a duplicate device and ignore if that’s the case. Otherwise I just add it to a list of DeviceScanData objects. My DeviceScanData objects are nothing but a container for the callback delivered info: the device, rssi, and scanRecord. The list is also the backing store for the adapter that the ListView is showing. So after adding the object we tell the adapter to let the ListView know to refresh by calling notifyDataSetChanged();

One thing to be aware of here. There is no guarantee that the callbacks will come in on the UI Thread. In fact most of the time they don’t, although this probably depends upon your particular device.  So be sure to post any UI operations back to the UI thread. Thats the only purpose of the mainThreadHander.post() in this case.

BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback(){
  @Override
  public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {


    if (!contains(deviceScanList, device)){
      DeviceScanData discovered = new DeviceScanData(device, rssi, new Advertisement(scanRecord));
      deviceScanList.add(discovered);
      // update the ui but make sure it happens on the UI thread
      Handler mainThreadHandler = new Handler(ScanActivity.this.getMainLooper());
      mainThreadHandler.post(new Runnable() {
        @Override
        public void run() {
         devicesAdapter.notifyDataSetChanged();
        }
      });
    }
  }
};

The most interesting code of this app is the manufacturing of the ListView items that appear in the ListView. As every Android developer knows, the manufacturing process takes place in getView() of the adapter. My adapter code is shown below. Notice the code in red, data.getAdvertisement(). This method returns an object of the class com.ranchosoftware.ranchobleexample.utility.Advertisement. This class is really one of the most interesting classes in my sample code. It contains the code for parsing the scanRecord data of a BLE broadcast. I recommend that you take a quick look at it. Its quite powerful, but not used very much in this example. It really requires a blog post all its own. My next post will be about it.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
  if (convertView == null){
    convertView = inflater.inflate(R.layout.device_item, null);
  }
  DeviceScanData data = this.getItem(position);
  BluetoothDevice device = data.getDevice();
  TextView nameTextView = (TextView) convertView.findViewById(R.id.tvName);
  // Check all the posibilties for a name
  if (device.getName() != null){
    nameTextView.setText(device.getName());
  } else {
    if (!data.getAdvertisement().getCompleteLocalName().equals("")){
      nameTextView.setText(data.getAdvertisement().getCompleteLocalName());
    } else if (!data.getAdvertisement().getShortenedLocalName().equals("")){
      nameTextView.setText(data.getAdvertisement().getShortenedLocalName());
    } else {
      nameTextView.setText("  - ");
    }
  }

  TextView companyTextView = (TextView) convertView.findViewById(R.id.tvCompanyName);
  TextView beaconTextView = (TextView) convertView.findViewById(R.id.tvBeacon);

  // do we recognize as beacon
  IBeaconAdvertisement beacon = data.getAdvertisement().getBeacon();
  if (beacon != null){
    beaconTextView.setVisibility(View.VISIBLE);
  } else {
    beaconTextView.setVisibility(View.GONE);
  }

  companyTextView.setText(data.getAdvertisement().getCompany());

  return convertView;

}

Thats all for this post. My next post will start getting into depth oncom.ranchosoftware.ranchobleexample.utility.Advertisement and the details of the bytes in a BLE broadcast.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s