Ellipto: Electric Imp meets Keen IO

Building a fitness tracker device with a beautiful dashboard in 71 lines of original code, with no soldering, and no servers.


So, the other day, my lovely wife Caroline came home with an Imp. An Electric Imp, to be precise — a WiFi sensor/actuator node in the form factor of an SD card. It’s supposed to be designed for simple development and low power consumption.

Electric Imp on an April breakout board.
With blue fingernails.

Electric Imp had just completed an integration with Keen IO (Caroline’s company). Keen IO is a powerful platform for backend analytics— you just send them the data and you can build beautiful dashboards by inserting simple javascript onto an HTML page.

This is what I wanted to investigate— can we build connected devices with no backend whatsoever. Yup, not even a single rented server, just these two services and a web hosting service. Yes, you can quibble that there is a backend, I’ve just outsourced it, and that’s fair enough. But I don’t have to deal with it, and my team doesn’t have to worry about it. In my solipsistic world, that means it doesn’t exist.

So, let’s build a connected device to see if this really can be done!

…inside an elliptical trainer

Well, being a quantified kind of self, I already have a Fitbit. But since it’s just an accelerometer, it has no idea what I’m actually doing. So when I hop on my elliptical trainer, it can’t tell the difference between walking and slogging through a high resistance elliptical session. So let’s try tracking that. And let’s be as lazy as possible when doing so. I want to see how few lines of code I can write, how few solder joints I need to make, and how fast it can happen.

How about if instead of building an accelerometer board, writing I2C code, sampling at high speed and figuring out some algorithm to turn the bouncing motion of walking into step counts, we just use a simple tilt switch — these devices are cheap and simple, just little balls that close a circuit when a device is pointed straight down.

And looking inside my elliptical, there’s this lovely rotating crank to attach to. Most of them are built this way, although I can’t promise the device we’ll build here will fit all of them — may take a little creativity, but I’m sure there’s a way to make these components fit.

So, every time the foot pedal goes through a cycle, the crank tilts through 360 degrees. This particular tilt switch goes from open to closed whenever you pass through a 45 degree region around pointing straight down. I hooked this up to pin 1 of the imp. This is a special “wakeup pin” which causes the sleeping imp to wake up when the pin is pulled high. So if we simply count these transitions and send each one to Keen IO, we’ll get a step count.

The Hardware

So here’s a picture of what I threw together with some simple off-the-shelf parts. Basically, I put an Electric Imp on their April breakout board, and plugged that into a mini-breadboard. I connected one side of the tilt switch to 3.3v, and the other to a digital IO pin, and powered it with 3 AA batteries. I added a few minor conveniences beyond that: a voltage divider going to an analog input on the Imp, and an LED that flashes when it counts a step. I stuck these onto a 1x1" plastic angle iron (generally sold as “corner protectors” or “corner moldings” at hardware stores or TAP plastics).

Here’s a parts list and a schematic of what’s connected up on the board. Wire it however you like, nothing is very critical. I just put a spacer between the protruding end of the imp and the breadboard to keep the pins from pulling out, then cable tied it onto the breadboard. Note that you should make sure the protruding part of the imp is not sitting on top of the batteries or electronics, or directly on a piece a metal, which would detune the antenna and cut your range. You should be able to get pretty much all of this from Sparkfun, Seeed Studio, Adafruit, Digikey, Mouser, Jameco, or a good local electronics store (Al Lasher’s in Berkeley is highly recommended if you’re in that neck of the woods). Should cost around $55 all in. I’ve linked some of the parts, just to clarify things.

Parts List

  • Electric Imp
  • April Dev board
  • Tilt switch. I used the E-Switch TM1000, ordered from Mouser, but you can pick up a tilt switch off of SparkFun, AdaFruit, or most good electronics stores.
  • mini-breadboard — like this
  • Resistors, 1 each of 500 ohm, 470k and 220k
  • Battery holder with power switch. I used one with 3xAA batteries; you’re fine using as few as 3 AAAs, but you can also use up to 6 D cells, or anything in between.
  • Plastic corner protector/corner molding, anything between 3/4" and 1 1/4" should work, I wouldn’t go larger. Probably available at Home Depot, Lowe’s, etc. if you aren’t lucky enough to have TAP plastics near you.
  • Cable ties, hookup wire, double sided tape, etc.
Ellipto Schematic
(Fritzing file is in the repo)

Setting up Ellipto

Once I had built the hardware, I used the Imp’s BlinkUp procedure on my phone, which allowed it to join my home WiFi and connect the Imp to my account. These settings are persistent, so you can do it as soon as you can power the imp. You should see the imp blink red, then green, then turn off within a minute, which indicates a successful setup. If it continually blinks yellow, or fast-blinks red, use the “clear wireless configuration” option of the phone app and redo the blinkup. If that fails, power cycle the device and hope.

