Remember that late-90s Disney Channel Original Movie Smart House in which a young, motherless, computer-genius boy wins a fully automated robot-house, only to have the house-software come alive as an overbearing 50s-esque mother who locks the boy and his family indoors ‘for their own good’?
…maybe you don’t remember. But I do, and I have always wanted to build that. Except without the sexist/robot-takeover part (jeez Disney, what is your deal with mothers?).
Well, I didn’t quite do that. But, I did build a pretty neat device and web app for controlling my air conditioner.
Here’s a step-by-step guide to what I did.
Note, before starting this project, I’d never used a raspberry pi (I’d done just a tiny bit of Arduino), I’d never written a server, and I’d never made a web app of any kind. So, if you’re coming to this with just a bit of programming experience, take heart! This is a totally doable project.
Components of the finished project
- Raspberry Pi (I used this one)
- Power Switch (or just a relay)
- USB wifi for raspberry pi
- A micro SD card (if you are using RPi B+, otherwise check your specs)
- Digital temperature sensor
- Micro USB charger for the raspberry pi + wall adapter
- 4.7K resistor
- Jumper wires, both male and female Tip! this ebay seller is reliable and cheap
- Prototype Paper PCB (DIY circuit board)
- Your computer
- Soldering iron and solder
- A very very skinny phillips head screwdriver
- A hot glue gun
For testing/setup only
Set up the Pi
I cannot stress this strongly enough: Adafruit tutorials for allll the things! Their tutorials are super informative and easy to follow. Plus they also sell all the parts. It’s a one stop super shop for all things hardware.
If you already have a Raspberry Pi set up, or you feel comfortable getting started by going directly to Adafruit, downloading an OS image to your SD card, configuring, setting up wifi, etc, just continue with this post.
If you want some more help getting started, check out my companion blog post on getting your Raspberry Pi set up as a first-time user.
If you don’t want to read this whole post and just want to see the code I used, check out my github repo.
ac_ping.py contains the code running on the Pi, while the app folder contains everthing for the server and UI, including the Procfile for deploying on Heroku. You’ll have to change the URLs, secret key, and username password, I’ve put in place-holders.
Part I: The Hardware: building your circuit
Before I get started with this section of the post, I want to thank Dana Sniezko, a fellow Hacker Schooler and hardware genius, for helping me SO much with this project. Seriously, Dana, if you’re reading this, thank you for letting me bug you with questions for weeks and for teaching me all this awesome stuff.
Raspberry Pi – PowerSwitch (relay) – Air conditioner
As you can see in the diagram, this is the only part of the entire system that interacts with the air conditioner. Instead of connecting your air conditioner’s power plug directly into the wall socket, you will plug it into the PowerSwitch Tail II, and plug the other end of that into the wall. (Make sure to check your air conditioner’s electrical specifications, because the PowerSwitch can only handle up to 15 amps. Most ACs will draw far less than that, though, mine only draws about 5).
Now the cool part. Inside the black box that is now between your air conditioner and the wall socket there is a relay, which is just an electrically controlled switch. When you send power to the switch from one of your Raspberry Pi’s GPIO (general purpose input/output) pins, the switch will close and power will flow between the wall and the AC. When no power is flowing from the Pi to the switch, the switch will be open, and no power will flow between the wall and the AC.
Make sure that your AC is always set to be ‘on’, we’re bypassing the air conditioner’s normal on/off control system.
Let’s set that up. Consult the pinout map for your Raspberry Pi. I find it useful to look at one that shows a picture of the Pi, so you can make sure you’re oriented correctly.
- Make sure your Raspberry Pi is not plugged into power while you are working on this.
- Choose any pin labeled GPIO except GPIO4 (which we need to reserve for our temperature sensor). For example, on the map above, pin number 11, the 6th pin down in the left column is GPIO17.
- Take one of your jumper wires (a red one would be a good choice), and connect it to that pin.
- Now connect one of the male to male jumper wires to that wire.
- Find a pin labeled ground (GND) and connect another wire (black or grey would be a good choice), and again connect that to one of the male to male wires.
- Connect the power (wire from the GPIO pin) to 1: +in on the PowerSwitch, and your ground wire to 2: -in. Ignore the spot labeled 3:Ground on the PowerSwitch.
- You’ll notice that on the face of the power switch with labels, there are some holes with screws in them. Use a small screw driver to adjust these screws such that your jumper wires are secured in the powerswitch and cannot be tugged out.
- Note: while you are testing, you don’t need the power switch at all, just plug the wires into a breadboard and use an LED. If you can turn the light on and off, you can turn your AC on and off.
1import RPi.GPIO as io 2import time 3 4io.setmode(io.BCM) 5switch_pin = 17 6io.setup(switch_pin, io.OUT) 7 8#blink 10 times 9for i in range(10): 10 io.output(switch_pin, False) # set output pin LOW (off) 11 time.sleep(5) # sleep for 5 seconds 12 io.output(switch_pin, True) # set out put pin HIGH (on) 13 time.sleep(5) 14io.cleanup() #reset the pins
We will adapt this blinking light for controlling the relay later on. Note, in order to use GPIO you have to run as root!
Digital Temperature sensor
Checkout this tutorial. The circuit diagram is slightly confusing so here is another I find easier to read:
For testing, set this up on the circuit board. You can send power and ground to the bus along the side.
When you set this up for real:
- Take a sheet of PCB (it doesn’t need to be large, so you can break off a piece if you want) 1.. Cut off the connecter end of the jumper wires coming from the pi and strip off the covering
- Put the wire through a hole of the pc.
- Strip down the leads from the temperature sensor.
- Solder red to red, yellow to yellow, black to black.
- Solder your resistor between red and yellow.
- Use your hot glue gun to put a blob of glue over the blobs of solder. (Not strictly necessary, but a good thing to do).
- Use something to secure the cord of the temperature sensor to your circuit board (so that you don’t end up putting a lot of strain on individual wires). I used a hairband.
Continue following the adafruit tutorial from above to set up the directory for the temperature sensor for the first time.
Once you’ve done that, here is the code you will use. I copied it directly into my own program. Note: I noticed that for some reason, every time I wanted to restart my program, I had to cd into my device folder first, otherwise I’d get an error saying that the folder didn’t exist.
1import os 2import glob 3import time 4 5os.system('modprobe w1-gpio') 6os.system('modprobe w1-therm') 7 8base_dir = '/sys/bus/w1/devices/' 9device_folder = glob.glob(base_dir + '28*') 10device_file = device_folder + '/w1_slave' 11 12def read_temp_raw(): 13 f = open(device_file, 'r') 14 lines = f.readlines() 15 f.close() 16 return lines 17 18def read_temp(): 19 lines = read_temp_raw() 20 while lines.strip()[-3:] != 'YES': 21 time.sleep(0.2) 22 lines = read_temp_raw() 23 equals_pos = lines.find('t=') 24 if equals_pos != -1: 25 temp_string = lines[equals_pos+2:] 26 temp_c = float(temp_string) / 1000.0 27 temp_f = temp_c * 9.0 / 5.0 + 32.0 28 return temp_c, temp_f
That’s it for the hardware side of this project. I’ll return to the full program I have running on the Pi in Part III.
Part II: The Server
If this is not new to you, write your server however you want, just make sure that your pi can make POST requests to some route that will add information to a database.
If this is new to you, I’ll do a quick overview of my Flask server, and you can check out the code for yourself on github. Note, this will not be a Flask tutorial. If you’ve never used Flask or written a server, I highly recommend Miguel Grinberg’s Flask Mega-Tutorial. Though it is much more information than you need for this project. I also recommend the realpython tutorial and their ‘discover flask’ video series.
The server I’ve written is intended to be used by just two clients, you, and your Raspberry Pi. It is not intended for a large scale app.
The three main routes
My server has three main routes,
/switch_state.Here is what they do.
/Index : The Homepage
This route (which can also be accessed with the bare
/), runs the homepage function which returns the homepage,
email@example.com('/', methods = ['GET','POST']) firstname.lastname@example.org('/index', methods = ['GET','POST']) 3def homepage(): 4 current_log = db.get_last_ac_state() 5 room_temp = current_log 6 is_running = bool(current_log) 7 return render_template('index.html', room_temp = room_temp, 8 is_running = is_running, 9 time=datetime.datetime.now())
/ac_status: The route for the Raspberry Pi
This route only accepts POST requests. We will run code on the Pi to POST to this route every second with its most up-to-date state information.
The function called in this route
update() will take the info from the Pi, and store it in a sqlite database (using the
sqlite3 module in python). Check out
db.py to see my database functions.
It will then return the information stored in a global variable called
desired-state to the Pi.
1AcState = namedtuple('AcState', 2 ['timestamp','room_temp','is_running', 3 'state_num','goal_temp']) 4 email@example.com('/ac_status', methods=['POST']) 6def update(): 7 response = request.json 8 9 room_temp = response['room_temp'] 10 is_running = response['is_running'] 11 state_num = response['state_num'] #1, 2, or 3 12 #an empty string or an integer 13 goal_temp = str(response['goal_temp']) 14 print room_temp 15 print "I heard from the AC" 16 db.add_ac_state(AcState(datetime.datetime.now(), 17 room_temp,is_running, 18 state_num,goal_temp)) 19 20 global desired_state 21 return jsonify(desired_state)
I will discuss how I handle the
/switch_state: The route that the UI will hit
This route takes both GET and POST requests, and it calls the function,
First, it gets the latest information from the database. If the request is just a GET, the function will simply return the latest information from the database to the browser. If it’s a POST request, however, we’ll first call another function
statify, to translate what the user has asked for on the browser end, into something easy to convey to the air conditioner, and we’ll update the global
For more detail: How to Make Your Own Smart AC