Estimote beacons and stickers are able to broadcast multiple packets: iBeacon, Eddystone, Nearable, Estimote Telemetry, and Estimote Location. Android SDK supports interacting with them in the following ways:
- monitoring and ranging for iBeacon packets advertised by beacons.
- scanning for Eddystone, Estimote Telemetry and Estimote Location packets advertised by beacons, and Nearable packets advertised by stickers.
Monitoring can be thought of as a geofence: a virtual barrier, here defined by the range of beacon or a group of beacons. Going in and out of this range triggers events that application can react to.
Ranging enables receiving more comprehensive beacon data: identifiers and proximity estimates of the beacons in range.
To use monitoring and ranging, follow these steps:
- Define beacon regions.
- Create a Beacon Manager object.
- Connect the Beacon Manager to the beacon scanning service maintained by the Estimote SDK.
- Set monitoring and ranging listeners.
- Set scan periods.
- Start monitoring/ranging for the regions defined earlier.
Beacon regions can be used to specify beacons or groups of beacons that should trigger a monitoring event or be included in the list of ranged devices. You can defined a region using beacon identifiers:
- UUID - most commonly represented as a String, e.g.:
"B9407F30-F5F8-466E-AFF9-25556B57FE6D"
- Major - an unsigned short integer, (1–65535)
- Minor - an unsigned short integer, (1–65535)
You can find UUIDs, majors, and minors of your beacons in Estimote Cloud.
Define a Region object like this:
Region mintBeaconRegion = new Region("Mint beacon",
UUID.fromString("B9407F30-F5F8-466E-AFF9-25556B57FE6D"), 24515, 28667);
You don't have to pass all three values—e.g., your region can be based on UUID only (skip other values by passing null):
Region allBeaconsRegion = new Region("Beacons with default Estimote UUID",
UUID.fromString("B9407F30-F5F8-466E-AFF9-25556B57FE6D"), null, null);
The available combinations are: UUID + major + minor, UUID + major, UUID alone, or none of it.
As you might need to reuse the Beacon Manager in many activities or fragments, it's a good practice to store an instance of it in your Application's subclass:
public class MyApplication extends Application {
private BeaconManager beaconManager;
@Override
public void onCreate() {
super.onCreate();
beaconManager = new BeaconManager(getApplicationContext());
}
}
Make sure that you add your Application
subclass to AndroidManifest.xml
:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".MyApplication"> <!-- <=== here -->
Once you create the Beacon Manager object, you need to connect it to the beacon service that is responsible for performing BLE scanning operations. You can simply do it by invoking the connect
method. Pass a ServiceReadyCallback
to receive information when mentioned service is ready to use.
@Override
public void onCreate() {
super.onCreate();
beaconManager = new BeaconManager(getApplicationContext());
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
@Override
public void onServiceReady() {
// Ready to start scanning!
}
});
}
Make sure that the beaconManager
's connection scope is strictly related to the lifecycle of its encompassing application (or activity, or fragment).
Beacon Manager offers a variety of listeners that facilitate detecting beacons.
Monitoring listener enables you to subscribe to monitoring events, such as entering and exiting the range of beacons in a Region.
beaconManager.setMonitoringListener(new BeaconManager.MonitoringListener() {
@Override
public void onEnteredRegion(Region region, List<Beacon> list) {
// ...
}
@Override
public void onExitedRegion(Region region) {
// ...
}
});
Ranging listener enables you to subscribe to a continuous (by default: every second) stream of ranging events, which provides a detailed list of beacons detected in range, their full identifiers, and relative proximity to them. If a beacon goes out of range, it will disappear from the list.
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
@Override
public void onBeaconsDiscovered(Region region, List<Beacon> beacons) {
if (beacons.size() != 0) {
Beacon beacon = beacons.get(0);
// ...
}
}
});
To conserve the host device's battery, Estimote SDK doesn't continuously scan for beacons. Instead, it cycles between periods of scanning and waiting. You can adjust the duration of these two periods with two methods listed below. Bear in mind that these values are only a hint—the internal Bluetooth stack of the device might shorten or extend the scan period.
- monitoring:
setBackgroundScanPeriod(long scanPeriodMillis, long waitTimeMillis)
- ranging:
setForegroundScanPeriod(long scanPeriodMillis, long waitTimeMillis)
For example, to increase responsiveness of monitoring events, you may set the scanPeriod
to 5 seconds and the waitTime
to 10 seconds.
beaconManager.setBackgroundScanPeriod(5000, 10000);
Just keep in mind that shortening the cycle can increase the battery drain.
If not sure what to do, leave these two methods out. The default values were picked carefully to provide a good balance between responsiveness and battery usage.
Once you set monitoring and ranging listeners you can start monitoring and ranging using the regions you defined earlier.
@Override
public void onCreate() {
super.onCreate();
beaconManager = new BeaconManager(getApplicationContext());
beaconManager.setMonitoringListener(/* ... */);
beaconManager.setRangingListener(/* ... */);
// beaconManager.setBackgroundScanPeriod(5000, 10000);
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
@Override
public void onServiceReady() {
// Ready to start scanning!
beaconManager.startMonitoring(allBeaconsRegion);
// beaconManager.startRanging(defaultUUIDRegion);
}
});
}
Since ranging is much more energy-intensive than monitoring, it's a good practice to only do it when you know (thanks to monitoring) that you're in range of some beacons:
private void setMonitoringListener() {
beaconManager.setMonitoringListener(new BeaconManager.MonitoringListener() {
@Override
public void onEnteredRegion(Region region, List<Beacon> list) {
beaconManager.startRanging(region);
}
@Override
public void onExitedRegion(Region region) {
beaconManager.stopRanging(region);
}
});
}
Android SDK supports scanning for devices that broadcast Eddystone, Estimote Telemetry, Estimote Location, and Nearable packets.
Since scanning is similar in nature to ranging, it uses the setForegroundScanPeriod
settings.
Eddystone is an open Bluetooth 4.0 protocol from Google that is designed to support multiple data packet types such as Eddystone-UID or Eddystone-URL. Estimote Beacons are fully compatible with Eddystone.
To start scanning for Eddystone packets:
-
Set an
EddystoneListener
:beaconManager.setEddystoneListener(new BeaconManager.EddystoneListener() { @Override public void onEddystonesFound(List<Eddystone> eddystones) { // ... } });
-
Start scanning for Eddystone devices. Make sure that you store the
scanId
value—you will need it later to stop scanning:public class MyActivity extends Activity { private BeaconManager beaconManager; private String scanId; @Override protected void onCreate() { super.onCreate(); beaconManager = new BeaconManager(this); beaconManager.setEddystoneListener(/* ... */); } @Override protected void onStart() { super.onStart(); beaconManager.connect(new BeaconManager.ServiceReadyCallback() { @Override public void onServiceReady() { scanId = beaconManager.startEddystoneScanning(); } }); } @Override protected void onStop() { super.onStop(); beaconManager.stopEddystoneScanning(scanId); } }
Nearable is the main advertising packet of Estimote Stickers. Apart from the sticker's 16-byte identifier, it includes sensor data (motion, temperature) and sticker's health data (battery voltage, firmware version).
To start scanning for Nearables:
-
Set a
NearableListener
:beaconManager.setNearableListener(new BeaconManager.NearableListener() { @Override public void onNearablesDiscovered(List<Nearable> nearables) { // ... } });
-
Start scanning for Nearable devices. Make sure that you store the
scanId
value—you will need it later to stop scanning:public class MyActivity extends Activity { private BeaconManager beaconManager; private String scanId; @Override protected void onCreate() { super.onCreate(); beaconManager = new BeaconManager(this); beaconManager.setNearableListener(/* ... */); } @Override protected void onStart() { super.onStart(); beaconManager.connect(new BeaconManager.ServiceReadyCallback() { @Override public void onServiceReady() { scanId = beaconManager.startNearableDiscovery(); } }); } @Override protected void onStop() { super.onStop(); beaconManager.stopNearableDiscovery(scanId); } }
To start scanning for Estimote Telemetry packets:
-
Set a
TelemetryListener
:beaconManager.setTelemetryListener(new BeaconManager.TelemetryListener() { @Override public void onTelemetriesFound(List<EstimoteTelemetry> telemetries) { // ... } });
-
Start scanning for Estimote Telemetry packets. Make sure that you store the
scanId
value—you will need it later to stop scanning:
public class MyActivity extends Activity {
private BeaconManager beaconManager;
private String scanId;
@Override
protected void onCreate() {
super.onCreate();
beaconManager = new BeaconManager(this);
beaconManager.setTelemetryListener(/* ... */);
}
@Override
protected void onStart() {
super.onStart();
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
@Override
public void onServiceReady() {
scanId = beaconManager.startTelemetryDiscovery();
}
});
}
@Override
protected void onStop() {
super.onStop();
beaconManager.stopTelemetryDiscovery(scanId);
}
}
To start scanning for Estimote Location packets:
-
Set a
LocationListener
:beaconManager.setLocationListener(new BeaconManager.LocationListener() { @Override public void onLocationsFound(List<EstimoteLocation> locations) { // ... } });
-
Start scanning for Estimote Location packets. Make sure that you store the
scanId
value—you will need it later to stop scanning:public class MyActivity extends Activity { private BeaconManager beaconManager; private String scanId; @Override protected void onCreate() { super.onCreate(); beaconManager = new BeaconManager(this); beaconManager.setLocationListener(/* ... */); } @Override protected void onStart() { super.onStart(); beaconManager.connect(new BeaconManager.ServiceReadyCallback() { @Override public void onServiceReady() { scanId = beaconManager.startLocationDiscovery(); } }); } @Override protected void onStop() { super.onStop(); beaconManager.stopLocationDiscovery(scanId); } }