Firmware

At this point, you need to get yourself a Keen IO account. Go to their website and get one, it’s free up to 50k events/month, that’s something like 5 hours of running and 10 hours of walking. If you’re a serious athlete, you might need to modify the code to only send every 10th step and make the dashboard multiply by 10. You’ll need the read and write keys to proceed, so grab those from your Keen IO dashboard.

So, now you open up the Electric Imp IDE. Create a project as shown in the example on the imp website. You can find the actual code for ellipto in the two files in this git repo: ellipto.agent.nut and ellipto.device.nut. This stuff is all in Squirrel, which is the language all imp software gets coded in. Apparently they wanted to do Javascript, but it wouldn’t fit. So the Electric Imp folks found a somewhat obscure language which is sort of like a scripting language mashed up with C. It’s pretty easy to get used to if you know C or javascript.

Paste the content of these files into the agent and device windows respectively. You’ll need to find the <API key> and <project ID> placeholders in the agent code and replace it with your own. Then hit “build and run” and things should work. I’ll let you go play, but if you want to know what you just did, c’mon back and I’ll explain. I’m not going to go through the code line by line, but I’ll focus on the key functions. I suggest you open up the IDE window and scroll through the code as you read the explanation.

Secret Agent Man

The agent isn’t very interesting. It contains the Keen class, linked here, which is one of the many SDKs available for Keen (contributed by Electric Imp). Then the function gotState() simply responds to any data coming from the imp by posting it to the collection StepData on Keen IO, and logging it to the Imp IDE with server.log. device.on links that function to the received data event.

function gotState(data) {
local result = keen.sendEvent(“StepData”, data);
server.log(result.statuscode + “: “ + result.body);
}
device.on(“step”,gotState);

The Devious Device

Here’s where all the work gets done. The guts of the thing are actually pretty simple. Basically, there’s a function, tilted(), which gets bound to pin 1 as a callback. In theory, whenever the device rotates, it will trip tilted() and we’ll count those up, send them to the agent, hop on the elliptical trainer, and run off into the sunset… except that we’re running on a stationary elliptical trainer getting nowhere fast. But I digress. That basic code was only 12 lines of code. Yes, you can build a functional embedded sensor with 12 lines of code. Imp really does do a lot for you.

Boing!
But there’s a fly in the ointment: switches bounce. Inside the tilt switch, there’s a little metal ball which shorts two contacts to close the switch. Unfortunately, the ball bounces, so instead of a nice clean on/off, we see a bunch of rapid on/off pulses each time we turn the switch on and off. As much as I’d love to get an extra couple thousand steps per day, that would be cheating. So we need to filter those out. Often these are filtered out by adding a hardware lowpass filter, but here, I do it in firmware. This accounts for nearly all the complexity of the code.

I won’t write a treatise on debouncing — there are a vast number of algorithms out there. I designed one to work well with the imp’s sleep modes, which are essential to keeping power consumption low. Essentially, there are two tricks in this one. You need to make sure you don’t see a quick bounce pulse or noise signal as a step, and you know there will be a bunch of pulses after each transition. I solve this by creating a lockout for 50 milliseconds after each valid step by setting ignore to lock out any tilts, and scheduling the debounce() function to run after 50 milliseconds to release the lock. This is an imp-friendly way to do things, because the imp can continue sleeping between tilts to save power— busy waiting and polling are the enemies of low power.

Since we’re ignoring all those bounces, we also need to verify that the tilt state has actually changed after the lockout time— it wasn’t just noise— so we verify that newtilt != tiltstate. If it looks good, then we put together an event, which includes the stepCount and the current battery voltage and send it off to agent.send. Here’s what those functions look like:

function debounce() {
ignore=false;
local newtilt = tilt.read();
if(newtilt==tiltstate) { // verify we settle to new value
if(tiltstate) {
counter++;
eventData <- {
batteryVoltage = batt_read(),
stepCount = counter
}
agent.send(“step”,eventData);
}
}
}
function tilted() { 
idle=0;
if(!ignore) {
local newTilt = tilt.read();
if(newTilt != tiltstate) { // ignore if state unchanged— noise
ignore=true;
imp.wakeup(0.05, debounce);
led.write(1); //LED on until debounce done
tiltstate=newTilt;
}
}
}

So that’s pretty much it for the sensing part. But we have a second problem.

Saving our precious electrons
When first I built this all up, the batteries would only last a few days. The Imp should be able to do better than that — it’s raison d’etre is really low power WiFi. And yes, it can do better.

