Search This Blog

Tuesday 22 December 2015

Raspberry Pi controlled Christmas lights

So, this year I decided to fix a bunch of lights to the outside of my house, something like this:


(except that my house isn't actually a badly Photoshopped garage image of course, but you get the idea).  It's been a pretty successful project, but I figured I could add a little Pi to make it more interesting, so I decided to add a pretty basic internet based power toggle feature.



Hardware bits:


I wanted to keep things straightforward so I simply cut one of the wires between the power/controller "brick" and added the switching side of a relay in series, and then used a Pi A+ to control the relay.

I didn't want to get into switching 240v so the LED power supply stays on all the time.


The eventual build looks like this:

I tried switching the relay from an interactive Python terminal on the Pi and found the hardware worked pretty well, so I then focused on the web bit.  The cable tidy blocks were left over from attaching the lights to the outside of the house.
Product Details
I used a relay board similar to this one.  The Pi 3v and Ground are connected to the obvious places and pin 17 to the other input.  Confusingly, the relay is active when there is 0v on pin 17, although the switched side of the relay has NO and NC options anyway...

The choice of pin 17 was entirely arbitrary, other pins also work.

Python bits:

I decided to use a Python Flask web server having learned the basics from the "Flask Mega Tutorial" (http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world), and pieced together some other bits to make it all go.


The run.py file, config to run on port 80 from any IP:

#!/usr/bin/env python
#!flask/bin/python
from app import app
app.run(debug=True, host='0.0.0.0', port=80)


The __init__.py with added GPIO:

from flask import Flask
import RPi.GPIO as GPIO

app = Flask(__name__)
from app import views

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)

The GPIO bits simply setup the Pi to talk to the relay board.  The warning suppression is handy for debugging where the Flask server is being repeatedly restarted but the GPIO is already initialised.


The views.py file:

from app import app
from flask import request
import RPi.GPIO as GPIO
from flask import render_template

@app.route('/xmas15/api/v1.0/lights', methods=['PUT'])
def lights_on():
        GPIO.output(17, 0)
        return '{lights: on}'

@app.route('/xmas15/api/v1.0/lights', methods=['DELETE'])
def lights_off():
        GPIO.output(17, 1)
        return '{lights: off}'

@app.route('/lights')
def jsLights():
        return render_template('lights.html')


The two methods required to toggle the state are exposed as REST style URLS.  These can be successfully called using CURL from the terminal.  The actual web interface is simple jQuery so is contained in a single template file with no parameters and exposed on the /lights URL


The lights.html template file:

<html>
<head>
<title>Xmas Lights 15</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>

$(function(){
$("#onBtn").click(function(){
var b = $("#onBtn");
b.animate({opacity: '0.4'}, "fast");
$.ajax({url: "http://mydomain.co.uk/xmas15/api/v1.0/lights", type: "PUT"});
b.animate({opacity: '1'}, "fast");
});

$("#offBtn").click(function(){
var b = $("#offBtn");
b.animate({opacity: '0.4'}, "fast");
$.ajax({url: "http://mydomain.co.uk/xmas15/api/v1.0/lights", type: "DELETE"});
b.animate({opacity: '1'}, "fast");
});
});
</script>
<style>
body{
background: #111111; 
color: black; 
font-family: sans-serif;
}
div{
width: 400px; 
height: 400px; 
display: inline-block; 
font-size: 3em; 
text-align: center;
}
#onBtn{background: yellow}
#offBtn{background: gray}
</style>
</head>
<body>
<div id="onBtn">on</div>
<div id="offBtn">off</div>
</body>
</html>


The two DIVs have click event handlers bound to them and then simply make parameterless calls to the REST URLs.  This isn't particularly elegant, but it gets the job done!

Linux bits

I used the following command line to run the Python script after the terminal session expires:

pi@raspberrypi ~/xmas15/app $ nohup sudo ~/xmas15/run.py &

I need to add something that runs this as a service (I have some bash somewhere I think...), but that can wait for another day.

The following crontab commands enable me to schedule on and off times:

00 23 * * * curl --silent curl -X DELETE localhost/xmas15/api/v1.0/lights
00 16 * * * curl --silent curl -X PUT localhost/xmas15/api/v1.0/lights

Firewall bits

I opened TCP80 and added a NAT rule to my Pi





There are some other lights on the house too, so perhaps next year I'll order a multi-relay board and look into wiring them to it too.

No comments:

Post a Comment