In our previous blog Getting Hands On With Beacons we discussed a high level overview of our internal R&D Courier project using iBeacons and some of the changes we’ve been making to it.  You may have seen us demo it at the Atlanta Tech Demo on February 12, 2014 but if not this post will break down the technical details starting with how iBeacons actually work.

Overview

Beacons are small, inexpensive Bluetooth low-energy (BLE) transmitters used to determine the proximity of nearby devices. They use the BLE specification focusing on minimal energy usage so that they can run for years on a single battery.  When powered, beacons only do one thing - continuously emit a Bluetooth signal announcing their presence.  

Right now Courier only uses iBeacon technology that conforms to the Apple iBeacon Proximity Specification.  If you’re interested in Android alternatives check out AltBeacon – the open source beacon specification that defers slightly from the iBeacon specification.  Since our code only works with iBeacons let’s look at the pieces that make up its BLE signal. 

iBeacon Data Format

Each BLE packet has a data field containing the iBeacon values.  The image below shows the different pieces of the field.

Identification

Three unique fields are used to identify a beacon.

  • Universal Unique Identifier (UUID):  This id is generally used to identify all the beacons for an organization.  If we set up beacons in each room at Make & Build we would use the same identifier for all the beacons and have logic to filter out detections coming from our own beacons.
  • Major:  This number is used to identify a smaller subset of beacons for an organization.  If Make and Build had multiple offices we would set the major depending on which office the beacon is in so we can easily locate it.
  • Minor: This number is used to identify the individual beacons.

To uniquely identify a beacon you must use all three of these values together.  For example if Make & Build had five beacons spread across two different offices, the numbering might look like the following:

Distance

The last field helps determine location relative to the beacon.

  • TX Power: The power measurement at a distance of 1 meter that can be used along with the RSSI to calculate the approximate distance from the beacon.

Included in the BLE packet, but not as part of the data field, is a RSSI value that is used together with the TX Power to determine the relative distance of the beacon.

  • Received Signal Strength Indicator (RSSI):  The signal strength detected from the beacon.

Beacons in Practice

Enough with the theory already, show me some code! 

Ok, ok, but before we get to that let’s get some terms out of the way.  The Courier project is split into four main pieces.  We’ve broken them down as follows (these are the terms we use but feel free to use your own).

Agent - The code that listens for the raw BLE signals emitted from beacons.  It forwards the detections to the server for processing.  We are running this code on a Raspberry Pi but you could run it on any device that supports BLE detections.

Server - The server side code that receives detections forwarded from the agent.  It processes the detections, translates them into enter, exit, and alive events, and sends a message to the engine to perform an action.  We’ve chosen to host this on Amazon Web Services (AWS) but that isn’t a requirement.

Engine - This code listens for a message from the server specifying an action to perform.  We are running this code on the same Raspberry Pi that the agent code is running on, but this could easily be separated to different devices.

Admin Console - This code runs in the browser and allows us to configure the beacons and agents.  It also lets us see which agents and engines are currently online. 

Agent

We chose to write the agent code in Node.js because we could run it on our Macs without needing to install BlueZ and it also runs on a Raspberry Pi.  Since we aren’t using an iOS device with built in detection processing we needed to write our own or find a Node library that could do it for us.  We opted for the later and used the open source node-bleacon NPM module to convert the raw BLE signals to usable JSON data for us.  Using node-bleacon we can start listening for detections with the following.

var bleacon = require('bleacon');
bleacon.on('discover', function (bleacon) {
    console.log(JSON.stringify(bleacon));
    // your custom code here
});
bleacon.startScanning(/*uuid,major,minor*/);

For a single beacon, the detection output looks like the following:

{
  "uuid": "b9407f30f5f8466eaff925556b57fe6d",
  "major": 50,
  "minor": 54,
  "measuredPower": -59,
  "rssi": -50,
  "accuracy": 0.5663288654664925,
  "proximity": "near"
}

Beacons emit a lot of detections but we don’t need to update the server every single time we get one.  We want to know when a beacon comes in range, but for practical purposes it’s fine to have a slight delay before the server is informed of the detection.  A configurable property determines how often to package up and send the detections to the server – with a default of every two seconds.  If there are multiple detections for a single beacon in the configured time period the distances are averaged before being sent to the server.  Detections are sent to the server using WebSockets with socket.io in the following JSON format.

[
  {
    "proximity" : 0.83, // approx. distance in meters
    "time" : 1424104114560, // time beacon was seen
    "uuid" : "b9407f30f5f8466eaff925556b57fe6d",
    "major" : 50,
    "minor" : 54,
    "rssi" : -52,
    "tx" : -59,
    "agentId" : "0c:4d:e9:b5:4f:f3" // agent that detected the beacon (defaults to its mac address)
  }
]

This payload indicates that the agent only has one beacon currently in range.  If there were multiple beacons in range the array would contain multiple detection objects.

Server

We chose to write the server in Node.js using the Express framework to get everything up and running quickly.  Node allowed us to get our web server running in just a few lines of code and Express helped simplify building the REST API.  We started out using REST for all communication with the server but have since started using WebSockets with socket.io as well.  Most notably we switched to WebSockets for communication with agents to remove the overhead of HTTP when publishing detections to the server every two seconds.

Once the server receives a detection payload from an agent, it saves the detections to a MongoDB database and analyzes them to determine which beacons came in range for the first time, which ones are still in range, and which ones are no longer detected - enter, alive, or exit events respectively.  Based on the event type, we send action commands to the configured engines.  Currently we send commands to play audio files when we receive an enter event for a beacon, but you can customize this to do whatever you want.

var engineNamespace = socketio.of('/engine');
engineNamespace.to(engineId).emit('playaudio', { filename: 'hello.wav' });

Engine

The engine code is written in Node.js and also runs on the Raspberry Pi – however you are free to run it on any device.  The engine uses socket.io to register with the server and then listens for playaudio events.

socket.on('playaudio', function(payload) {
    console.log('Courier has requested this RPI play: ' + JSON.stringify(payload));
    // code to play audio file
});

Admin Console

The admin console is written in Angular.js and connects to the server using its REST API.  You can use it to manage and check the status of your agents, beacons, and engines.  You can also view a list of the saved detections.

System Components

Putting all the pieces together the full system looks like this.

Source Code

Although the codebase is still a work in progress we’ve open sourced it in its current state to fill in the remaining details.  Feel free to contribute, provide feedback, or use parts of it in your own projects.

Agent - https://github.com/makeandbuild/courier-agent-nodejs

Server & Admin Console - https://github.com/makeandbuild/Courier

Engine - https://github.com/makeandbuild/courier-agent-engine