X

Geertjan's Blog

  • June 16, 2016

Track the Closest Beacon on Android via Cordova

Geertjan Wielenga
Product Manager

I've blogged a series of articles on getting started with beacons on Android via Cordova, starting here.

The tricky bit comes in when you want to keep track of the closest beacon. Here is an approach to doing so, based on a variety of resources on-line.

References:

The code below is a subset of this example:

https://github.com/mikaelkindborg/evo-demos/tree/master/Demos2015/cordova-ibeacon

Code in the JavaScript (i.e., "viewModel") side of a JET module:

define(['ojs/ojcore', 'knockout', 'jquery'],
function (oj, ko, $) {
function IncidentsViewModel() {
var self = this;
var mNearestBeacon = null;
var mNearestBeaconDisplayTimer = null;
self.beaconName = ko.observable();
self.beaconDescription = ko.observable();
self.proximity = ko.observable();
self.accuracy = ko.observable();
self.rssi = ko.observable();
var uuid1 = '0ea75ed4-ce75-4a09-bca8-f0565bae82bb';
var major1 = 5;
var minor1 = 2;
var uuid2 = '0ea75ed4-ce75-4a09-bca8-f0565bae82bb';
var major2 = 5;
var minor2 = 3;
var beacon1 = uuid1 + ":" + major1 + ":" + minor1;
var beacon2 = uuid2 + ":" + major2 + ":" + minor2;
var mRegions =
[
{
id: 'region1',
uuid: uuid1,
major: major1,
minor: minor1
},
{
id: 'region2',
uuid: uuid2,
major: major2,
minor: minor2
}
];
self.handleAttached = function () {
document.addEventListener('deviceready', onDeviceReady);
};
self.handleDetached = function () {
document.removeEventListener('deviceready', onDeviceReady);
clearInterval(mNearestBeaconDisplayTimer);
mNearestBeaconDisplayTimer = null;
};
function onDeviceReady()
{
startRanging();
mNearestBeaconDisplayTimer = setInterval(displayNearestBeacon, 1000);
}
function startRanging()
{
function onDidRangeBeaconsInRegion(result)
{
updateNearestBeacon(result.beacons);
}
var delegate = new cordova.plugins.locationManager.Delegate();
cordova.plugins.locationManager.setDelegate(delegate);
delegate.didRangeBeaconsInRegion = onDidRangeBeaconsInRegion;
startRangingRegions(mRegions);
}
function startRangingRegions(regions)
{
for (var i in regions)
{
startRangingRegion(regions[i]);
}
}
function startRangingRegion(region)
{
var beaconRegion = new cordova.plugins.locationManager.BeaconRegion(
region.id,
region.uuid,
region.major,
region.minor);
cordova.plugins.locationManager.startRangingBeaconsInRegion(beaconRegion)
.fail()
.done();
}
function getBeaconId(beacon)
{
return beacon.uuid + ':' + beacon.major + ':' + beacon.minor;
}
function isSameBeacon(beacon1, beacon2)
{
return getBeaconId(beacon1) === getBeaconId(beacon2);
}
function isNearerThan(beacon1, beacon2)
{
return beacon1.accuracy > 0
&& beacon2.accuracy > 0
&& beacon1.accuracy < beacon2.accuracy;
}
function updateNearestBeacon(beacons)
{
for (var i = 0; i < beacons.length; ++i)
{
var beacon = beacons[i];
if (!mNearestBeacon)
{
mNearestBeacon = beacon;
} else
{
if (isSameBeacon(beacon, mNearestBeacon) ||
isNearerThan(beacon, mNearestBeacon))
{
mNearestBeacon = beacon;
}
}
}
}
function displayNearestBeacon()
{
if (!mNearestBeacon) {
return;
}
var beaconId = getBeaconId(mNearestBeacon);
if (beaconId === beacon1) {
self.beaconName('Rembrandt van Rijn');
self.beaconDescription('Nachtwacht');
} else if (beaconId === beacon2) {
self.beaconName('Vincent van Gogh');
self.beaconDescription('Sunflowers');
}
self.proximity(mNearestBeacon.proximity);
self.accuracy(mNearestBeacon.accuracy);
self.rssi(mNearestBeacon.rssi);
}
}
return new IncidentsViewModel;
}
);

The above enables you to create a simple view, like this:

<h2><span data-bind="text: beaconName"></span></h2>
<h3><span data-bind="text: beaconDescription"></span></h3>
<hr>
<p><b><span data-bind="text: proximity"></span></b></p>
<p>Distance: <b><span data-bind="text: accuracy"></span></b></p>
<p>RSSI: <b><span data-bind="text: rssi"></span></b></p>

The next step is to use Oracle Mobile Cloud Service 2.0 as a beacon registry so that the UUID information does not need to be hardcoded, i.e., so that it can be managed in the Cloud.

The big problem with the code above is that it takes about 5 seconds for my app to detect the closest beacon. Maybe that means I need to finetune the beacons or maybe there's something sub-optimal about the code or maybe the beacons themselves. 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.