Over the holidays I got involved with the Cheerlights project by ioBridge Labs. The basic premise is that if you tweeted a colour to @cheerlights, it would appear on a series of feeds they were hosting, enabling all sorts of synchronised physical and virtual christmas light gizmo’s to be constructed. Coming latish to the game, I spent a happy afternoon on Christmas Eve knocking up a version in Processing. [all code here on GitHub.]
Background image.
Sketch 1, which just consisted of a simple triangular tree, and one bauble showed that drawing an interesting tree was going to be a real pain, not to mention rather slow. So I went in search of a tree image I could use as a background onto which I could simply place the baubles and snow. Luck led to the fine image above on the internet which was ideal. Now I could put it in a simple sketch.
Note: I needed a way to get the baubles positions. To avoid calculating it, I temporarily added the mousePressed() method and printed out the mouseX/Y co-ordinates to the debug screen while clicking on the right parts of the tree which I then cut and pasted back into the sketch.
Baubles.
I created a class of Bauble as an ArrayList of Points. Points is an inner class to handle the display of a particular bauble. There’s not much to Bauble; an add() method to add a Point and a display() method to run through the array and show the Points. They in turn have a show() method with a little bit of extra code to add a basic ‘twinkle’:
void show() { int w = 10; int h = 10; if (frameCount % (int) random(4,10) == 0) { x = (int) random(4,10); y = (int) random(4,10); } ellipse(x,y,w,h); }
What this does is use the frame count to sure all the points don’t alter at the same time and each time one does it gets a random size. The effect is ok; but not quite like the real thing.
Snow.
SnowFlakes is again a class, with an inner called Flake. Flake creates a ellipse() of a random size and position on the screen. To make it look realistic each flake drifts slightly across the screen. SnowFlakes creates a (300) Flake array and then the fall() method runs through the array calling each Flake’s update() method. Again nothing too interesting apart from a little bit of code to add a slight drift depending on size of flake and slightly different speeds for each fall :
void update() { ellipse(xPosition, yPosition, flakeSize, flakeSize); //make them drift at slightly different speeds depending on size if(direction == 0) { xPosition += flakeSize * 0.1; } else { xPosition -= flakeSize * 0.1; } // falls between 0.7 and 100% yPosition += (flakeSize * fall) + direction; //reset if off the screen if(xPosition > width + flakeSize || xPosition < -flakeSize || yPosition > height + flakeSize) { xPosition = random(0, width); yPosition = -flakeSize; } }
At that point it was going pretty well. I’d some baubles and some mostly-realistic snow. The next step was to hook it up to the @cheerlights system. Initially, I was going to use the API text file to get the latest colour, until I realised from Twitter that the excellent @andysc had an MQTT version up as well.
MQTT+CheerLights
Now I’d done a previous blog post almost exactly a year ago about hooking up Processing and MQTT. Sill not updated to the latest library from IA92, but pretty sure it would still work, I was able to simply hook up that library to my new code to get the latest colour. The MQTTLib pde does the heavy lifting with a callback to send the new value to the Bauble class which gained a change() method. A bit of copy-paste to drop in the MessageHandler and add the right paths for the Cheer Lights MQTT broker/topic later I’d a working prototype which looks like so (support classes excluded).
//MQTT import com.ibm.mqtt.MqttSimpleCallback; private MQTTLib m; private String MQTT_BROKER ="tcp://test.mosquitto.org:1883"; private String CLIENT_ID = "Tingenek23"; private int[] QOS = {0}; private String[] TOPICS = { "cheerlights"}; private boolean CLEAN_START = true; private boolean RETAINED = false; private short KEEP_ALIVE = 30; PImage bg; Baubles baubles; SnowFlakes snowflakes; void setup() { size(640,480); bg = loadImage("snowtree.jpg"); m = new MQTTLib(MQTT_BROKER, new MessageHandler()); m.connect(CLIENT_ID, CLEAN_START, KEEP_ALIVE); m.subscribe(TOPICS, QOS); frameRate(30); noStroke(); smooth(); baubles = new Baubles(); snowflakes = new SnowFlakes(300); } void draw() { background(bg); baubles.show(); snowflakes.fall(); } void mousePressed() { baubles.add(mouseX,mouseY); println(mouseX+","+mouseY); } private class MessageHandler implements MqttSimpleCallback { public void connectionLost() throws Exception { System.out.println( "Connection has been lost." ); //do something here m.connect(CLIENT_ID, CLEAN_START, KEEP_ALIVE); m.subscribe(TOPICS, QOS); } public void publishArrived( String topicName, byte[] payload, int QoS, boolean retained ){ String s = new String(payload); //Display the string println("New colour is " + s); baubles.change(s); }
We’re not done yet though. This works fine as a Java applet, but the latest Processing also supports an Android mode. Now you have to set this up, but once done, simply connecting up a phone, changing to Android mode and re-running, and I’ve got a version compiled onto my phone as well. [Remember to change ClientID before you run on your phone to avoid conflict with your desktop version]
Enjoy and have a great 2014!