A connected Christmas Tree using an Arduino, a RapsberryPi and WebSockets

Screen Shot 2014-12-23 at 1.20.44 PM

It’s not too late to make your Christmas Tree connected to the Internet and let your friends control the lights over the web! There are many similar implementations online (even in public spaces) and code samples, but I would like to share my implementation based on:

  • An Arduino with 3 Grove relay switches that control 3 christmas light strips
  • A RaspberryPi that controls the Arduino over the Serial Port based on commands from the Christmas Tree Website
  • A WebSocket server that synchronises the light status among the website and the RaspberryPi
  • A WebCam that streams the feed from the Christmas Tree to a webpage (through MJPEG)

I have selected this setup because I wanted to install the tree in the office and had 2 requirements to meet:

  • Lack of non portal-based WiFi authentication (so no WiFi shields, Flyport, SparkCore or Tessel-based solution). Also, WiFi sometimes can be unstable so had to go with a more reliable solution, like Ethernet
  • Since the website controlling the lights would be public, I had to make it as much secure as possible. So thus a 3-tier architecture with a separate WebSocket server synchronising the light status between the Rpi and the website. In addition, since the WebCam is connected to the local intranet, the most secure way to get the live feed was to post image frames to a webserver, instead of accessing the stream directly from the web.

Presentation1

A Node.JS script running on the RaspberryPi takes care both of the light control and synchronisation as well as the WebCam feed update:


"use strict";

var ArduinoFirmata = require('arduino-firmata');
var arduino = new ArduinoFirmata();

var FormData = require('form-data');
var fs = require('fs');
var http = require('http');
var cam = require('foscam');

cam.setup({

host: 'IP.OF.WEBCAM',
port: 80,
user: 'admin',
pass: ''
})

var WebSocket = require('ws');
var ws = new WebSocket('ws://IP.WEBSOCKET.SERVER:PORT');

var relay1 = 4;
var relay2 = 3;
var relay3 = 2;
arduino.connect('/dev/ttyACM0');

arduino.on('connect', function(){

console.log("board version"+arduino.boardVersion);

//initialize the lights to ON
arduino.digitalWrite(relay1, true, function callback(){});
arduino.digitalWrite(relay2, true, function callback(){});
arduino.digitalWrite(relay3, true, function callback(){});

//send light status to WebServer for synchronizing all web clients
ws.send('{"light1":"ON", "light2":"ON", "light3":"ON"}');

ws.on('open', function open() {
//ws.send('something');
//reset:

});

ws.on('message', function(data, flags) {
var obj = JSON.parse(data);
if(obj.light1 == "ON") {
arduino.digitalWrite(relay1, true, function callback(){});
}
if(obj.light1 == "OFF") {
arduino.digitalWrite(relay1, false, function callback(){});
}
if(obj.light2 == "ON") {
arduino.digitalWrite(relay2, true, function callback(){});
}
if(obj.light2 == "OFF") {
arduino.digitalWrite(relay2, false, function callback(){});
}
if(obj.light3 == "ON") {
arduino.digitalWrite(relay3, true, function callback(){});
}
if(obj.light3 == "OFF") {
arduino.digitalWrite(relay3, false, function callback(){});
}

//upload CAM image to webserver
uploadImage();
});

});

setInterval(uploadImage, 1000);
function uploadImage() {

//console.log('capturing');

//get snapshot from CAM
cam.snapshot('image.jpg', nothing);
//upload it to the WebServer using a POST request
var form = new FormData();
form.append('photo', fs.createReadStream('image.jpg'));

var http = require('http');

var request = http.request({
method: 'post',
host: 'xxx.domain.com',
path: '/path/imagehandle.php',
headers: form.getHeaders()
});

form.pipe(request);

request.on('response', function(res) {
//console.log(res.statusCode);
});

}
function nothing(data) {

}

The code first connects to the attached Arduino board (running the standard firmata sketch) and initialises the lights by turning the on.

It opens a WebSocket and listens for messages from the server and changes the lights status accordingly, by turning On/Off the digital pins that control the relay switches connected to the lights. Also, every 1 second it grabs an image frame from the webcam and uploads it to a WebServer via a POST request and a PHP script.

On the (web) client side, a WebSocket connection is initiated to the same WebSocket server for synchronising 3 buttons (checkbox elements made to look like switches using proper CSS) and also fetches every 1 second the WebCam image, uploaded to the server.

You can find the complete source code here.

Leave a Reply

Your email address will not be published. Required fields are marked *

*