Introduction: Home Automation With Arduino... and JavaScript!
In this instructable (my first one actually), I'll guide you to make automation in your home with Arduino, yeah, not so new... But we will also include JavaScript, using Node.js and an awesome library to interact with Arduino called johnny-five (yes, as the little robot from the movie).
The automation is focused on controlling lights (some with LEDs and other with a bulb, also, one of them is dimmable), and a fan. You might know that turning lights and a fan on and off with a physical button is not actually ~automation~, that's why we are going to use a photoresistor, a temperature sensor, and also a web server with an user interface to control and monitor all together. Let's start!
It's important to say that this instructable is focused on the SOFTWARE side, not the hardware, so you need some basic electronics knowledge to build the circuit, but don't worry, it's really easy, this is an easy project, we will take advantage of the facility to use JavaScript with Arduino.
You can download my own version of this project from GutHub Download!
Step 1: What You'll Need
Physical:
- Arduino (UNO used with this project, but with some adjustments, almost any developer board will work)
- LEDs (as much as you want to control)
- Push buttons (at least three)
- TIP41 or TIP42 (at least one)
- Relay (at least one)
- Photoresistor (at least one)
- Temperature sensor (on this project I used TMP36, but many can work well)
Non-physical:
- Arduino IDE
- Sublime Text (or any text editor of your preference)
- Node.js
- Johnny-five JS
- Socket.io
- p5.js
- bootstrap
We need to set up Arduino to make it work well with this project, we'll see how in the next step.
Step 2: Getting Arduino Ready
As you may know, Arduino works with the sketch you upload to it, normally you write the code on the Arduino IDE, compile it, and upload it to the board, but with this Instructable you will run on live code with the board getting and posting data in two ways, so we need to set up the Arduino to be able to do it.
Open your Arduino IDE and an example named StandardFirmata, upload it to your board and that's it! Your Arduino is ready to interact with your computer via JavaScript.
We will work first on the server side, getting all the environment ready to interact between Arduino and the browser, so let's go to the next step and configure all.
Step 3: Getting Ready From the Server
First we need to start with a folder dedicated to the project, so, in your command line do this:
mkdir myRockingProject && cd myRockingProject #name it as you want npm init #to work with node mkdir public #here we will put the client (browser) stuff
You can download the package.json file I attached, put it on your project folder and in your command line run:
npm install
Then, create a file named server.js, we will put all our serverside stuff here, this is the main file we want to work with, because here is all the communication between node.js and Arduino.
If you created your own package.json with npm init, we will need to add the node modules that let us work well on the environment, so let's run:
npm install --save express johnny-five socket.io
That will install and let you work with the mentioned modules (express j5 and socket.io), you will be able to see a change on your package.json file including the following:
"dependencies": {<br> "express": "^4.13.4", "johnny-five": "^0.9.43", "socket.io": "^1.4.5" }
Note: We will not use socket.io right now, but we installed it to get it ready when the time comes.
Now, in our server.js file, we will call the modules to work with, first we need to use express, this will let us route the client calls to our files and interact with it and the server, so let's create our server:
var express = require('express'); // Calling the module var app = express(), // Creating an express 'app' server = app.listen(3000); // Telling the server to listen on port 3000 (localhost:3000) app.use(express.static('public')); // We tell our app (express, to serve the static files located on the 'public' folder
Our server is ready to listen to the client requests and serve information to it, but we still with nothing to serve nor get to, and we don't either have communication with the Arduino.
The next thing is to setup the Arduino-server communication, we will first setup it on the server, so, with the help of Johnny-five library, a powerful JavaScript-Arduino bridge to enable controlling the board directly with JavaScript, we will set all we need to make our automation happen!
In the same file we were working (server.js) we will write the code we would otherwise write on the arduino IDE, so let's write the following:
// Setting up johnny-five var five = require("johnny-five"), arduino = five.Board(); //////////////////////////////// VARIABLES //////////////////////////////// var living_room_light = false, other_rooms_light = false, fan = false, backyard_light = false; // Helpers var living_room_button, other_rooms_light_button, backyard_light_button; // Buttons pins var living_room_light_pin_led, other_rooms_light_pin_led, fan_pin, dimmable_led; // LEDs pins var backyard_light_pin; // Relay pin var photoresistor; // Light sensor var temperature; // Tmp sensor //////////////////////////////// BOARD //////////////////////////////// arduino.on("ready", function() { //////////////////////////////// DIMMABLE LED //////////////////////////////// dimmable_led = five.Led(6); //////////////////////////////// LIVING ROOM //////////////////////////////// //Initialize pushbutton for living room at digital input 2 living_room_button = five.Button(2); // Pin 13 is used to set living room light, analog input A0 is used to check light intensity from a photoresistor photoresistor = new five.Sensor("A0"); living_room_light_pin_led = new five.Led(13); living_room_light_pin_led.off(); // Check if photoresistor gets less than a half of light available and change living room light if applicable photoresistor.on('change', function() { if(this.scaleTo([0, 100]) < 60){ living_room_light = !living_room_light; living_room_light_pin_led.on(); console.log('photoresistor-change'); } }); // Changes living room light when pushbutton is pushed living_room_button.on("release", function () { living_room_light = !living_room_light; living_room_light_pin_led.toggle(); console.log('living-room-light-pushbutton'); }); //////////////////////////////// OTHER ROOMS //////////////////////////////// // All rooms excepting the living room are simultaneously light powered on manually other_rooms_light_button = five.Button(4); // Light is powered via pin 12, LEDs connected in parallel other_rooms_light_pin_led = new five.Led(12); // Change light state whenever 'other_lights_button' is pressed then released other_rooms_light_button.on("release", function () { other_rooms_light = !other_rooms_light; other_rooms_light_pin_led.toggle(); console.log('other-rooms-change'); }); //////////////////////////////// FAN CONTROLLING WITH TEMPERATURE MEASURING //////////////////////////////// // Temperature will be measured with a TMP36 sensor temperature = new five.Thermometer({ controller: "TMP36", pin: "A1", freq: 2000 }); // TIP42 transistor is attached to pin 5 fan_pin = new five.Pin(5); // Whenever temperature provided by LM35 sensor is greater than 22° C the fan input changes its value to 'high' and when temperature is less or equal to 22° C it goes 'low' temperature.on("data", function () { console.log('temperature: ' + this.celsius.toFixed(2)); if(this.celsius > 24.00) { if(fan) { fan_pin.high(); fan = !fan; console.log("Temperature is: "+this.celsius.toFixed(2)+", fan is on"); } } else if(this.celsius < 24.00) { if(!fan) { fan_pin.low(); fan = !fan; console.log("Temperature is: "+this.celsius.toFixed(2)+", fan is off"); } } }); //////////////////////////////// BACKYARD LIGHT //////////////////////////////// backyard_light_button = new five.Button(8); // Relay to toggle the backyard light is attached to pin 9 backyard_light_pin = new five.Pin(9); // Check any pushbutton event to toggle the light backyard_light_button.on("release", function() { backyard_light = !backyard_light; if(backyard_light) { backyard_light_pin.high(); console.log("Backyard light is on"); } else { backyard_light_pin.low(); console.log("Backyard light is off"); } }); });
So far, we are ready to interact with arduino via our server, and we could just build our circuit, run the code and it would work, but where's the fun with that? Everywhere, circuits are awesome, but anyway the objective of this instructable is to interact with the arduino using a web user interface, so let's go to the next step and create our UI.
Attachments
Step 4: Client Side (the Browser)
We will now work with our /public folder, there, we will add the index of our app and the JS files that will make it dynamic, so let's go:
First, create a folder named 'assets', and inside it, create two more named 'lib' and 'styles', on the /lib folder, put bootstrap, jquery, and the p5 files, these will help us approach our objective, bootstrap to look smooth, and p5 and jquery to add custom functionality and a chart to track the house temperature.
Then, in the main folder (/public) create a file named index.html, you can check mine and paste it if you want, and after ending the instructable customize it for you and have fun!
After you have your index file there needs to be also two javascript files, one of them to control the interface with jquery, and another one to create a chart showing the temperature on real time. Also, we will start working with socket.io at this moment.
Socket.io is a powerful JS library to build realtime web applications, we will take advantage of it and use it to emit events from the Arduino-server to the client and viceversa, you can check the socket.io documentation here and there are also many examples on how to use it. Let's continue to our files previously mentioned.
One file will be called script.js and needs to contain the following:
$(function() {
var socket = io.connect("http://localhost:3000"); // Slider with jQuery UI $( "#slider-range-max" ).slider({ range: "max", min: 0, max: 255, value: 0, slide: function( event, ui ) { // Assign the slider value to the dimmable-led input $( "#amount" ).val( ui.value ); // Send the event to the server with the name and value of it socket.emit('dimmable-led', ui.value); console.log("Slider value: " + ui.value); } }); $( "#amount" ).val( $( "#slider-range-max" ).slider( "value" ) ); // Both this and the next ( $("#other-rooms-btn").click() ) change the calling action button state and emit the event via socket $("#living-room-btn").click(function() { changeBtnState("#living-room-btn", "#living-room-light"); socket.emit('living-room-light', $("#living-room-light").val()); console.log($("#living-room-btn").val()); }); $("#other-rooms-btn").click(function() { changeBtnState("#other-rooms-btn", "#other-rooms-light"); socket.emit('other-rooms-lights', $("#other-rooms-light").val()); console.log($("#other-rooms-btn").val()); }); // Checks for events sent from arduino to change the living room or every other rooms because of a pushbutton or photoresistor socket.on('living-room-light-pushbutton', function() { changeBtnState("#living-room-btn", "#living-room-light") }); socket.on('backyard-light-change', function(value) { if(value) { if($("#backyard-light").val() == "Off") { $("#backyard-light").val("On"); } } else if($("#backyard-light").val() == "On") { $("#backyard-light").val("Off"); } }); ///// I need to change this to handle the photoresistor only once per state ///// socket.on('photoresistor-change', function() { changeBtnState("#living-room-btn", "#living-room-light") }); socket.on('other-rooms-change', function() { changeBtnState("#other-rooms-btn", "#other-rooms-light") }) // One function to rule them all, well, the UI buttons... // btn: the button id to change ------ input: the input id to change function changeBtnState(btn, input) { var btnClass = $(btn).attr('class'); var text, state, newBtnClass, oldBtnClass; if(btnClass === "btn btn-success") { oldBtnClass = 'btn-success'; newBtnClass = 'btn-danger'; text = 'off'; state = "On"; } else if(btnClass === "btn btn-danger") { oldBtnClass = 'btn-danger'; newBtnClass = 'btn-success'; text = 'on'; state = "Off"; } $(btn).removeClass(oldBtnClass); $(btn).addClass(newBtnClass); $(btn).text("Turn " + text); console.log(btn + " is " + state); $(input).val(state); } });
Here we are handling the UI events (click's and a slider) and with them emitting messages via sockets that will be received on the server and will do Arduino work based on them.
In the other file, which we'll name 'temperature_canvas_sketch' we will display the data we got from the temperature sensor with the help of p5.js, a great JS library based on Processing lang. So in our temperature_canvas_sketch.js file let's add this:
var chartpoints = []; chartpoints.push({x: 0, y: 0}); var socket = io.connect("http://localhost:3000"); // Creating a canvas where the chart will be displayed and matching the connection with the socket function setup() { cnv = createCanvas(displayWidth / 2, displayHeight / 5); cnv.parent("termo-container"); // Gets a change whenever the temperature sensor changes and sets it to its element socket.on('temperature', function(temperature) { $("#termometer").val(temperature + "°C"); createPoint(temperature); }); } // Handle chart points to display function draw() { background(255); noFill(); stroke(0); // Here we draw the last temperature value from the chartpoints array where it is supposed to be //// Starts draw of point beginShape(); for (var i=0; i < chartpoints.length; i++) { var P = chartpoints[i]; vertex(P.x, height - P.y); text(P.y, P.x, height - P.y); //if (P.x<0)chartpoints.pop(i); P.x--; } endShape(); //// Ends draw of point } // This function is called whenever the tmp36 sends a new value to the client function createPoint(temp) { //var t = random(0, height-20); // Creates a new point with x -> live width of the canvas & y -> the temperature value from arduino var P = new Points(width, temp); chartpoints.push(P); } // Custom class of points that will be drawed var Points = function() { var x; var y; var constructor = function Points(x, y) { this.x = x; this.y = y; }; return constructor; }();
This will take care of draw a chart with the data we send, in this case is to show the live temperature of our home.
But now we have sockets on the client and not in the server, we need to go back there and add them to work properly, so go on.
Step 5: Sockets With the Server and Arduino Event Handling
Now we have our socket event handler / emitter on the client side, we need to make it work on server side, remember that we already installed socket.io module on the second step, so we only need to set it up adding the following lines to our server.js file:
var socket = require('socket.io');<br>// Creating a socket var io = socket(server); // Retrieving client info via socket when a new connection (only one for this project) is established io.sockets.on('connection', function(socket) { // Get dimmable light value from the UI and send it to the arduino socket.on('dimmable-led', function(value) { console.log('Dimmable LED value is now: ' + value); dimmable_led.brightness(value); }); // Living room and other rooms lights can be controlled via UI socket.on('living-room-light', function(state) { console.log('Living room light is: ' + state); living_room_light_pin_led.toggle(); }); socket.on('other-rooms-lights', function(val) { other_rooms_light_pin_led.toggle(); }); });
As you may see, we are now handling events from the client, retrieving messages and making the arduino react to them, dimming the LED, and turning on / off the living room and the other rooms lights.
After this, we need to emit events to the client to change the UI when getting data/changes from the arduino, so in our arduino code we will need to add and change some of the lines.
On the living room code:
photoresistor.on('change', function() { if(this.scaleTo([0, 100]) < 60){ living_room_light = !living_room_light; living_room_light_pin_led.on(); io.sockets.emit('photoresistor-change'); // this is new console.log('photoresistor-change'); } }); <br>
living_room_button.on("release", function () {<br> living_room_light = !living_room_light; living_room_light_pin_led.toggle(); io.sockets.emit('living-room-light-pushbutton', null); //this is new console.log('living-room-light-pushbutton'); });
On the other rooms code:
other_rooms_light_button.on("release", function () {
other_rooms_light = !other_rooms_light; other_rooms_light_pin_led.toggle(); io.sockets.emit('other-rooms-change'); console.log('other-rooms-change'); });
On the temperature measuring code just add the following line at the beginning of the callback on the .on("data",...) function:
io.sockets.emit('temperature', this.celsius.toFixed(2));
And on the backyard light code:
backyard_light_button.on("release", function() {<br> backyard_light = !backyard_light; if(backyard_light) { backyard_light_pin.high(); console.log("Backyard light is on"); io.sockets.emit('backyard-light-change', 1); //this is new } else { backyard_light_pin.low(); console.log("Backyard light is off"); io.sockets.emit('backyard-light-change', 0); //this is new } });
That's it, our code must work now, go to your command line and run the server
node server
And go in your browser to http://localhost:3000, you should see an UI as the one shown in the attached picture, being able to interact with your Arduino with the UI and vice versa.
I attached my own script.js file so you can take a look.
Step 6: Final Argument
I hope this instructable was clear for you all and that you will make it and apply yourself. I've got to say that all the code shown was for my particular case use, and this project was originally accomplished as a school subject project, but anyway I'm trying to apply it on my own house (wish me luck). Feel free (and I hope you will) to change the code as you needs are, if you have any doubt, or question you can comment below or contact me for more.
Future steps can be accomplished by you like:
- Adding sensors or relays to control more stuff like turning on and of your TV, the fridge, knowing if there's someone at the door before even knocking or ringing the bell
- Connect your Arduino to a raspberry pi to get your local server running all the time
- Creating an app to your phone with the help of some Node.js frameworks
- Humidity sensors to show you if your plants are hydrated
And many more!
Thank you all for reading and let's imagination run free!