# This is a variation on LadyAda's original Tweet-a-Watt script, # which sends the readings to pachube.com and ALSO tweets the readings. # Additional Pachube scripting by Brian Naughton, # Twitter scripting put back in by Dave Pentecost from LadyAda original. #!/usr/bin/env python import serial, time, datetime, sys from xbee import xbee import twitter import sensorhistory # For Pachube import eeml # parameters API_KEY = 'YOUR API KEY' API_URL = 'http://www.pachube.com/api/YOUR FEED.xml' SERIALPORT = "YOUR SERIAL PORT" # the com/serial port the XBee is connected to BAUDRATE = 9600 # the baud rate we talk to the xbee CURRENTSENSE = 4 # which XBee ADC has current draw data VOLTSENSE = 0 # which XBee ADC has mains voltage data MAINSVPP = 170 * 2 # +-170V is what 120Vrms ends up being (= 120*2sqrt(2)) vrefcalibration = [492,498] # Calibration for sensor #0 & #1 CURRENTNORM = 15.5 # conversion to amperes from ADC NUMWATTDATASAMPLES = 1800 # how many samples to watch in the plot window, 1 hr @ 2s samples # Twitter username & password twitterusername = "YOUR TWITTER USERNAME" twitterpassword = "YOUR TWITTER PASSWORD" def TwitterIt(u, p, message): api = twitter.Api(username=u, password=p) print u, p try: status = api.PostUpdate(message) print "%s just posted: %s" % (status.user.name, status.text) except UnicodeDecodeError: print "Your message could not be encoded. Perhaps it contains non-ASCII characters? " print "Try explicitly specifying the encoding with the it with the --encoding flag" except: print "Couldn't connect, check network, username and password!" # open up the FTDI serial port to get data transmitted to xbee ser = serial.Serial(SERIALPORT, BAUDRATE) ser.open() # open our datalogging file LOGFILENAME = "powerdatalog.csv" # where we will store our flatfile data logfile = None try: logfile = open(LOGFILENAME, 'r+') except IOError: # didn't exist yet logfile = open(LOGFILENAME, 'w+') logfile.write("#Date, time, sensornum, avgWatts\n"); logfile.flush() # a simple timer for twitter, makes sure we don't twitter more than once a day twittertimer = 0 sensorhistories = sensorhistory.SensorHistories(logfile) print sensorhistories # the 'main loop' runs once a second or so def update_graph(idleevent): global avgwattdataidx, sensorhistories, twittertimer # grab one packet from the xbee, or timeout packet = xbee.find_packet(ser) if not packet: return # we timedout xb = xbee(packet) # parse the packet # we'll only store n-1 samples since the first one is usually messed up voltagedata = [-1] * (len(xb.analog_samples) - 1) ampdata = [-1] * (len(xb.analog_samples ) -1) # grab 1 thru n of the ADC readings, referencing the ADC constants # and store them in nice little arrays for i in range(len(voltagedata)): voltagedata[i] = xb.analog_samples[i+1][VOLTSENSE] ampdata[i] = xb.analog_samples[i+1][CURRENTSENSE] # get max and min voltage and normalize the curve to '0' # to make the graph 'AC coupled' / signed min_v = 1024 # XBee ADC is 10 bits, so max value is 1023 max_v = 0 for i in range(len(voltagedata)): if (min_v > voltagedata[i]): min_v = voltagedata[i] if (max_v < voltagedata[i]): max_v = voltagedata[i] # figure out the 'average' of the max and min readings avgv = (max_v + min_v) / 2 # also calculate the peak to peak measurements vpp = max_v-min_v for i in range(len(voltagedata)): #remove 'dc bias', which we call the average read voltagedata[i] -= avgv # We know that the mains voltage is 120Vrms = +-170Vpp voltagedata[i] = (voltagedata[i] * MAINSVPP) / vpp # normalize current readings to amperes for i in range(len(ampdata)): # VREF is the hardcoded 'DC bias' value, its # about 492 but would be nice if we could somehow # get this data once in a while maybe using xbeeAPI if vrefcalibration[xb.address_16]: ampdata[i] -= vrefcalibration[xb.address_16] else: ampdata[i] -= vrefcalibration[0] # the CURRENTNORM is our normalizing constant # that converts the ADC reading to Amperes ampdata[i] /= CURRENTNORM #print "Voltage, in volts: ", voltagedata #print "Current, in amps: ", ampdata # calculate instant. watts, by multiplying V*I for each sample point wattdata = [0] * len(voltagedata) for i in range(len(wattdata)): wattdata[i] = voltagedata[i] * ampdata[i] # sum up the current drawn over one 1/60hz cycle avgamp = 0 # 16.6 samples per second, one cycle = ~17 samples # close enough for govt work :( for i in range(17): avgamp += abs(ampdata[i]) avgamp /= 17.0 # sum up power drawn over one 1/60hz cycle avgwatt = 0 # 16.6 samples per second, one cycle = ~17 samples for i in range(17): avgwatt += abs(wattdata[i]) avgwatt /= 17.0 # Print out our most recent measurements print str(xb.address_16)+"\tCurrent draw, in amperes: "+str(avgamp) print "\tWatt draw, in VA: "+str(avgwatt) if (avgamp > 13): return # hmm, bad data # retreive the history for this sensor sensorhistory = sensorhistories.find(xb.address_16) #print sensorhistory # add up the delta-watthr used since last reading # Figure out how many watt hours were used since last reading elapsedseconds = time.time() - sensorhistory.lasttime dwatthr = (avgwatt * elapsedseconds) / (60.0 * 60.0) # 60 seconds in 60 minutes = 1 hr sensorhistory.lasttime = time.time() print "\t\tWh used in last ",elapsedseconds," seconds: ",dwatthr sensorhistory.addwatthr(dwatthr) # Determine the minute of the hour (ie 6:42 -> '42') currminute = (int(time.time())/60) % 10 # Figure out if its been five minutes since our last save if (((time.time() - sensorhistory.fiveminutetimer) >= 60.0) and (currminute % 5 == 0) ): # Print out debug data, Wh used in last 5 minutes avgwattsused = sensorhistory.avgwattover5min() print time.strftime("%Y %m %d, %H:%M")+", "+str(sensorhistory.sensornum)+", "+str(sensorhistory.avgwattover5min())+"\n" # Send Data to Pachube pac = eeml.Pachube(API_URL, API_KEY) pac.update(eeml.Data(0, sensorhistory.avgwattover5min(),minValue=0, maxValue=None, unit=eeml.Unit(name='watt', type='derivedSI', symbol='W'))) pac.put() # Reset our 5 minute timer sensorhistory.reset5mintimer() # We're going to twitter at midnight, 8am and 4pm # Determine the hour of the day (ie 6:42 -> '6') currhour = datetime.datetime.now().hour # twitter every 8 hours if (((time.time() - twittertimer) >= 3660.0) and (currhour % 8 == 0)): print "twittertime!" twittertimer = time.time(); if not LOGFILENAME: message = appengineauth.gettweetreport() else: # sum up all the sensors' data wattsused = 0 whused = 0 for history in sensorhistories.sensorhistories: wattsused += history.avgwattover5min() whused += history.dayswatthr message = "Currently using "+str(int(wattsused))+" Watts, "+str(int(whused))+" Wh today so far, and posting to Pachube" # write something ourselves if message: print message TwitterIt(twitterusername, twitterpassword, message) while True: update_graph(None)