3 people, 2 days, 1 ridiculous idea
There is no sadder sight in this world than a mug of tea left abandoned; neglected as its owner buys milk. If only they’d known how much milk remained before starting the tea-making. The onerous task of opening up the fridge to manually inspect the milk far too daunting to be considered, one might begin to think there should be another way to know how much milk was in the fridge, in real-time.
Thus the low-milk-early-warning-detection system was born. And they called it “Got Milk”.
The 2-day NCR Edinburgh CodeFest provided the perfect time to address such an issue; hack digital scales to transmit the current weight of the milk to a server, build an Android app to display the current quantity and provide additional visual cues as to the state of the milk with colour LEDs.
With the company having provided a set of Philips Hue programmable colour LEDs specifically for CodeFest, it would have been foolish not to make use of them. LEDs placed strategically around the office providing at-a-glance cues as to the state of the milk.
Despite the title of the project, we realised that limiting the system to just milk would be missing out on a great opportunity to capture other essentials. Although only the milk’s reading is automatically retrieved, other products can be set manually when someone notices their state has changed. In a future improvement we could extend the automated readings to all other products too. We would just need to scale-up the hardware - by adding more scales.
The initial idea of allowing the team to manually input remaining milk levels was long since forgotten when Alex (the ‘hardware guy’) signed up. He brought some tools with him from home.
He used phrases like “load cell” and “wheatstone bridge” and other such things we didn’t understand. Pulling apart an off-the-shelf set of kitchen scales, rewiring and rebuilding it to communicate its reading over XBee did not seem to phase him. He destroyed, he soldered, he duct taped, he resembled a mad scientist. And he built apparatus that calculated the milk levels automatically.
Alex “doing the hardware”
With Nick joining the project, our server-side was born. Not content with building a scalable RESTful web service to tie it all together, it had to be portable. It may just have been a hack day, but there is nothing hacky about its deployment. Docker was chosen to allow easy transfer to another machine when needed.
Nick (pictured outside the office, because he eluded the camera during the event)
There was also me. As a native app developer, I wanted some tablets in the mix. The plan for the tablets was for them to sit alongside the LEDs and show the current product readings. Additionally they would allow users to manually input amounts for the other products we weren’t weighing.
Craig - working hard on the next project… Got Fish?
I knew roughly what I wanted; one tablet in the kitchen and one by the office’s main door. The kitchen tablet would have its main job being to allow users to manually update how much of each product remained. The tablet by the main door would have its main job being to show, at a glance, how much of each product remained. The general idea being that if a team member happened to be heading out to the local store they might notice we are running low on some items and pick them up for us.
I wanted an iPad Mini in there to give me a chance at using the new iOS programming language, Swift. However, plans for this went sour as the developer preview of Xcode sitting atop the developer preview of OS X Yosemite ground my system to a halt. Had I persevered, I would have spent the two days growling at my computer instead of having fun. So iOS was out and it became all about Android.
We had a spare Nexus 7 (2012 model) lying around that I commandeered. Initially I’d planned on developing two separate apps (a viewer and an updater) because I planned to write one on iOS and the other on Android. However, given the decision to ditch iOS, it became a reasonable solution to include both the viewing and updating functionality within the same app.
Not clear perhaps from the photo, but the bulbs in the app mirror the physical LED bulbs’ colours.
Oh, and of course that Nexus tablet stand was 3D printed! Alex, recently added a 3D printer to his toolkit and kindly printed one off for us.
Since we initially built the APIs with the scales in mind all the values are passed in grams. This had the unfortunate consequence of making the users estimate the amount of tea bags left, in grams! Turns out that people aren’t great at this task so made some crude conversion estimates:
Therefore when the user is entering how much fruit remains they can enter that there are “12 bits of fruit” instead of 1920g.
For the Android app I had a few libraries and frameworks I hadn’t managed to use yet. I wanted to try out an Android dependency injection and general Android utility framework Roboguice, an alternative HTTP client library called OkHttp, a REST client called Retrofit.
Roboguice 2 is introduced as being “Google Guice on Android”. Its primary function is to allow dependency injection to eliminate a lot of the boilerplate code in Android. If you are an Android developer, you will be familiar with the structure of this snippet:
class AndroidWay extends Activity {
TextView name;
String myName;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name = (TextView) findViewById(R.id.name);
myName = getString(R.string.app_name);
name.setText( "Hello, " + myName );
}
}
The tedium of separating the TextView
declaration from its assignment (assuming you’ll use it throughout your Activity
), the required setContentView
at the right time, the general clutter of the onCreate
method and that ghastly need to cast the result of findViewById
to a TextView
are not pretty things to a developer’s sensitive eyes. You have to get through a lot of code before you actually do the thing you really wanted to do.
The RoboGuice way:
@ContentView(R.layout.main)
class RoboWay extends RoboActivity {
@InjectView(R.id.name) TextView name;
@InjectResource(R.string.app_name) String myName;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
name.setText( "Hello, " + myName );
}
}
Robojuice provides a more concise, more legible, less boring way to write code and there is less to go wrong. I found it a pain having to subclass RoboActivity
, RoboService
(there must be a better way) and so on, but generally use of the framework looks promising. I did have some intermittent null pointer exceptions where it seemed sometimes the dependency hadn’t been injected at all, but I assume this was due to my unfamiliarity of the framework more than anything else.
There are other dependency injection frameworks out there (I’m looking at you Dagger ) that I would like to try before committing to any one but I can’t argue against the simplicity of using RoboGuice for this small project.
OkHttp is an HTTP client built by Square with many nice features built in for efficiency (connection pooling, SPDY support, GZIP support, response caching) as well as stability (ability to handle common connection problems).
I started to use this library but realised that many of the benefits wouldn’t be useful in the scope of this small project. As such, it would feel like I was just using any other HTTP client. That’s not to in any way disparage the library as it looks great as an everyday replacement of built-in HTTP clients in Android. It just didn’t make the cut as being exciting enough to be included in the codefest project.
Ousted by another product made by Square, I decided I didn’t want a general purpose HTTP client; I wanted a client designed for REST.
Retrofit is a REST client. You define an Interface which maps to your RESTful endpoints on the server and makes use of annotations to define the required type of API you are dealing with.
This is an excerpt from the Got Milk RemoteGotMilkService
Interface; it declares the existence and structure of the listProducts remote API method which the server makes available.
public interface RemoteGotMilkService {
@GET("/products/")
ProductResult listProducts(@QueryMap Map<String, String> options);
// other API methods omitted
}
In order to actually call the remote APIs, you need an implementation of the service I’ve just defined.
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(baseUrl)
.build();
RemoteGotMilkService service = restAdapter.create(RemoteGotMilkService.class);
I could then use the implementation of RemoteGotMilkService
to retrieve the list of products available:
ProductResult result = service.listProducts(queryParameters);
Retrofit automatically handles object conversion to/from JSON as it relies on GSON under the hood. Well reasonably automatically anyway, I still had to annotate some properties that were in the JSON under a different name than I wanted to use but this is to be expected of any unmarshalling tool.
You can of course also POST requests (and all other HTTP method types) set query params, alter headers and generally everything you’d expect to be able to do.
Retrofit provides a very elegant, declarative solution for consuming RESTful API methods. There is no magic here, nothing you couldn’t code “by hand”, but that’s why I like it. It guides the client development such that all API-consuming client code has a recognisable structure; if you’ve used one API method you will recognise all others. This familiarity and consistency would be greatly appreciated on a larger project.
As part of the Got Milk team in the recent NCR Edinburgh CodeFest I was responsible for creating the hardware we needed to accurately measure how much milk we have left in the fridge.
To make this happen we needed a lot of bits, here they are:
We wanted a cheap, simple-ish way to measure the liquid, so, figuring that all digital kitchen scales must be able to weigh something and get the results digitally, that would be a good start.
I purchased a set of Argos value digital kitchen scales (£6.99, other brands are available) and proceeded to take them apart.
The electronics consisted of two main parts; the load cell and the PCB.
The PCB consisted of a single chip (i.e. everything integrated, no handy amplified or adc output here) with a couple of buttons and an interface for the LCD display.
There are a couple of different ways we could interface with the scale; either interface with the buttons and read the digital signals going to the LCD or read the load cell directly and turn the analogue signal into a digital one.
The pros and cons of each approach:
LCD read
Load cell read
I decided to use the load cell read approach as it's less error prone and doesn't tie us to any particular design of kitchen scale (as far as I know most digital kitchen scales will use some form of load cell).
Scientific calibration process
In order to read the signal from the load cell some amplification is necessary.
We used a TI INA125P instrumentation amplifier. It's very similar to other differential amplifiers I have used before, but only one resistor is required to adjust the gain and we don't have to worry about input impedance matching. The INA125P is particularly great as it runs on a huge range of voltages from very low single supply voltages (less than 3V) up to dual supply +-18V, so fine for our single supply 3.3V MSP430 development board.
The circuit, including the load cell looks like:
With our 3.3V excitation voltage we were getting a barely perceptible change in output (around 2mV) with a 5kg weight on the scale. Our ADC gives us a sensitivity of around 3mV per level (10 bit ADC or 1024 levels between 0 and 3V). So we needed to amplify the voltage to be around 1000 times higher. The potentiometer allows us to change the gain of the amplifier so we end up with a good range going into the ADC.
I've previously used Microchip PIC 8-bit microcontrollers in various personal projects including Robot Wars robots and an automated cocktail machine. The PICs are still a very good choice, using very little power and being incredibly cheap, however I've yet to find a good, free development environment under Linux.
For this project I wanted to see what was available now in terms of low power micros with some decent toolchain and development environment for Linux. I settled on the TI MSP430 series as there are packages, out of the box, on many distributions for MSP430 GCC and the other tools required to get a program up and running on the hardware.
While I did try to use the Fedora 20 packaged MSP430 tools plus Eclipse and the CDT plugin for development, I could not get them to reliably connect to the development board for programming and debugging.
Since we only had 2 days for the project I decided to try out the all-in-one, Arduino style, Energia tools made available from TI. Energia worked flawlessly and while not as powerful (e.g. no debugging) as the separate tools plus Eclipse, it got me up-and-running very quickly.
In order to get the digital value from the microcontroller to a computer we needed some way communicate the value. While testing I was using the built-in serial to USB converter on the MSP430 development board to communicate with my laptop.
Our fridge has some power near it, but no servers, RJ45 ports or other convenient physical data connection point for our microcontroller. In order to get the data somewhere useful we needed to use wireless.
I had a couple of XBee transceiver modules in my spare electronic bits box so after hooking one of these up to the MSP430 board we had one side of our wireless link. One thing to note with Energia is that it, at present, only lets you use the default serial port (UART) on the MSP device, even if the device has multiple UARTs. On our board this meant having to remove the jumpers that connect the default UART pins to the USB to serial converter, and then connecting those pins up to our XBee device.
For the other end of the link we just needed something with a USB port, as I had a spare XBee adaptor with a built in USB-to-serial chip. Since the servers are quite a long way from the fridge we decided to use a spare Raspberry Pi to poll the MSP430 for weight values from the scale and submit those to our Got Milk server using a little bit of Python.
The XBees were set up to act as a point-to-point serial link, rather than as a full self-organising mesh network, as again we only had less than 2 days to get something up and running and demoed.
There was very little to do on the microcontroller, the main work was in calibration, however as the output seemed pretty much linear once a zero value and steps-per-gram had been worked out the rest is straight forward. The program on the microcontroller just sits in an infinite loop waiting for a start byte on the serial port. When it receives this it reads the ADC, 6 times to get an average, then outputs the value, both in raw ADC value and the weight in grams, again over the serial port. The values are output in ASCII so that they are human readable, to make debugging easier.
On the raspberry pi side a simple python program was created. When invoked it sends the start byte over the specified serial port and waits up to 2 seconds for a response (more than enough time for the microcontroller to respond). It then takes the ASCII formatted weight response and converts that to an integer, ready for use by whatever needs it.
Our data producing hardware, and our data consuming hardware and software stack design were so diverse that we needed a flexible, platform agnostic way to bridge them that would support data persistence, and hopefully provide an easy way to set up everything with a rich ecosystem of extensions and plug-ins, so that new functionality could be added without too much hassle.
It was apparent when we examined the requirements that I needed to implement some kind of web service. Using technologies that would provide most of the required functionality out-of-the-box was required if we were to complete anything remotely useful inside the two day time period.
One of the most flexible application frameworks that supports most of our requirements out-of-the-box is Django.
Django is the core of a rich ecosystem of technologies that provide the flexibility that we needed. Core Django supports, out-of-the-box, an object relational mapper (ORM) that can use any number of the most popular database backends. I used SQLite for our project, a built in development web server that allowed us to develop and debug everything quickly. A rich middleware that supports caching, user management etc, a full blown templating engine, a built in administration interface for our database backend and finally wsgi support out of the box, that will allow us to use any production level web server for our deployment.
A screenshot of Got Milk backend administration interface
Even though Django supports many of the requirements out of the box there are a few requirements missing. One thing to know about Django is that it is part of the python ecosystem. Python ecosystem uses the pip python package manager to provide access to a huge amount of python packages and thus enhance the capabilities of any python project with just a few commands.
Pip is the python package manager. Through Pip, we can query, install, uninstall and upgrade packages from the huge collection provided by the python community. One more piece of functionality provided by pip is the ability to document and reproduce a setup with just a couple of commands. The pip freeze command will allow us to dump the existing set up of an environment into a text file, that can be subsequently loaded and replayed in any other python enabled environment with the command
pip -r install dependencies.txt
Using this approach we can easily reproduce our environment. Django as a native python project fully uses pip and its capabilities so there are at the moment a huge number of Pip enabled Django packages that enhance the core Django functionality, including Django core itself.
As if Django and Pip combination was not powerful enough, I have also used virtualenv and virtualenvwrapper to provide development environment isolation. These two powerful tools allow me to create and manage multiple isolated python environments in one single machine allowing thus to install any python modules we need without affecting the system-wide python module installation or any other project developed on the same machine.
We have now a solid framework on which we will base our web service. According to our requirements, we needed to provide a platform-agnostic interface for our data producers and data consumer agents. This need is best served by designing a REST interface that will be consumed by our agents according to their needs. One of the most prominent REST Django packages is Tastypie.
Tastypie is a web service API framework for Django. It provides a convenient yet powerful and highly customizable abstraction for creating REST-style interfaces.
Tastypie makes exposing your Django models easy but gives you full control over what you expose; this lets you abstract away the database as much as needed. Tastypie is of course provided as a Pip package and that is how it was installed.
Using Tastypie allowed us to create our platform agnostic REST API and link together our agents providing maximum flexibility. There are, however, a number of other important technologies used during our application development. One of the most important ones is South.
South is one of the must-have tools in any Django developer’s toolkit. It is a tool that allows us to modify and migrate our database schema and data. During a Django project development we usually define our tables in a number of Django-ORM oriented classes called models in Django terminology. These are used by Django to create the database tables in our RDBMS of choice. At the moment, Django supports natively only the creation of these tables; it does not provide any way to change your models and automatically apply the changes in the database backend.
This is where South comes to the rescue and provides the missing functionality of data and schema migrations.
Having linked our data creator and consumer agents, and having provided a way for everyone to communicate with each other, we thought that it will be a nice idea to provide some kind of historical data representation since we have now a full dataset of events. Django-chartit is a Django module that can be used to create interactive charts effortlessly. It uses the HighChart JS library and JQuery.
Our app creates a huge number of events so plotting all these events, even on a daily basis, would not be very convenient so we needed to perform some kind of data transformation. Our Got Milk real time chart functionality was born:
Now that we have everything working, including our chart capabilities, we need to make them production ready and even increase the portability of our application. We decided to use gunicorn as our production level web browser and Docker in order to containerize our app.
Gunicorn, is a python-based wsgi http server. It is one of the natural choices for any python-based wsgi enabled project. It is a high performance, production-ready HTTP server, that supports out of the box a number of worker backends, including synchronous and asynchronous workers, without needing any code changes. The asynchronous workers are based on python greenlets.
By using these technologies our load testing of the application revealed that our modest setup could serve millions of requests per day without problem. Further fine-tuning could be done to boost the performance of the app even more.
Docker has recently become very famous for its ability to isolate complicated environments, by creating really portable containers, that will work out-of-the-box on any docker-enabled environment. Guess what, they are right. By using Docker, we can reduce our Got Milk deployment phase even further to just one command.
Having everything up and running, we needed a way to connect our Got Milk infrastructure with our Philips Hue led bulbs and provide an optical warning system for low milk (and fruits) conditions.
Philips Hue provide their own REST API so all that we needed was a bridge between Got Milk REST API and Philips Hue REST API and a little bit of data manipulation.
We needed to decide our various states and the corresponding colours. After a little discussion and some helpful comments from our colleagues, we decided to use:
All these are once more implemented using python in a script that polls the Got Milk Server for changes and updates the state of our Philips Hue bulbs.
We loved taking part in the NCR CodeFest. From pitching ideas, picking teams, planning, execution through to the inevitable presentation it is such a great experience getting the full end-to-end experience of building a working prototype system in such a short space of time.
Working with people you don’t normally work with brings the chance to learn from new people. Getting the opportunity to work in new areas broadens your skill set. Working in such a short timeframe but yet still going through the entire process is liberating. It is very empowering to be allowed full freedom to work on whatever you please and develop it as you see fit. Of course it helps if you build something which can continue operating after the CodeFest has finished, as we managed with Got Milk.
For hackfests like these, the crazier the idea the better. It might not seem obvious what the benefits to the business are for running such an event. However, as I recall the variety of tools and techniques I learned in such a small space of time, the benefits are clear to me. Learn loads, code fast, have fun.
Alex Mordue
Tags: hackday, hardware, django, android, ridiculous, milk
August 25th 2014