Peanuts for the code monkey

Write code, commit to GitHub, get peanuts…

A few months ago I bought a mini gumball machine from the clearance section of Maplin – I think I paid about 4 for it. I wasn’t really sure what to do with it, so I just left it for a while. Then I had an idea: a bit like those animal behaviour experiments you sometimes see on TV, it would be cool to have a gumball machine that automatically rewarded me for doing work.

The thing I like about this project is that it’s a really minimal hack. I’m not aware of many — any? — Ardunio projects that use just one wire (you’d normally need at least two wires to complete a circuit for even a simple blinking LED project). There is no Arduino code to write because the project uses the Standard Firmata sketch that comes with every Arduino install.

In fact the only ‘work’ in this project was writing the Node code to query GitHub and control the Arduino pin.

If you’d like to make your own version of this project, here’s what you need…


For this project I used an Arduino Nano. It’s the smallest Arduino I’m aware of that has built-in USB. A Nano without pin headers can fit easily inside the base of the gumball machine. If it has pin headers you might have to bend some of them to get it to fit.

The Arduino USB connection needs to feed through the back of the gumball machine and out to the ‘host’ machine that you plan to run Node on. I used a drill and hacksaw to make a narrow slot in the back of the gumball machine. It’s pretty neat, but if you were going for perfection you could wire a USB port to the back of the gumball machine so that it can be mounted flush for a factory-fresh look.

The switching is really simple. Rather than control the motor directly (which would’ve required a relay, MOSFET, motor shield or similar) I relied on the electronics already contained in the gumball machine. The machine has a touch sensitive button which detects its small current being grounded by a person touching the button.

You can achieve the same effect by using the internal pull-up resistors on an Arduino. Simply cut the wire that is going to the button and connect it to one of the digital pins on the Arduino. I used a short female jumper cable to make it a solderless hack, but you could just as easily solder the wire straight on to one of the Arduino pins.

All that remains on the gumball side is to write the StandardFirmata sketch onto the Arduino board. It’s in File > Examples > Firmata within the Arduino IDE.

The Node.js script

The node script is available on GitHub and here…

 * Peanuts for the code monkey
 * by Grant Gibson
 * (standing on the shoulder of giants - jgautier / firmata)
 * Dispenses peanuts - or the snack of your choice - in exchange for X lines of code added to a GitHub repo

console.log('Peanuts for the codemonkey starting...');

var ledPin = 13;
var buttonPin = 12;
var lastFeed = 0;
var linesBetweenFeed = 10;
var checkFrequency = 120; // in seconds - if you set to less than 60 you'll hit the API rate limit of 60/hr

var ghUser = "grantgibson";
var ghRepo = "peanuts-for-the-codemonkey";

var https = require('https');
var firmata = require('../lib/firmata');

var board = new firmata.Board('/dev/tty.usbserial-AD01Z6TO', function(err) {
    if (err) {

    console.log('Firmware: ' + + '-' + board.firmware.version.major + '.' + board.firmware.version.minor);

    var ledOn = true;
    board.pinMode(ledPin, board.MODES.OUTPUT);
    board.pinMode(buttonPin, board.MODES.INPUT);



function checkRepo(){

	var url = '' + ghUser + '/' + ghRepo + '/stats/code_frequency';

	https.get(url, function(res) {
	    var body = '';

	    res.on('data', function(chunk) {
	        body += chunk;

	    res.on('end', function() {
	        var ghResponse = JSON.parse(body);
	        var linesThisWeek = 0;

	        if(ghResponse && ghResponse.length && ghResponse[ghResponse.length-1].length) {
		        linesThisWeek = ghResponse[ghResponse.length-1][1];
		        console.log("GitHub says... " + linesThisWeek + " lines added this week.");
		    } else {
		    	if (ghResponse && ghResponse.message && ghResponse.message.indexOf("rate limit exceeded" == -1)) {
		    		// Rate limit exceeded for now, don't try again
		    		console.log("Rate limit exceeded");
		    		return false;
		    	// GitHub typically returns an empty response for the first request if it isn't cached
		    	console.log("No response this time, trying again.");
		    	setTimeout(checkRepo, 2000);
		    	return false;

	        if(linesThisWeek < lastFeed) {
	        	// Lines added are less than the last time we checked - new week?  If so, reset counter.
	        	lastFeed = linesThisWeek;

	        if(lastFeed == 0) {
	        	console.log("Free start up bonus:");

	        var linesAdded = linesThisWeek - lastFeed;

	        if(linesAdded >= linesBetweenFeed) {
	        	console.log("Added " + linesAdded + ", so feed the monkey...");
	        	// Light the LED
	        	board.digitalWrite(ledPin, board.HIGH);
	        	// Pull the trigger pin high (or low?)
	        	board.digitalWrite(buttonPin, board.HIGH);

	        	// Set pins off again
	        		board.digitalWrite(ledPin, board.LOW);
	        		board.digitalWrite(buttonPin, board.LOW);
	        	}, 1000);

	        	// Update the lastFeed data
	        	lastFeed = linesThisWeek;
	        } else {
	        	console.log("Monkey write more code!");
	}).on('error', function(e) {
	      console.log("Got error: ", e);