Tracking LTE Beacon’s outdoor position with built-in GPS

Most Estimote Beacons rarely see the world outside their indoor locations.

The LTE Beacon is a brave new one! Thanks to the built-in GPS, it can step outside, and easily find its place in the open world thanks to the dozens of satellites tirelessly orbiting the Earth.

Let’s try to build a micro-app like this: upon a button press, it’ll start the GPS location tracking. Then, as it receives location updates from the GPS module, it’ll keep saving them to memory. When it reaches a pre-defined destination point, it’ll stop the GPS tracking and upload the history of location updates to Estimote Cloud. You can then, for example, pull it out via Estimote Cloud API, and plot the beacon’s journey on a map.

(If you haven’t yet, it might be helpful to read about Syncing with Cloud first, so that you’re familiar with the concepts of “events,” “event queue,” “sync,” the difference between a periodic sync and a forced sync, etc.)

What’s ahead (aka Table of Contents)

Initial configuration

The top-level code in the micro-app is executed just once, when the app is starting, so it’s a perfect spot to do some initial configuration:

var DESTINATION = {lat: 50.0618971, long: 19.9367559};

var MAX_SYNC_PERIOD = 7776000; // 90 days
sync.setSyncPeriod(MAX_SYNC_PERIOD);

Here, we set the coordinates of our destination point, and we also set the automatic-sync period to the maximum possible value, 90 days. That’s because we don’t need our beacon to sync unless we’re on a trip—so we’ll conserve some battery by default, and override this later.

Tip: You’ll probably want to fire up Google Maps, find some alternative destination near you, and use that instead. You can right-click anywhere on the map and pick “What’s here” to see the latitude and longitude of that point.

Or, you can leave the coordinates as above, and make your way to Kraków’s beautiful main square 😇

Starting location updates

Here’s the outline of the code we’ll use:

io.press(() => {
    location.startUpdates(
        // 1st argument -- callback for location updates
        (position) => {
            // TODO
        },
        // 2nd argument -- options
        {minInterval: 60 /* s */, minDistance: 100 /* m */, timeout: 0 /* s */}
    );
});

First of all, we subscribe to a button press, and in response, we call for the location updates to start.

There’s a couple of arguments that we hand over to the startUpdates function:

  1. A callback function to handle the location updates. We’ll fill this in shortly.

  2. Location update options. Here, we’re saying that:

    • we only need the location updates to happen every 60 seconds
    • we only need a new location update when we move at least 100 meters
    • location updates won’t time out—they will go on indefinitely, until we manually stop them

    These options should help you conserve the battery in your beacon.

Handling location updates

Let’s now tell the micro-app what to do with the location updates:

// 1st argument -- callback for location updates
(position) => {
    // TODO
    // ==> add this line: <==
    cloud.enqueue('location-update', position);
},

That will make sure that every time the position of the beacon changes, we record that as a “location-update” event. We also attach the current position to that event. The position is handed to our callback by the LTE Beacon, and it’s an object which looks somewhat like this:

{ts: 1537333256000, lat: 30.452345, long: 20.23444343, alt: 265, prec: 1.20}

It includes the geographical coordinates, latitude and longitude, but also altitude, a timestamp, and something called dilution of precision, which describes how accurate that data is.

Stopping location updates

There’s one more thing we actually want to do in our location updates callback—the stop condition:

// find this line:
cloud.enqueue('location-update', position);

// add this below:
if (location.distance(position, DESTINATION) < 100) {
    location.stop();
    sync.now();
}

With this new if code, every time we get a location update, we’ll calculate the distance between the beacon’s current position, and our destination. If it’s less than 100 meters, we’ll stop the tracking and immediately force a sync. This will cause all of our queued “location-update” events to make their way to Estimote Cloud.

Adding periodic syncs

Our current app is fully functional now—try it out if you want! Press the button, and the beacon will record its trip, and upload it to Estimote Cloud upon reaching the destination. You can then access it via Device Events API.

But what if you want to track the beacon’s journey as it goes? Sure thing! Let’s modify the app to sync the updates every hour while on a trip.

On button press, right as we’re starting the location updates, let’s change the sync interval from 90 days to 1 hour:

// find this:
io.press(() => {
    location.startUpdates( // ...

// change it to:
io.press(() => {
    sync.setSyncPeriod(3600); // <== new line in between
    location.startUpdates(

Now, even before the trip concludes, the beacon will periodically upload its latest location updates to Estimote Cloud.

And just for correctness, let’s also add some code to reset the sync period back to 90 days when the tracking stops:

// find this:
location.stop();
sync.now();

// change it to:
location.stop();
sync.now();
sync.setSyncPeriod(MAX_SYNC_PERIOD); // <== new line

So, what are the advantages of adding this periodic sync?

  • We can now see the latest movements of our beacon every hour.
  • Since we’re emptying the events queue every hour, there’s less risk running out of memory to store the “location-update” events for very long trips. (See: Syncing with Cloud: memory considerations).

The disadvantages?

  • The beacon will run out of battery sooner.