First of all, I set up a low power mode , which puts the imp’s receiver in a sampling mode. This adds 1/4 sec of delay to downstream messages, but we don’t care, since we don’t have any messages going to the imp, just data flowing upstream.

imp.setpowersave(true);

But we can do better than that. The imp has a deep sleep mode, which turns off WiFi and shuts down the processor until the wakeup line goes high. That’s pin 1, so it’ll automatically wake up on the next step. We’ll lose a few steps while it rejoins WiFi, but by design, the imp has a very fast rejoin cycle, so it’s only a step or three.

function sleepIfIdle() {
if(idle) {
if(!tilt.read()) {
server.log(“Entering deep sleep”);
server.sleepfor(86400);
}
else {
server.log(“Can’t sleep; Rotate crank 45 degrees to permit deep sleep for more power savings”);
}
}
else
{
idle=1;
imp.wakeup(600,sleepIfIdle);
}
}

Only ugly detail here is that the Imp can’t deep sleep if you happen to leave the crank in a position where the tilt switch is active. Unfortunately, the wakeup signal is level-triggered rather than edge-triggered (a hardware limitation), so you can’t sleep if the level switch is closed. We can overcome this by adding an edge detector circuit to the wakeup pin, but this is left as an exercise to the reader, or my future self. With powersave on, idle power consumption is down to 5 mA, so we should be able to last a good long time if we only occasionally fail to sleep.

The rest of the code should be self-explanatory — just setting up pins, initializing variables, reading battery voltage and setting up timers. Just to show how simple it was, here’s how you configure a periodic event, and bind a callback to a digital IO line (this one happens to be the wakeup pin as well).

tilt.configure(DIGITAL_IN_WAKEUP, tilted);
imp.wakeup(600,sleepIfIdle);

Installing Ellipto

I installed Ellipto in my elliptical trainer, which is a Precor EFX5.21i. Your mileage may vary, so be careful — you don’t want to have any mechanical interferences damage your elliptical trainer or your sensor. I suspect this will actually work on bikes, some ergometers and a bunch of other devices, but no promises— this device is mainly for learning value, I take no responsibility for anything you do with it.

On my machine, I started by removing the 4 allen screws on the plastic cover at the back of the elliptical. Then I stuck the assembled sensor onto the crank with dual lock industrial velcro (the plastic kind) which is incredibly robust stuff. After installation, you need to turn the crank slowly and listen and watch to make sure nothing is rubbing against the internals of the mechanism. You should see the LED flashing as it turns. If it flashes more than once in a cycle, don’t worry— the flashes only represent a partially debounced signal. But it should flash.

Then put the plastic cover back on, and again, listen carefully for rubbing as you very slowly rotate the crank. If it moves smoothly, you should be all done.

The Dashboard

Of course we need some way to see the results, so Keen IO to the rescue. They have a very nice dashboard template that I’ll use as a starting point. I was able to modify it for my own nefarious purposes by just changing the details of the queries, the graphing parameters and the HTML.

Here’s how it came out. Not so shabby, if you ask me. It’s even a responsive design, and looks good in Chrome and Safari, but also on my iPad and iPhone, landscape or portrait.

The Ellipto dashboard.
And no, I haven’t been getting much exercise because some project has been distracting me

The template consists of an HTML file which does nothing but include scripts for bower, bootstrap.js, jQuery and Keen IO and then creates divs for the graphs to go in using a bootstrap grid framework, which gives us the nice responsive design. There’s a basic CSS file, and then the javascript where the magic happens. Keen actually provides a dashboard tool which lets you try out queries and gives you graphs and the associated javascript you could put on the page to implement the queries. But aside from the modifications I mentioned above, I pretty much followed the template. Here’s an example of how this works, the javascript for the 3 month activity graph:

 var three_month_history = new Keen.Query(“count”, {
eventCollection: “StepData”,
interval: “daily”,
filters: [
{“property_name”: “stepCount”, “operator”: “exists”, “property_value”: true}
],
timeframe: “this_12_weeks”
});
 var req_three_month_history = client.draw(three_month_history, document.getElementById(“chart-03"), {
chartType: “columnchart”,
title: false,
height: 150,
width: “auto”,
chartOptions: {
chartArea: {
height: “75%”,
left: “7%”,
top: “5%”,
width: “85%”
},
hAxis: {
gridlines: {
count: 12
},
format: “MMM d”
},
vAxis: {
format: “####”,
title: “Steps”
},
bar: {groupWidth: “95%”}
}
});

First we create the query object, three_month_history. It’s taking a count with the stepData events, giving one point daily, only returning data where stepCount exists (the other events are just battery updates and don’t indicate actual steps), and covering a timeframe of 12 weeks.

