We are open

I have been looking for a way to automatically show on the blog when Ripon road is open.
The  project below does this by tweeting open or closed based on the light being on or off.
The Arduino has a light dependent resistor and a temperature sensor which sends data to Google app engine. Appengine then tweets the time, temperature and open or closed.
The temperature is also continually graphed here
The blog has the tweets embedded on the top right, and by following @makerspaceFY1, you will see when Ripon road opens and closes.
This will show everyday, rather than just the Saturday meeting.
The Arduino code.
//This sketch will send the temperature and light values to a server.
//An LDR and a DS18B20 (or similar) temperature sensor is required
//An ethernet shield is also required for internet connectivity.
//This tutorial shows you how to connect the DS18B20 to your Arduino: http://www.hobbytronics.co.uk/ds18b20-arduino
//This tutorial shows you how to connect the LDR to your arduino: http://bildr.org/2012/11/photoresistor-arduino/
#include
#include
#include //Not included by default, must be manually downloaded.
#include
byte mac[] = { 
  0x90, 0xA2, 0xDA, 0x00, 0x72, 0xA2}; //Ethernet Shield MAC address (needs to be unique for every device on your network)
//IPAddress ip(192,168,2,130); // If you require a static IP address, uncomment this line and change the IP address to something available
EthernetClient client;  //For main internet connection
float celsius; //The latest temperature reading from the temperature sensor
int light; //The latest light value from the LDR
void setup() {
  pinMode(9, INPUT); //Temperature sensor DS18B20
  pinMode(A1, INPUT); //LDR
  
  Serial.begin(9600); //Open the serial with a 9600 baud, this allows you to see debugging information while the Arduino is connected to your computer
  Serial.println(“Starting..”);
  Serial.println(” “);
  
  Serial.print(“Connecting to network.. “);
  Ethernet.begin(mac); //Connect to the internet via DHCP
  //Ethernet.begin(mac, ip); //If you want to connect via static IP address, uncomment this line of code
  Serial.println(Ethernet.localIP()); //Print the IP address of the Arduino. If the code gets stuck at this point, it can’t connect to the DHCP server and you should consider using a static IP
}
void loop() {
  getTemperature(9); //Function to get the temperature from the DS18B20 on pin 9
  //getTemperature() will return either ‘true’ or ‘false’. True if the temperature is valid, false otherwise. You may want to loop the function while false
  getLight(); //Get the light level from the LDR on pin A0 (The pin must be changed from inside the function)
  sendData(); //Send the data to the server (The server can be changed from inside the function)
  delay(60000);
  
}
void getLight(){
  light = analogRead(A0);
  Serial.print(“Light level: “);
  Serial.println(light);
  
  //Get the light reading, store it in ‘light’ (integer) and then print it to Serial for debugging
}
void sendData() {
  //The data is sent using URL parameters in a HTTP GET request. (eg. http://www.my_url.com/myscript?temperature=22.53C).
  //Change the URL to your server so you can process the incoming data.
  //The server needs to have a script running which can process data sent via URL parameters
  
  //This simply prints the URL which will be requested for debugging purposes, it is not required;
  Serial.print(celsius);
  Serial.print(“&light=”);
  Serial.println(light);
  client.stop();  //Ensure there are no open connections.
  Serial.print(“Connecting to server.. “);
  
  //If you are sending data to appengine, this must be set to google.com below. Replace “Host: myapp.appspot.com” (below) with your appengine application URL (or your own server)
  //If you want to send your data to another server, replace “google.com” with your server (don’t include subdomains or pages, they are specified below)
  if (client.connect(“google.com”, 80)) {
    Serial.println(” OPEN”); //The connection to the server worked.
    
    client.print(“GET myapp.appspot.com/myscript?temperature=”); //The url where the data is being sent to. (include subdomains, and the page/script the data will be sent to)
    //Add the values (temperature and light) as URL parameters;
    client.print(celsius); //Send the temperature value to the server
    client.print(“&uptime=”);
    client.print(millis()); //This sends the amount of time the Arduino has been running for (in milliseconds) to the server
    client.print(“&light=”);
    client.print(light); //Send the light value to the server
    client.println(” HTTP/1.1″); //Required for all HTTP connections
    client.println(“Host: myapp.appspot.com”); //Required.. change this to the server url including subdomains (do not mention pages or scripts here, only mention them above)
    client.println(“Connection: close”); //Tells the server to close the connection once the data has been sent.
    client.println(“User-Agent: Makerspace Arduino Sketch by Joe”); //The user-agent of your sketch, this can be anything.
    client.println(“Cache-Control: max-age=0”); //This tells the server not to cache any pages, this isn’t needed but some servers don’t work as expected when this is removed
    client.println(“Content-Type: application/x-www-form-urlencoded”); //Required for some servers, but not for all
    client.println(“Accept-Language: en-US,en;q=0.8”); //Required for some servers, but not for all
    client.println();
    Serial.println(“Data Sent!”);
    
  } else {
   client.println(“FAILED”); //The connection to the server failed.
   Serial.print(“Renew DHCP lease, raw response: “);
   Serial.println(Ethernet.maintain()); //Because the server connection failed, we will try to get a new IP address from the DHCP server in case that is the problem.
  }
  client.stop(); //Close the connection to the server
  Serial.println(“Appengine connection: CLOSED”);
  Serial.println(” “);
  
}
boolean getTemperature(byte pin) {
  celsius = 0;
  boolean working = true;
  OneWire  ds(pin); //Tell the OneWire library which pin we are using for the temperature sensor
  
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[9];
  if ( !ds.search(addr)) {
    //Serial.println(“”);
    ds.reset_search(); //Look for a connected temperature sensor and get it’s ID
    //Delay(250);
  }
  if (OneWire::crc8(addr, 7) != addr[7]) {
    Serial.println(“CRC is not valid!”); //The temperature sensor could not be found, check the wires.
    working = false;
    //celsius = 0;
    ds.depower();
    return false; //Error. Return false
  }
  switch (addr[0]) { //The library found a working sensor, this checks the sensor is compatible with the library.
  case 0x10:
    type_s = 1;
    break;
  case 0x28:
    type_s = 0;
    break;
  case 0x22:
    type_s = 0;
    break;
  default:
    Serial.println(“Device is not a DS18x20 family device.”); //Unknown temperature sensor (this may be cause by a faulty sensor or a bad connection/wiring)
    //celsius = 0;
    working = false;
    ds.depower(); //Turn off the sensor (if possible) to avoid wasting power
    return false; //Error. Return false
  }  
  ds.reset(); //Gets the sensor ready for a temperature reading
  ds.select(addr);         
  ds.write(0x44,1);         // Tell the temperature sensor to get the current temperature
  delay(750);     // We need to wait at least 700ms while the temperature sensor determines the temperature.
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    //Serial.print(data[i], HEX);
    //Serial.print(” “);
  } 
  int raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // count remain gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 – data[6];
    }
  } 
  else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw << 3;  
    else if (cfg == 0x20) raw = raw << 2; 
    else if (cfg == 0x40) raw = raw << 1; 
  }
  
  celsius = (float)raw / 16.0; //Put the current temperature into the variable ‘celsius’ (float value)
  ds.depower(); //Turn off the temperature sensor to save power
  Serial.print(“Got temperature: “); //Print the current value from the temperature sensor (for debugging; not required)
  Serial.print(celsius);
  Serial.println(“.C”);
  
  return working; //Returns true if the temperature was received, else return false.
  delay(100);
   
}

