├── README.md ├── blink.js ├── light-switch.js ├── multi-light-switch.js ├── package.json └── server.js /README.md: -------------------------------------------------------------------------------- 1 | raspberry-pi-home-automation 2 | ============================ 3 | A node.js based home automation system based around the Raspberry Pi. For background around this project: 4 | 5 | [![The Well Tempered Hacker](http://img.youtube.com/vi/SEAQVXHSwg4/0.jpg)](http://www.youtube.com/watch?v=SEAQVXHSwg4) 6 | 7 | Installation 8 | ============ 9 | To get started, clone the repository and install the required dependencies. 10 | 11 | git clone https://github.com/anders94/raspberry-pi-home-automation.git 12 | cd raspberry-pi-home-automation 13 | npm install 14 | 15 | Hardware 16 | ======== 17 | The Raspberry Pi needs a little bit of circuitry to protect and amplify its GPIO 18 | ports. Here's schematics of the circuits I created for this project. 19 | 20 | Protected Pull-Up Switch 21 | ------------------------ 22 | If you want to read from the GPIO pins on the Raspberry Pi, you shouldn't just 23 | switch the pin between 3.3v+ and nothing. Rather, you should either pull it up 24 | to 3.3v+ through a small (1k) resistor or drain it down to ground through a 25 | larger (10k + 1k = 11k) resistance. 26 | 27 | Protected Pull-Up Switch Circuit 28 | 29 | Relay Driver 30 | ------------ 31 | The GPIO pins on the Raspberry Pi run at 3.3v which isn't really enough to 32 | solidly throw relays. (in my case, I'm using a solid state relay but the 33 | theory is the same) This circuit uses an NPN transistor to amplify the 3.3v 34 | GPIO output to a 5v output which is enough to throw the relay. 35 | 36 | Protected Pull-Up Switch Circuit 37 | 38 | Server 39 | ====== 40 | The server uses MQTT, a lightweight messaging channel over TCP, and presents a 41 | pub-sub like interface to clients. Clients connect and can publish messages which 42 | get copied to all other connected clients. 43 | 44 | To start the server: 45 | 46 | node server 47 | 48 | It will connect and listen to 0.0.0.0:1883 by default. 49 | 50 | Clients 51 | ======= 52 | Clients read from and optionally write to the GPIO pins on the Raspberry Pi. Light 53 | switches are directly attached to 3.3v GPIO pins pushing them either high or low. 54 | Solid state relays to switch 120v AC loads are driven via 3.3v GPIO pins which are 55 | up-converted to 5v with a transistor. (3.3v isn't quite enough to solidly switch the 56 | Sharp S216S02 solid state relays I'm using) You may need to access GPIO pins as root 57 | depending on how you have things set up. 58 | 59 | watch.js 60 | -------- 61 | Fires a callback when the GPIO pin state changes. Use this to test GPIO input 62 | functionality. 63 | 64 | blink.js 65 | -------- 66 | Blinks GPIO pins on and off for 5 seconds so you can get your SSR setup working. 67 | 68 | light-switch.js 69 | --------------- 70 | Simple on or off lightswitch example, linking input and output. It does exactly 71 | what you think it does. 72 | 73 | multi-light-switch.js 74 | --------------------- 75 | Connects to the server (IP is manually set in the source) via MQTT and 76 | publishes light switch events. It works as a four position lightswitch 77 | for two lights. See the video for a demonstration of this in action. 78 | 79 | TODO 80 | ==== 81 | 82 | Publish the SPI code and circuit schematic for the 120vAC current sensing capability. 83 | -------------------------------------------------------------------------------- /blink.js: -------------------------------------------------------------------------------- 1 | var Gpio = require('onoff').Gpio, 2 | led1 = new Gpio(23, 'out'), 3 | led2 = new Gpio(24, 'out'), 4 | iv, 5 | count = 0, 6 | state = 1; 7 | 8 | iv = setInterval(function() { 9 | led1.writeSync(state); 10 | if (state) 11 | state = 0; 12 | else 13 | state = 1; 14 | led2.writeSync(state); 15 | count = count + 1; 16 | }, 500); 17 | 18 | setTimeout(function() { 19 | clearInterval(iv); 20 | console.log("count " + count); 21 | led1.writeSync(0); 22 | led1.unexport(); 23 | led2.writeSync(0); 24 | led2.unexport(); 25 | }, 5000); 26 | -------------------------------------------------------------------------------- /light-switch.js: -------------------------------------------------------------------------------- 1 | var Gpio = require('onoff').Gpio, 2 | button = new Gpio(14, 'in', 'both'), 3 | led = new Gpio(23, 'out'), 4 | state = 0; 5 | 6 | function setlight(value) { 7 | console.log('Button pressed!, its value was ' + value); 8 | led.writeSync(value); 9 | button.watch(function (err, value) { 10 | if (err) throw err; 11 | if (state) 12 | state = 0; 13 | else 14 | state = 1; 15 | setlight(state); 16 | }); 17 | } 18 | 19 | setlight(0); 20 | 21 | //setTimeout(function() { 22 | // led.writeSync(0); 23 | // led.unexport(); 24 | // button.unexport(); 25 | //}, 10000); 26 | -------------------------------------------------------------------------------- /multi-light-switch.js: -------------------------------------------------------------------------------- 1 | var mqtt = require('mqttjs'), 2 | Gpio = require('onoff').Gpio, 3 | button = new Gpio(14, 'in', 'both'), 4 | led1 = new Gpio(23, 'out'), 5 | led2 = new Gpio(24, 'out'), 6 | state = 1; 7 | 8 | var serverIP = '192.168.1.245'; 9 | 10 | mqtt.createClient(2883, serverIP, function(err, client) { 11 | if (err) process.exit(1); 12 | 13 | function setLight() { 14 | console.log('switch ' + state); 15 | led1.writeSync(state % 2 ? 0 : 1); 16 | led2.writeSync(state % 3 ? 0 : 1); 17 | client.publish({topic: 'switch state', payload: JSON.stringify(state)} ); 18 | button.watch(function (err, value) { 19 | if (err) throw err; 20 | state++; 21 | if (state > 3) 22 | state = 0; 23 | setLight(); 24 | }); 25 | } 26 | 27 | client.connect({keepalive: 3000}); 28 | console.log('connect'); 29 | 30 | client.on('connack', function(packet) { 31 | if (packet.returnCode === 0) { 32 | console.log('connected '+JSON.stringify(packet)); 33 | } 34 | else { 35 | console.log('connack error %d', packet.returnCode); 36 | process.exit(-1); 37 | } 38 | }); 39 | 40 | client.on('close', function() { 41 | process.exit(0); 42 | }); 43 | 44 | client.on('error', function(e) { 45 | console.log('error %s', e); 46 | process.exit(-1); 47 | }); 48 | 49 | setLight(); 50 | }); 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ncs" 3 | , "description": "Node Control Server - MQTT server for collecting info from clients" 4 | , "version": "0.0.1" 5 | , "private": false 6 | , "dependencies": { 7 | "express": ">=3.11.0" 8 | , "jade": ">= 0.0.1" 9 | , "redis": ">= 0.7.1" 10 | , "socket.io": ">= 0.8.7" 11 | , "mqttjs": ">= 0.1.7" 12 | , "onoff": ">= 0.1.0" 13 | , "spi": ">= 0.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var mqtt = require('mqttjs'); 2 | var readline = require('readline'); 3 | var port = 1883; 4 | 5 | var rl = readline.createInterface({ 6 | input: process.stdin, 7 | output: process.stdout 8 | }); 9 | 10 | mqtt.createServer(function(client) { 11 | var self = this; 12 | 13 | process.stdin.resume(); 14 | process.stdin.setEncoding('utf8'); 15 | 16 | if (!self.clients) self.clients = {}; 17 | 18 | client.on('connect', function(packet) { 19 | console.log('connection from '+packet.client); 20 | client.connack({returnCode: 0}); 21 | client.id = packet.client; 22 | self.clients[client.id] = client; 23 | }); 24 | 25 | client.on('publish', function(packet) { 26 | console.log('got ['+packet.topic+' | '+packet.payload+']'); 27 | }); 28 | 29 | client.on('subscribe', function(packet) { 30 | console.log('subscribe '+packet.subscriptions); 31 | var granted = []; 32 | for (var i = 0; i < packet.subscriptions.length; i++) { 33 | granted.push(packet.subscriptions[i].qos); 34 | } 35 | 36 | client.suback({granted: granted}); 37 | }); 38 | 39 | client.on('pingreq', function(packet) { 40 | console.log('pingreq'); 41 | client.pingresp(); 42 | }); 43 | 44 | client.on('disconnect', function(packet) { 45 | console.log('disconnect'); 46 | delete self.clients[client.id]; 47 | client.stream.end(); 48 | }); 49 | 50 | client.on('close', function(err) { 51 | console.log('close'); 52 | delete self.clients[client.id]; 53 | }); 54 | 55 | client.on('error', function(err) { 56 | console.log('error'); 57 | client.stream.end(); 58 | util.log('error!'); 59 | }); 60 | 61 | rl.on('line', function (cmd) { 62 | console.log('sending ['+cmd+']'); 63 | for (var k in self.clients) { 64 | self.clients[k].publish({topic: "msg", payload: cmd}); 65 | } 66 | }); 67 | 68 | }).listen(port); 69 | --------------------------------------------------------------------------------