Then we draw the graph, specifying that it goes into the HTML DIV named chart-03. The rest of the parameters are just specifying the details of the graph in the format of the Google Visualization Library, which they currently use to actually render the graphs.

Since this is a realtime application and I like to watch it update on my iPad while I’m on the elliptical, I added code to make the graphs refresh in realtime. For example

 setInterval(function () {
req_three_month_history.refresh();
}, 10000);

If you modify the dashboard, be careful to be nice to the Keen IO folks and remember that the interval is in milliseconds — they can handle a huge number of queries, but query spam is not neighborly.

The queries and graphs I implemented were basically as follows:

  • Steps for the day. This is just a query for this_1_day that counts all the records in stepData, with a filter requiring them to contain steps. This is to filter out events that are just the hourly battery voltage data. I display it as a metric, i.e. just a prettyprinted number
  • Battery Voltage. This is an extraction of the latest batteryVoltage
  • 2 hour activity. This is a count query of steps in each 5 minute interval, filtering out battery-only events and converting steps/5 mins to mph (assuming a 30" stride for now) displays as a linegraph
  • Activity for the day. This counts steps in each 15 minute interval and displays steps/minute as a columngraph, filtering out battery-only events
  • 3 month activity. This shows steps per day for the previous 12 weeks, filtering out battery-only events. It’s also a columngraph.

There was one change I could not resist making, however, because it illustrates a cool, underdocumented feature of Keen IO. I wanted to have my graph output in mph, and not in steps per minute. I could have done this on the device, but at some point, I’d like to have the page accept a stride length setting, so the calculation had to be done on the client side. This turned out to be pretty easy, since Keen provides an iterator which you can apply before doing the visualization which can apply an arbitrary javascript function to each element of the array to be graphed. So the graphing portion of the 2 hour activity javascript looked like this:

 client.run(now_history, function (response) {
Keen.utils.each(response.result, function (record, index) {
record.value *= 5.68e-3;
});
   var req_now_history = new Keen.Visualization(response,     document.getElementById(“chart-01"), {
chartType: “linechart”,
title: false,
height: 150,
width: “auto”,
chartOptions: {
chartArea: {
height: “75%”,
left: “10%”,
top: “5%”,
width: “85%”
},
vAxis: {
format: “##.#”,
title: “mph”
},
bar: {groupWidth: “95%”}
}
});
});

Essentially, the first part is an iterator scaling all of the values, and the second part is drawing the graph itself. No reason you can’t do much more complex things. This is why I love the platform revolution — good platforms expose this sort of fine-grained control in a clean way.

Loose Ends

This was kind of just a toy, but it turned into a pretty evolved toy. But there are still a few loose ends :

  • The mechanical form factor makes me itch, although it’s held up OK for me thus far. Using a breadboard for this was quick, but it’s not so robust. If enough people want this for real, we could make a PCB, but you could also just solder one up on a protoboard for yourself.
  • Power consumption. The sleep problem could be corrected by adding an edge detector circuit. Also, you could just pull out the LED— the thing lives inside a box, that was just for debugging and general coolness. And we could cut transmit power and decrease the number of Keen events by just sending every 50th step.
  • Fancier UI. The dashboard could be interactive, with features like switchable step count vs. mph, english vs. metric units, and allow you to set your stride length
  • Integration with Fitbit, MyFitnessPal, and other services. For example, Fitbit has an API which lets you override your step count with a defined activity, like “elliptical trainer, 30 minutes, high intensity.” It’d be great to connect that up.

I’m off to other projects for the moment, but feel free to implement the ideas above or any other coolness that occurs to you and send me a pull request.

Conclusions

That was pretty quick and painless for such an ambitious project. For a complete system, all it took was a total of 50 lines of original code for the device and 21 lines of new javascript on the dashboard side— the rest of the code is just copied and modified to configure the new screen. And the new javascript was just because I wanted to get fancy and display mph instead of steps/minute. I even managed to build it without soldering, and it’s survived OK. The only tricky part in the code was the debouncing on the device, which did take a few iterations to get right. I’m measuring an accuracy of around 95%, which is better than I do with a Fitbit.

So we in the end, we wound up with a pretty robust sensor system with a beautiful dashboard with no backend code whatsoever. No servers — even rented ones— no DevOps team, no monthly fees.

My main takeaway here is this:

I have seen the future, and it has no backend.

——


All files referenced here can be found at https://github.com/jimreich/Ellipto

Thanks to Dustin Larimer of Keen IO for assistance with the (very new) dashboard template and Peter Keyashian and Matt Haines of Electric Imp for providing Imps for me to beat on. And, of course, Caroline.

Except where other license is provided, the contents of this document are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. If someone wants to commercialize it, let’s talk.