Difference between revisions of "Nest Model"
Line 77: | Line 77: | ||
import tornado.web | import tornado.web | ||
import RPi.GPIO as GPIO | import RPi.GPIO as GPIO | ||
+ | import subprocess | ||
+ | import Adafruit_GPIO.SPI as SPI | ||
+ | import Adafruit_MCP3008 | ||
+ | |||
+ | print "mode: " | ||
+ | print GPIO.getmode() | ||
+ | |||
+ | #GPIO.setmode(GPIO.BOARD) | ||
+ | |||
+ | #print "mode: " | ||
+ | #print GPIO.getmode() | ||
+ | |||
+ | #software box configuration: | ||
+ | OUT1 = 24 | ||
+ | LED1 = 23 | ||
+ | |||
+ | #software SPI configuration: | ||
+ | CLK = 23 | ||
+ | MISO = 21 | ||
+ | MOSI = 19 | ||
+ | CS = 24 | ||
+ | mcp = Adafruit_MCP3008.MCP3008(clk=CLK, cs=CS, miso=MISO, mosi=MOSI) | ||
+ | print "mode: " | ||
+ | print GPIO.getmode() | ||
+ | |||
+ | # Hardware SPI configuration: | ||
+ | SPI_PORT = 0 | ||
+ | SPI_DEVICE = 0 | ||
+ | mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE)) | ||
+ | |||
+ | #Define Variables | ||
+ | delay = 0.5 | ||
+ | ldr_channel = 0 | ||
+ | |||
+ | def readadc(i): | ||
+ | # read SPI data from the MCP3008, channel 1 is temp channel 0 is light | ||
+ | if i == 1: | ||
+ | millivolts = mcp.read_adc(i)*(3300/1024) | ||
+ | temp_C = ((millivolts-500.0)/10.0) | ||
+ | temp_F = (temp_C*9.0/5.0)+32 | ||
+ | temp_C = "%.1f" % temp_C | ||
+ | temp_F = "%1.f" % temp_F | ||
+ | return temp_F | ||
+ | else: | ||
+ | return (mcp.read_adc(i))/160 | ||
#Initialize Raspberry PI GPIO | #Initialize Raspberry PI GPIO | ||
− | GPIO. | + | GPIO.setup(LED1, GPIO.OUT) |
− | GPIO.setup( | + | GPIO.setup(OUT1, GPIO.OUT) |
− | GPIO. | + | GPIO.output(LED1, False) |
+ | GPIO.output(OUT1, True) | ||
+ | |||
#Tornado Folder Paths | #Tornado Folder Paths | ||
settings = dict( | settings = dict( | ||
Line 96: | Line 143: | ||
class WSHandler(tornado.websocket.WebSocketHandler): | class WSHandler(tornado.websocket.WebSocketHandler): | ||
def check_origin(self, origin): | def check_origin(self, origin): | ||
− | return | + | print origin |
+ | return origin == "http://ec2-3-85-217-77.compute-1.amazonaws.com" | ||
def open(self): | def open(self): | ||
Line 103: | Line 151: | ||
def on_message(self, message): | def on_message(self, message): | ||
print '[WS] Incoming message:', message | print '[WS] Incoming message:', message | ||
− | if message == " | + | if message == "on_outlet1": |
− | GPIO.output( | + | GPIO.output(OUT1, False) |
− | if message == " | + | GPIO.output(LED1, True) |
− | GPIO.output( | + | if message == "off_outlet1": |
+ | GPIO.output(OUT1, True) | ||
+ | GPIO.output(LED1, False) | ||
+ | if message == "refresh_lum": | ||
+ | self.write_message(str(readadc(0))+ " LUM") | ||
+ | if message == "refresh_temp": | ||
+ | self.write_message(str(readadc(1))+ " TEMP") | ||
def on_close(self): | def on_close(self): | ||
print '[WS] Connection was closed.' | print '[WS] Connection was closed.' | ||
Line 132: | Line 186: | ||
var WEBSOCKET_ROUTE = ":8888/ws"; | var WEBSOCKET_ROUTE = ":8888/ws"; | ||
− | + | var piIP3 = "172.27.8.108"; | |
+ | var piIP0 = "172.27.178.159"; | ||
if(window.location.protocol == "http:"){ | if(window.location.protocol == "http:"){ | ||
//localhost | //localhost | ||
− | var ws = new WebSocket("ws://" + | + | var ws = new WebSocket("ws://" + piIP0 + WEBSOCKET_ROUTE); |
console.log("http case"); | console.log("http case"); | ||
} | } | ||
Line 142: | Line 197: | ||
//Dataplicity | //Dataplicity | ||
console.log("in the https state"); | console.log("in the https state"); | ||
− | var ws = new WebSocket("wss://" + | + | var ws = new WebSocket("wss://" + piIP0 + WEBSOCKET_ROUTE); |
} | } | ||
Line 150: | Line 205: | ||
ws.onmessage = function(evt) { | ws.onmessage = function(evt) { | ||
+ | //console.log(evt.data); | ||
+ | if(evt.data.includes("LUM")){ | ||
+ | $("#lumin").html(parseInt(evt.data,10)); | ||
+ | } | ||
+ | if(evt.data.includes("TEMP")){ | ||
+ | $("#temp").html((parseInt(evt.data,10))); | ||
+ | } | ||
}; | }; | ||
ws.onclose = function(evt) { | ws.onclose = function(evt) { | ||
− | $("#ws-status").html("Disconnected"); | + | $("#ws-status").html("Disconnected. If you were unable to connect, please restart your outlet."); |
}; | }; | ||
− | $("# | + | /*$("#outlet2_on").click(function(){ |
− | ws.send(" | + | ws.send("on_outlet2"); |
+ | if(ws.readyState === ws.OPEN){ | ||
+ | $("#outlet2-status").html("OUTLET ON"); | ||
+ | } | ||
+ | }); | ||
+ | $("#outlet2_off").click(function(){ | ||
+ | ws.send("off_outlet2"); | ||
+ | $("#outlet2-status").html("OUTLET OFF"); | ||
+ | });*/ | ||
+ | |||
+ | $("#outlet1_on").click(function(){ | ||
+ | ws.send("on_outlet1"); | ||
+ | if(ws.readyState === ws.OPEN){ | ||
+ | $("#outlet1-status").html("OUTLET ON"); | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | $("#outlet1_off").click(function(){ | ||
+ | ws.send("off_outlet1"); | ||
+ | $("#outlet1-status").html("OUTLET OFF"); | ||
+ | }); | ||
+ | |||
+ | $("#check_lumin").click(function(){ | ||
+ | ws.send("refresh_lum"); | ||
}); | }); | ||
− | $("# | + | $("#check_tem").click(function(){ |
− | ws.send(" | + | ws.send("refresh_temp"); |
}); | }); | ||
Revision as of 19:00, 27 April 2019
Contents
Overview
Our project is to create a smart outlet consisting of a raspberry pi zero, a relay, and a container that works between an AC outlet and a device. We'd like to be able to communicate between a web interface and the outlet to be able to turn the device on and off. We would also like to add timing control features through the web interface. If we are successful with the initial project, we will add additional modules to the web interface to be able to control multiple devices.
Team Members
Amanda Hua
Tricia Brown
TA: Keith Kamons
Professor: James Feher
Links
[View Project Log]
[View Github]
[View Preliminary Presentation]
[Tutorial]
[Poster]
Objectives and Goals
Build a module that can relay between an AC outlet and device that we want to control
Build a web interface that successfully communicates with the built module in at least a binary fashion
Set up a server to host the web interface
Challenges
- A majority of our programming expertise is in Java, and learning more about web development languages, especially Python, will be time consuming.
- We have minimal background in hardware and circuitry, so a big challenge will be ensuring that we will account for all of the details necessary for our module.
- We have no background in web security, so we need to be very careful in allowing access to the devices and files that we will be using.
- We have never worked with Raspberry Pi before.
- Linux is an unfamiliar operating system.
Budget
- Raspberry Pi(s) - provided ($35)
- Raspberry Pi Zero(s) - provided ($5)
- Relay - provided ($1.25)
- AC Prongs - $2.79
- AC Duplex Outlet - $0.43
- Module box - $27.44
- MCP 3008 - $3.75
- TMP 36Gz - $1.50
- SD card - provided ($4.99)
- Component (fan) - $16.99
- Cable - $6
- Outlet Covers - $14.44
Total: -----needs to be updated ----
Useful Resources
RasPi Resources
Temp Sensor Resources
132 Assignment (More Context)
Spec Sheet
DevOps and Server Side Resources
Project Results
Gantt Chart
https://docs.google.com/spreadsheets/d/10Ftzdzjh8UOGXznYvhBb2OvnZ2Exib2vUMbfKiH339s/edit?usp=sharing
Media:GanttChart2-16.png
Design and Solution
Website
The website uses websockets to communicate with the raspberry pi hardware. The html buttons on the website will run a script on the pi to turn on/off the smart outlet.
On boot, the pi should run the server side websocket code(works on pi3 but not pi0, pi0 requires manual start, debugging). This starts the server that listens for emissions that are communicating with the IP address of the pi. The EC2 instance (continually running) connects to the server through a javascript script embedded in the website's html. This is kept track of by the Websocket Status shown at the bottom of the page. When connected, the client side (EC2) will send input from buttons on the page as messages to the pi, which interprets the incoming messages and converts them into instructions for its GPIO pins.
#SERVER SIDE SCRIPT server.py on Raspberry Pi
#! /usr/bin/python
import os.path
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import RPi.GPIO as GPIO
import subprocess
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008
print "mode: "
print GPIO.getmode()
#GPIO.setmode(GPIO.BOARD)
#print "mode: "
#print GPIO.getmode()
#software box configuration:
OUT1 = 24
LED1 = 23
#software SPI configuration:
CLK = 23
MISO = 21
MOSI = 19
CS = 24
mcp = Adafruit_MCP3008.MCP3008(clk=CLK, cs=CS, miso=MISO, mosi=MOSI)
print "mode: "
print GPIO.getmode()
# Hardware SPI configuration:
SPI_PORT = 0
SPI_DEVICE = 0
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))
#Define Variables
delay = 0.5
ldr_channel = 0
def readadc(i):
# read SPI data from the MCP3008, channel 1 is temp channel 0 is light
if i == 1:
millivolts = mcp.read_adc(i)*(3300/1024)
temp_C = ((millivolts-500.0)/10.0)
temp_F = (temp_C*9.0/5.0)+32
temp_C = "%.1f" % temp_C
temp_F = "%1.f" % temp_F
return temp_F
else:
return (mcp.read_adc(i))/160
#Initialize Raspberry PI GPIO
GPIO.setup(LED1, GPIO.OUT)
GPIO.setup(OUT1, GPIO.OUT)
GPIO.output(LED1, False)
GPIO.output(OUT1, True)
#Tornado Folder Paths
settings = dict(
template_path = os.path.join(os.path.dirname(__file__), "templates"),
static_path = os.path.join(os.path.dirname(__file__), "static")
)
#Tornado server port
PORT = 8888
class MainHandler(tornado.web.RequestHandler):
def get(self):
print "[HTTP](MainHandler) User Connected."
self.render("index.html")
class WSHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
print origin
return origin == "http://ec2-3-85-217-77.compute-1.amazonaws.com"
def open(self):
print '[WS] Connection was opened.'
def on_message(self, message):
print '[WS] Incoming message:', message
if message == "on_outlet1":
GPIO.output(OUT1, False)
GPIO.output(LED1, True)
if message == "off_outlet1":
GPIO.output(OUT1, True)
GPIO.output(LED1, False)
if message == "refresh_lum":
self.write_message(str(readadc(0))+ " LUM")
if message == "refresh_temp":
self.write_message(str(readadc(1))+ " TEMP")
def on_close(self):
print '[WS] Connection was closed.'
application = tornado.web.Application([
(r'/', MainHandler),
(r'/ws', WSHandler),
])
if __name__ == "__main__":
try:
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(PORT)
main_loop = tornado.ioloop.IOLoop.instance()
print "Tornado Server started"
main_loop.start()
except:
print "Exception triggered - Tornado Server stopped."
GPIO.cleanup()
#End of Program
//CLIENT SIDE SCRIPT ws-client.js on EC2 instance
$(document).ready(function(){
var WEBSOCKET_ROUTE = ":8888/ws";
var piIP3 = "172.27.8.108";
var piIP0 = "172.27.178.159";
if(window.location.protocol == "http:"){
//localhost
var ws = new WebSocket("ws://" + piIP0 + WEBSOCKET_ROUTE);
console.log("http case");
}
else if(window.location.protocol == "https:"){
//Dataplicity
console.log("in the https state");
var ws = new WebSocket("wss://" + piIP0 + WEBSOCKET_ROUTE);
}
ws.onopen = function(evt) {
$("#ws-status").html("Connected");
};
ws.onmessage = function(evt) {
//console.log(evt.data);
if(evt.data.includes("LUM")){
$("#lumin").html(parseInt(evt.data,10));
}
if(evt.data.includes("TEMP")){
$("#temp").html((parseInt(evt.data,10)));
}
};
ws.onclose = function(evt) {
$("#ws-status").html("Disconnected. If you were unable to connect, please restart your outlet.");
};
/*$("#outlet2_on").click(function(){
ws.send("on_outlet2");
if(ws.readyState === ws.OPEN){
$("#outlet2-status").html("OUTLET ON");
}
});
$("#outlet2_off").click(function(){
ws.send("off_outlet2");
$("#outlet2-status").html("OUTLET OFF");
});*/
$("#outlet1_on").click(function(){
ws.send("on_outlet1");
if(ws.readyState === ws.OPEN){
$("#outlet1-status").html("OUTLET ON");
}
});
$("#outlet1_off").click(function(){
ws.send("off_outlet1");
$("#outlet1-status").html("OUTLET OFF");
});
$("#check_lumin").click(function(){
ws.send("refresh_lum");
});
$("#check_tem").click(function(){
ws.send("refresh_temp");
});
});
Figure 1. Rough design of website home page
Figure 2. Communication depicted between the AWS web interface and the user, the database with user data, and the pi module.
Figure 3. Original plan for configuration of the website pages and link paths
Sensors & Hardware
Sensors to Transmit Data: We decided to add sensors to collect data that can be displayed on the main web page. For our project, we were only able to add a temperature and light sensor, but many more features could be added in the future. We had to wire an AD converter into the Raspberry Pi because it does not have one built in. The AD converter reads a voltage from the device and outputs a meaningful value.
Power Supply: Our device plugs directly into the wall, however a typical outlet supplies 120 volts. The Raspberry Pi can only handle 5 volts at a time so we had to include a relay to the module in order to separate the pi from the wall outlet voltage unless the device needs to be turned on. *need to add explanation about how the relay works*
- insert relay circuit diagram*