The Appengine code

#!/usr/bin/env python

#This is a Google Appengine Python script; it will only run properly on Google Appengine.
#This code processes data sent from an Arduino (only temperature and light readings)
#It will send a tweet when the light is turned on, and then again when the light is turned off. It is not designed for daylight.
#This code also sends the data from the Arduino to Xively so it can be graphed.
#NOTE: This page is not password protected, so if someone finds out your URL they can send false readings to the server. Keep your URL private.

#For this code to work properly, you also need a file called “app.yaml” in the same folder as this script, “app.yaml” must contain this code;
“””
application: appID
version: 1
runtime: python27
api_version: 1
threadsafe: no

handlers:
– url: /myscript
  script: myscript.app
“””
#Do NOT include the “”” at the top and bottom
#Replace “appID” with the name of your application, you can create one at appengine.google.com
#Replace “myscript.app” with the filename of this script (leave .app on the end)

import webapp2 #Required by Google Appengine
from google.appengine.api import urlfetch #Required to send web requests to Xively
import datetime #This is required to determine the current date/time
from google.appengine.api import memcache #This is required for accessing memcache
import logging #Required for printing debugging information to Appengine

import tweepy #This is not included and must be downloaded manually from github.com/tweepy/tweepy

#This information is found on your Twitter account at dev.twitter.com/apps
consumer_key=”Your key”
consumer_secret=”Your secret”

