Beacon tracking with Node.js and Raspberry Pi
Over the last few weeks we've been working with beacons. We're using them for a project that features indoor proximity tracking. We needed to track the beacon itself moving around a space then visualize the output on screen. As a proof of concept we decided to roll our own beacon scanner with a Raspberry pi and node.js. See the video below for the final result.
At the onset of the project we had a basic working knowledge of Bluetooth smart (Bluetooth low energy) and we quickly ran into some unexpected and interesting hurdles as we got into production. In this post we'll walk through our process and some of the insights we've gleaned so far.
Hardware Roundup
We waded through a lot of hardware before we arrived at a solution. Here’s the highlights.
Raspberry Pi B+
We decided to use a Raspberry PI B+ as the base hardware for the scanner. The form factor suited our needs and getting node.js setup on the Raspbian distro was a breeze. We also added a basic wifi and bluetooth dongles.
Nordic Semiconductor nRF beacons
We started with the Nordic Semiconductor nRF beacons. These are the same chips used in the Estimotes and we liked that they came without any housing incase we wanted to 3D print our own later. One important thing to note about these beacons is they aren't configured to broadcast iBeacon messages out of the box. Which threw us off at first because they didn't show up in some of our Bluetooth utility scanners (like this awesome one from Huge )
Estimotes
We've had these bad-boys sitting around our office for about 8 months and finally got to put them to the test. The Estimotes are iBeacon certified and all of our bluetooth LE utilities detected them right away. They also seemed to be the most consistent with regards to the data being reported back. The Estimote iOS app is top-notch and the overall hardware build quality is great. The only downside is their size. For our purposes we were looking for something a bit smaller. We’re really excited to see how their beacon stickers work out when they ship.
Gimbal by Qualcomm
We also signed up for a qualcomm developer account and got 3 free Gimbal beacons, thanks Qualcomm! The keychain fob was closer to what wanted for our demo and you can’t beat the $5 price point. These beacons come with an iOS app and web-app configuration tool that makes it pretty easy to update the devices’ firmware.
The only annoying thing about these beacons is they come out of the box with a security feature that changes the mac addresses every time they emit an advertisement. So when we turned on our Bluetooth scanners we’d see about 20 unique Bluetooth smart devices. Other than that, the form-factor is nice and Gimbal gives you decent control over the beacon’s configuration.
Hardware Take aways
In general we felt there is strong community of people doing work targeting devices that support Apple’s iBeacon format. Apple seems to be a head in the race and iBeacon is broadly supported (vs non iBeacon configurations). Also, it was relatively easy to use our Google-fu to find community answers when we had questions.
The beacons that didn't broadcast with an iBeacon configuration seemed a bit harder to work with and track down documentation, but that’s mostly because we’re impatient. Overall the Estimotes were our favorite beacon hardware.
Software
Bluetooth tracking on Raspberry Pi is pretty straightforward, the biggest hurdle (and most time consuming) was getting all the right software in place. Here’s a list of software we used to get out POC working.
Bluez
Bluez is the core bluetooth stack for linux and what the Raspberry Pi needs to start scanning for Bluetooth smart devices. All of the subsequent libraries and utilities mentioned in this post depend on having Bluez installed.
Getting Bluez installed wasn’t too hard. Jared Wolff’s tutorial helped me through this part of the process. Grab a coffee, this install takes about 90 min on the Pi.
hcitool & gatttool
Once Bluez is installed you can use these two utilities to scan for and connect to Bluetooth and smart devices. Hcitool became a really useful tool for quickly scanning for beacon hardware without writing any custom code. With Bluez installed you can get a list of nearby hardware addresses with this command:
sudo hcitool lescan
Once you've found the hardware address you're looking for you can use gatttol to connect to the devices to get more information about it including it’s manufacturer data and available services. If you're looking to dig a little deeper into gatttol I would recommend Mike Ryan’s Blog post…it helped demystify it for us.
Node.js
The next step was to get node running on the pi so we could start pumping the data to a server for analysis. Thankfully, this process has gotten a lot easier with the release of the ARM-version of node. Just type the following commands and in about 20min you’ll be ready to go.
sudo wget http://node-arm.herokuapp.com/node_latest_armhf.deb
sudo dpkg -i node_latest_armhf.deb
Noble
With node installed we searched Github for node-based beacon libraries and we found Noble by SandeepMistry. Sandeep has also written a few other bluetooth libraries including Bleno, and Bleacon, but for our purposes we proceeded with Noble.
Noble makes it really easy to scan for bluetooth devices and access to their meta data. With the following code we were able to scan for nearby devices and print out their hardware address, rssi, and local name.
Note — in Noble “perihperal.uuid” is actually the mac address, the actual uuid can be parsed out of the advertisement packet with manufacturerData payload which typically looks something like (4C00 02 15 585CDE931B0142CC9A1325009BEDC65E 0000 0000 C5). Refer this blog for more info about the payload.
Socket.io
Once we were able to reliably track the rssi for a given beacon we pushed the data out via web-sockets. We used the stand alone socket.io-client which allowed us to pass messages to another node server for analysis. The code looked something like this:
Node.js (server)
Our server app (also running node.js with socket.io) is responsible for capturing the data being sent from the pi. We tried to keep any calculations off of the Pi to reduce the CPU load and pass the raw data to our server (in this case my macbook pro). The server waits for the pi to connect then captures the data to do some calculations before sending messages to the client. The code below shows the basic structure of how we setup the server to receive messages from the pi.
HTML/CSS/Js (client)
The last piece of the puzzle was to render the location of the beacon on a client webpage. The UI itself is super simple — we used socket.io again and assign the socket to the “client” room. When the server sends down the distance value we pass it to a linear scale (using d3.js) to convert it’s location in meters to pixels on screen. In this case we have a hard coded a linear scale based on the min/max distance values we want to track (0–20m) and the available pixels on screen (0 - 1000px). Lastly we tween the location of the visual with TweenLite so the beacon visual animates to it’s new position. The key points of the code are highlighted below:
Proximity Tracking
At this point we have everything in place to start doing some proximity tracking. Should be easy right? Well..it was a little more involved than I thought. There were two big hurdles we needed to overcome:
- Estimate distance from rssi
- Smooth the noisy data coming from the sensor
Estimating distance from RSSI
This was a little confusing at first, but with a little searching we realized a lot of people in the community were looking for the same thing. This stackoverflow post contained the magic we were looking for. We quickly converted the code to javascript and tested it out. The idea behind the algorithm is to calculate the ratio between the rssi signal and measured power and then add some decay for signal fall off. I won’t claim to know exactly what the numbers in algorithm below are doing, but after a bunch of tests we felt like it was accurate enough for our POC.
Smoothing the signal
As we roamed around the office with beacons in our pockets we were surprised to see how unreliable and noisy the data was being reported. We observed several things that caused major signal interference or absorption:
- Bluetooth signals don’t travel through the body well — facing the scanner with the beacon in a front pocket worked as expected. Turning away from the scanner caused a huge drop in the volume of data reported and very low rssi signals.
- Tracking gets very unreliable the farther away you get — The data throughput drops off significantly after 6–8 feet and the variance of the values reported increases dramatically.
- Placement of the scanner matters — After a bunch of tests we determined our scanner got the best results being placed on a wall (not ceiling) between 5'-6.5' off the ground.
This is all good stuff, but we still needed to smooth the values coming from the scanner so we could render a beacon’s position on screen in a way that didn’t look like the beacons were jittering around all over the place.
We started by calculating a rolling average of the rssi values. Unfortunately, that didn’t work because it took too long for the average to “catch up” as the beacon moved near the scanner after being far away for a while. So we implemented a rolling window that never holds more than 10 values and take the average every second. This worked pretty well, but the visual result was still a bit “jumpy”.
Enter the Kalman Filter — This algorithm is often used for estimating vehicle position with unreliable GPS signals. It’s also generally used for signal processing. It takes a series of noisy measurements and estimates the optimal output value. It seemed ideal and luckily we found this fidddle that we adapted for our purposes. Combined with our rolling window approach we finally saw some respectable results.
Next Steps
Although our results so far are pretty basic, we felt like there was value in showing our process since piecing of this stuff together from scratch was pretty time-consuming.
For next steps, we’ll be moving on to trilateration to track multiple beacons with multiple scanners. If you’ve had any success with indoor beacon tracking or have any suggestions on what we’ve hacked together we’d love to hear it. Hit us up on twitter via @truthlabschi