Software7

Personal Developer Notebook

Creating a Beacon App for Android in Less Than 10 Minutes from Scratch

 

Before starting to create a Beacon app for Android a few words about Bluetooth.

Bluetooth Low Energy (BLE)

Compared to classic Bluetooth, Bluetooth Low Energy consumes much less power (e.g. 0.01 W instead of 1W), the BLE devices are considerable cheaper to build, but the data transfer rate is much lower (e.g. 0.27 Mbit/s).

Great applications for BLE are scenarios where the power consumption must be low and the data rate is not important, e.g. the communication with small wearables, watches and fitness equipment and so on.

So it was only logical when Apple decided to use BLE as base technology for their small passive transmitters called iBeacons®.

Beacons

Beacons are permanently sending out their unique identifier to notify nearby smartphones of their existence, hence the name beacons. A compatible app running on the smartphone then knows that it is in a close proximity. Depending on the application, this can trigger an action on the smartphone, e.g. displaying some information about nearby items.

Beacons are available in different sizes and shapes and from different vendors, e.g. one being www.beaconinside.com.

Different Beacons from different Vendors

Different Beacons from different Vendors

Bluetooth Smart

Bluetooth Low Energy is also known as Bluetooth Smart. Since Android 4.3 Jelly Bean, Android also supports Bluetooth Smart.

Android Smartphone Requirements:

  • Android > 4.3
  • Bluetooth Smart radio unit

The Android SDK has no built-in support for beacons. But there is a library from Radius Networks that can be used.

Beacon Advertisements

Beacons broadcast in regular intervals (e.g. something between 100ms and 1 second). They send out their ID.

The ID consists of three parts:

  • UUID (organization or company)
  • major (arbitrarily, e.g. specific chain store)
  • minor (e.g. location in store)

Flow Control

First you have to define a Region. A region does not mean a geographically region, it’s rather a certain types of beacons you are interested in.

After defining a monitoring and ranging callback you can start monitoring.

Pseudo Code:

region = new Region("<someUUID>")
addMonitorCallback
    ->(didEnterRegion)
        startRangingBeaconsInRegion
    ->(didExitRegion)
        stopRangingBeaconsInRegion
addRangingCallback
    ->(didRangeBeaconsInRegion)
        evalute beacon info

startMonitoring(region)     

Coarse-grained Distance Estimations

The received signal strength indicator (RSSI) together with the Transmit Power can be used to measure the distance between transmitter and smartphone. The distance is not exactly measured; it’s more a coarse-grained estimation. Therefore often the proximity is divided into_Immediate_ (centimeters), Near (couple of meters) and Far (> 10 meters).

The library averages the distance measurements over 20 seconds in time, therefore you will note a lag, when the distance between beacons and smartphone changes. This might exactly be the behavior you want, since the distance estimations are coarse-grained and you are most likely interested in the information, which one of the beacons is the closest one.

Screencast

The following screencast shows in detail how to create a Beacon app for Android in less than 10 minutes from scratch.

Source Code

Finally here is the source code for this simple example.

package com.software7.beacon.app;

import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import org.altbeacon.beacon.*;

import java.util.Collection;


public class BeaconActivity extends ActionBarActivity implements BeaconConsumer {

    public static final String TAG = "BeaconsEverywhere";
    private BeaconManager beaconManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_beacon);

        beaconManager = BeaconManager.getInstanceForApplication(this);

        beaconManager.getBeaconParsers().add(new BeaconParser()
                .setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));

        beaconManager.bind(this);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        beaconManager.unbind(this);
    }

    @Override
    public void onBeaconServiceConnect() {
        final Region region = new Region("myBeaons", Identifier.parse("<replaceBySomeUIID>"), null, null);

        beaconManager.setMonitorNotifier(new MonitorNotifier() {
            @Override
            public void didEnterRegion(Region region) {
                try {
                    Log.d(TAG, "didEnterRegion");
                    beaconManager.startRangingBeaconsInRegion(region);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void didExitRegion(Region region) {
                try {
                    Log.d(TAG, "didExitRegion");
                    beaconManager.stopRangingBeaconsInRegion(region);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void didDetermineStateForRegion(int i, Region region) {

            }
        });

        beaconManager.setRangeNotifier(new RangeNotifier() {
            @Override
            public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
                for(Beacon oneBeacon : beacons) {
                    Log.d(TAG, "distance: " + oneBeacon.getDistance() + " id:" + oneBeacon.getId1() + "/" + oneBeacon.getId2() + "/" + oneBeacon.getId3());
                }
            }
        });

        try {
            beaconManager.startMonitoringBeaconsInRegion(region);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_beacon, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}


Last Famous Words

I hope that gets you started with your beacon project.

If you found this helpful, share it!