access_token=”Your access token”
access_token_secret=”Your access token secret”

class MainHandler(webapp2.RequestHandler):
    def get(self): #This function is called each time there is a HTTP GET request to this URL (/myscript)
     
        temperature = self.request.get(‘temperature’) #This gets any data stored in the URL parameter ‘temperature’
     
        if temperature:
            #This code runs when the URL paramter ‘temperature’ is found (sent by the Arduino)
            memcache.set(‘temperature’, temperature) #If the temperature was included in the HTTP request, save it into Memcache
            #Memcache is temporary storage similar to RAM
         
            light = self.request.get(‘light’) #Get the current light value from the ‘light’ URL parameter (sent from the Arduino)
            lastlight = memcache.get(‘lastlight’) #Get the light value from the last request (if it exists)
            #This is set later on in the code so we can compare the value between each request
         
            #This code sends both the light and temperature to Xively (A free website which graphs your data)
            urlfetch.set_default_fetch_deadline(40) #If the connection to Xively takes more than 40 seconds, cancel it (timeout)
            toSend = ‘temperature, ‘ + temperature + “””
“”” + ‘light, ‘ + light #Construct the data (the temperature and light value) so it can be sent.
             
            try:
                url = ‘https://api.xively.com/v2/feeds/FEEDID.csv&#8217; #Replace ‘FEEDID’ with the ID of your Xively page
                result = ”
                result = urlfetch.fetch(url=url,
                    method=urlfetch.PUT, #This tells Appengine to make a PUT request to Xively (which is the recommended way of sending it)
                    payload=toSend, #This tells Appengine to put the value of ‘toSend’ into the PUT request
                    headers={‘Pragma’: ‘no-cache’,
                             ‘X-ApiKey’: ‘YOUR_API_KEY’}) #Replace ‘YOUR_API_KEY’ with your Xively API key
            except:
                logging.warning(‘Unable to reach xively’) #If we can’t connect to Xively, print a warning message into the Appengine logs
             
         
         
            if str(lastlight) == ” or str(lastlight) == ‘None’: #This checks to see if the value of ‘lastlight’ is valid
                lastlight = 0 #If the data isn’t valid, set it to ‘0’
             
            if str(light) == ” or str(light) == ‘None’: #This checks the value of ‘light’ to ensure it’s valid
                light = 0 #If it isn’t valid, make it ‘0’
         
            light = int(light) #Ensure the variables are both integers
            lastlight = int(lastlight)
         
            memcache.set(‘lastlight’, light) #Save the value of ‘lastlight’ into memcache. This allows us to compare it with future values

            lighton = False #Create boolean variables called ‘lighton’ and ‘confirmed’
            confirmed = False
            if light > 525: #If the light reading is more than 525, the light is turned on
                #(You will most likely need to change this for your environment)
                logging.debug(‘Light is ON’)
                #This code runs when the light is CURRENTLY greater than 525 (eg, the light is on)
                if lastlight > 525:
                    #This code runs when the light is CURRENTLY on AND it was on last time we checked
                    logging.debug(‘Confirmed’)
                    lighton = True #The light was on the last 2 times we checked, so that means we can be certain the light is really on
                    confirmed = True
            else:
                logging.debug(‘Light is OFF’)
                if lastlight < 525:
                    #The light is currently off, and it was off last time we checked; we can be certain the light is really off
                    logging.debug(‘Confirmed’)
                    lighton = False
                    confirmed = True
                 
            if confirmed == False:
                #The light has changed (eg, the light was turned on or off) but we will wait until we get another message from the Arduino..
                #..so we can be certain about the change. (This stops the tweet from being triggered every time someone walks in front of the light sensor)
                logging.debug(“Light status changed but waiting for another message from the Arduino”)
                return
         
            lasttweet = memcache.get(‘lasttweet’) #When we make a tweet saying ‘light on’ we set this to True, and we set..
            #.. it to false whenever we tweet ‘light off’
         
            auth = tweepy.OAuthHandler(consumer_key, consumer_secret) #These are needed for the Twitter library to work
            auth.set_access_token(access_token, access_token_secret)
            api = tweepy.API(auth)
         
            logging.info(‘logged in as ‘ + str(api.me().name)) #Show the currently logged in user (to Twitter) into the Appengine logs
             
            if lasttweet == True: #The last tweet sent was saying ‘lights on’
                if lighton == True: #Currently, the lights are on
                    #Because the last tweet sent was saying ‘lights on’ and the lights are still on, we don’t need to Tweet again
                    logging.debug(“The light is on and a tweet was sent earlier”)
                    memcache.set(‘lasttweet’, True)
                    return
                if lighton == False: #The last tweet sent was ‘lights on’ but the lights are now off, send a tweet.
                    logging.debug(“The light has been turned off, will send a tweet saying CLOSED”)
                    memcache.set(‘lasttweet’, False)
                    api.update_status(‘We turned off our lights at ‘ + str(datetime.datetime.now().time())[:5])
                    #This posts “We turned off our lights at 14:32” (or whatever time it is) to your Twitter account
                    return
             
            if lasttweet == False: #The last tweet we sent said ‘lights off’
                if lighton == True: #Currently the lights are on, and the last tweet said they were off..
                    #This means we need to send a tweet saying the lights are now on
                    logging.debug(“The light has been turned on, will send a tweet saying OPEN”)
                    memcache.set(‘lasttweet’, True)
                    api.update_status(‘We turned on our lights at ‘ + str(datetime.datetime.now().time())[:5] + ‘ and the temperature was ‘ + temperature + ‘.C’)
                    #This posts “We turned on our lights at 08:47 and the temperature was 23.42.C” for example
                    return
                if lighton == False:
                    #The last tweet sent was saying ‘lights off’ and they’re still off, we don’t need to tweet again.
                    logging.debug(“The light is off and a tweet was sent earlier”)
                    memcache.set(‘lasttweet’, False)
                    return
             
            if lasttweet == None:
                #If the value of ‘lasttweet’ is missing, set it to False and then do nothing until the next message from the Arduino
                logging.warning(“No lasttweet value, assuming False.”)
                memcache.set(‘lasttweet’, False) #Sets ‘lasttweet’ to False in Memcache
                return
         
        else:
            logging.warning(‘No temperature was sent’) #No temperature was sent, log a warning to the Appengine logs
            #Your arduino should request “/myscript?temperature=22.64&light=453” (but substituting the current values in).
            #This part of the code only runs when the “?temperature=” part is missing. Check the Arduino code.

         
app = webapp2.WSGIApplication([
    (‘/myscript’, MainHandler) #Run the function ‘MainHandler’ every time the URL ‘/myscript’ is visited.
], debug=True)

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s