Created
May 11, 2013 19:14
-
-
Save kaihansen79/5561045 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import serial, time, datetime, sys | |
import eeml | |
from xbee import xbee | |
import sensorhistory | |
from time import sleep | |
import optparse | |
import smbus | |
API_KEY = 'L8rLOh9ceb350sAy_YPWwBtvYHaSAKxBOXZuNXkvYlVYZz0g' | |
FEED = '128485' | |
API_URL = '/v2/feeds/{feednum}.xml' .format(feednum = FEED) | |
ENERGY_PRICE = 0.158025 # Chicago elec rate/kwh | |
usage = "usage: %prog [options] file" | |
parser = optparse.OptionParser(usage=usage, description=__doc__) | |
parser.add_option('-d', '--debug', action="store_true", | |
help='''debug''', | |
default=False) | |
parser.add_option('-g', '--graphit', action="store_true", | |
help='''graphit''', | |
default=False) | |
options, args = parser.parse_args() | |
if options.graphit == True: | |
# for graphing stuff | |
GRAPHIT = True # whether we will graph data | |
else: | |
GRAPHIT = False | |
if options.debug == True: | |
DEBUG = True | |
else: | |
DEBUG = False | |
# use App Engine? or log file? comment out next line if appengine | |
# for graphing stuff | |
LOGFILENAME = "powerdatalog.csv" # where we will store our flatfile data | |
if not LOGFILENAME: | |
import appengineauth | |
# for graphing stuff | |
##if GRAPHIT: | |
## import wx | |
## import numpy as np | |
## import matplotlib | |
## matplotlib.use('WXAgg') # do this before importing pylab | |
## from pylab import * | |
SERIALPORT = "/dev/ttyUSB0" # 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, # Calibration for sensor #0 | |
486, # Calibration for sensor #1 | |
507, # Calibration for sensor #2 | |
492, # Calibration for sensor #3 | |
501, # Calibration for sensor #4 | |
493] # etc... approx ((2.4v * (10Ko/14.7Ko)) / 3 | |
CURRENTNORM = 15.5 # conversion to amperes from ADC | |
NUMWATTDATASAMPLES = 1800 # how many samples to watch in the plot window, 1 hr @ 2s samples | |
MAXWATTLISTLEN = 200 | |
watts = [] | |
def watt_average(watts): | |
if len(watts) == 0: | |
return None | |
else: | |
return sum(watts) / len(watts) | |
def add_wattvalue(value, watts): | |
if len(watts) < MAXWATTLISTLEN: | |
watts.append(value) | |
else: | |
watts.pop(0) | |
watts.append(value) | |
return watts | |
# open up the FTDI serial port to get data transmitted to xbee | |
ser = serial.Serial(SERIALPORT, BAUDRATE) | |
# open our datalogging file | |
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() | |
##if GRAPHIT: | |
## # Create an animated graph | |
## fig = plt.figure() | |
## # with three subplots: line voltage/current, watts and watthr | |
## wattusage = fig.add_subplot(211) | |
## mainswatch = fig.add_subplot(212) | |
## | |
## # data that we keep track of, the average watt usage as sent in | |
## avgwattdata = [0] * NUMWATTDATASAMPLES # zero out all the data to start | |
## avgwattdataidx = 0 # which point in the array we're entering new data | |
## | |
## # The watt subplot | |
## watt_t = np.arange(0, len(avgwattdata), 1) | |
## wattusageline, = wattusage.plot(watt_t, avgwattdata) | |
## wattusage.set_ylabel('Watts') | |
## wattusage.set_ylim(0, 500) | |
## | |
## # the mains voltage and current level subplot | |
## mains_t = np.arange(0, 18, 1) | |
## voltagewatchline, = mainswatch.plot(mains_t, [0] * 18, color='blue') | |
## mainswatch.set_ylabel('Volts (blue)') | |
## mainswatch.set_xlabel('Sample #') | |
## mainswatch.set_ylim(-200, 200) | |
## # make a second axies for amp data | |
## mainsampwatcher = mainswatch.twinx() | |
## ampwatchline, = mainsampwatcher.plot(mains_t, [0] * 18, color='green') | |
## mainsampwatcher.set_ylabel('Amps (green)') | |
## mainsampwatcher.set_ylim(-15, 15) | |
## | |
## # and a legend for both of them | |
## #legend((voltagewatchline, ampwatchline), ('volts', 'amps')) | |
sensorhistories = sensorhistory.SensorHistories(logfile) | |
#print sensorhistories | |
##sensorhistories1 = sensorhistory1.SensorHistories(logfile) | |
##print sensorhistories1 | |
## | |
##sensorhistories2 = sensorhistory2.SensorHistories(logfile) | |
##print sensorhistories2 | |
## | |
##sensorhistories3 = sensorhistory3.SensorHistories(logfile) | |
##print sensorhistories3 | |
# the 'main loop' runs once a second or so | |
def update_graph(idleevent): | |
global avgwattdataidx, sensorhistories, DEBUG | |
# 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 | |
print xb.address_16 | |
if DEBUG: # for debugging sometimes we only want one | |
print xb | |
# 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] | |
if DEBUG: | |
print "ampdata: "+str(ampdata) | |
print "voltdata: "+str(voltagedata) | |
# 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 [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 | |
if (avgamp > 13): | |
return # hmm, bad data | |
## if GRAPHIT: | |
## # Add the current watt usage to our graph history | |
## avgwattdata[avgwattdataidx] = avgwatt | |
## avgwattdataidx += 1 | |
## if (avgwattdataidx >= len(avgwattdata)): | |
## # If we're running out of space, shift the first 10% out | |
## tenpercent = int(len(avgwattdata)*0.1) | |
## for i in range(len(avgwattdata) - tenpercent): | |
## avgwattdata[i] = avgwattdata[i+tenpercent] | |
## for i in range(len(avgwattdata) - tenpercent, len(avgwattdata)): | |
## avgwattdata[i] = 0 | |
## avgwattdataidx = len(avgwattdata) - tenpercent | |
# retreive the history for this sensor | |
sensorhistory = sensorhistories.find(xb.address_16) | |
#print sensorhistory | |
if (xb.address_16==1): | |
sensorhistory1 = sensorhistories.find(xb.address_16) | |
print sensorhistory1 | |
elif (xb.address_16==2): | |
sensorhistory2 = sensorhistories2.find(xb.address_16) | |
print sensorhistory2 | |
elif (xb.address_16==3): | |
sensorhistory3 = sensorhistories3.find(xb.address_16) | |
print sensorhistory3 | |
# 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 | |
## elapsedseconds1 = time.time() - sensorhistory1.lasttime | |
## elapsedseconds2 = time.time() - sensorhistory2.lasttime | |
## elapsedseconds3 = time.time() - sensorhistory3.lasttime | |
dwatthr = (avgwatt * elapsedseconds) / (60.0 * 60.0) # 60 seconds in 60 minutes = 1 hr | |
## dwatthr1 = (avgwatt * elapsedseconds1) / (60.0 * 60.0) # 60 seconds in 60 minutes = 1 hr | |
## dwatthr2 = (avgwatt * elapsedsecwhusonds2) / (60.0 * 60.0) # 60 seconds in 60 minutes = 1 hr | |
## dwatthr3 = (avgwatt * elapsedseconds3) / (60.0 * 60.0) # 60 seconds in 60 minutes = 1 hr | |
sensorhistory.lasttime = time.time() | |
## sensorhistory1.lasttime = time.time() | |
## sensorhistory2.lasttime = time.time() | |
## sensorhistory3.lasttime = time.time() | |
#print "\t\tWh used in last ",elapsedseconds," seconds: ",dwatthr | |
sensorhistory.addwatthr(dwatthr) | |
## sensorhistory1.addwatthr(dwatthr1) | |
## sensorhistory2.addwatthr(dwatthr2) | |
## sensorhistory3.addwatthr(dwatthr3) | |
# 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 | |
#------------------------------------------KaW1-------------------------------------------------------------------- | |
if (((time.time() - sensorhistory1.fiveminutetimer) >= 60.0) and (sensorhistory1.sensornum == 1) | |
and (currminute % 2 == 0) | |
): | |
wattsused = 0 | |
whused = 0 | |
# Taking avgwattover5min from ALL or only one sensor => erronious KWh | |
for history in sensorhistories.sensorhistories: | |
wattsused += history.avgwattover5min() | |
whused += history.dayswatthr | |
kwhused = whused/1000 | |
avgwatt = sensorhistory.avgwattover5min() | |
cost = kwhused * ENERGY_PRICE | |
cost = "%.2f" % cost | |
message = "Currently using "+str(int(wattsused))+" Watts, "+str(int(whused))+" Wh today so far #tweetawatt" | |
pac = eeml.Pachube(API_URL, API_KEY) | |
pac.update(eeml.Data(0, | |
avgwatt, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='watt', | |
type='derivedSI', | |
symbol='W', | |
) | |
) | |
) | |
# total KWh | |
pac.update(eeml.Data(1, | |
kwhused, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='kilowatthour', | |
type='derivedSI', | |
symbol='KWh', | |
) | |
) | |
) | |
# Cost | |
pac.update(eeml.Data(2, | |
cost, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='cost', | |
type='contextDependentUnits', | |
symbol='$' | |
) | |
) | |
) | |
try: | |
print "pachube update: try" | |
pac.put() | |
print "pachube update: OK" | |
except: | |
print "pachube update failed" | |
# 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" | |
# Lets log it! Seek to the end of our log file | |
if logfile: | |
logfile.seek(0, 2) # 2 == SEEK_END. ie, go to the end of the file | |
logfile.write(time.strftime("%Y %m %d, %H:%M")+", "+ | |
str(sensorhistory.sensornum)+", "+ | |
str(sensorhistory.avgwattover5min())+"\n") | |
logfile.flush() | |
# Or, send it to the app engine | |
if not LOGFILENAME: | |
appengineauth.sendreport(xb.address_16, avgwattsused) | |
# Reset our 5 minute timer | |
sensorhistory.reset5mintimer() | |
#------------------------------------------KaW2-------------------------------------------------------------------- | |
elif (((time.time() - sensorhistory.fiveminutetimer) >= 60.0) and (sensorhistory.sensornum == 2) | |
and (currminute % 2 == 0) | |
): | |
wattsused = 0 | |
whused = 0 | |
for history in sensorhistories.sensorhistories: | |
wattsused += history.avgwattover5min() | |
whused += history.dayswatthr | |
kwhused = whused/1000 | |
avgwatt = sensorhistory.avgwattover5min() | |
cost = kwhused * ENERGY_PRICE | |
cost = "%.2f" % cost | |
message = "Currently using "+str(int(wattsused))+" Watts, "+str(int(whused))+" Wh today so far #tweetawatt" | |
pac = eeml.Pachube(API_URL, API_KEY) | |
pac.update(eeml.Data(3, | |
avgwatt, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='watt', | |
type='derivedSI', | |
symbol='W', | |
) | |
) | |
) | |
# total KWh | |
pac.update(eeml.Data(4, | |
kwhused, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='kilowatthour', | |
type='derivedSI', | |
symbol='KWh', | |
) | |
) | |
) | |
# Cost | |
pac.update(eeml.Data(5, | |
cost, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='cost', | |
type='contextDependentUnits', | |
symbol='$' | |
) | |
) | |
) | |
try: | |
print "pachube update: try" | |
pac.put() | |
print "pachube update: OK" | |
except: | |
print "pachube update failed" | |
# 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" | |
# Lets log it! Seek to the end of our log file | |
if logfile: | |
logfile.seek(0, 2) # 2 == SEEK_END. ie, go to the end of the file | |
logfile.write(time.strftime("%Y %m %d, %H:%M")+", "+ | |
str(sensorhistory.sensornum)+", "+ | |
str(sensorhistory.avgwattover5min())+"\n") | |
logfile.flush() | |
# Or, send it to the app engine | |
if not LOGFILENAME: | |
appengineauth.sendreport(xb.address_16, avgwattsused) | |
# Reset our 5 minute timer | |
sensorhistory.reset5mintimer() | |
#------------------------------------------KaW3-------------------------------------------------------------------- | |
elif (((time.time() - sensorhistory.fiveminutetimer) >= 60.0) and (sensorhistory.sensornum == 3) | |
and (currminute % 2 == 0) | |
): | |
wattsused = 0 | |
whused = 0 | |
for history in sensorhistories.sensorhistories: | |
wattsused += history.avgwattover5min() | |
whused += history.dayswatthr | |
kwhused = whused/1000 | |
avgwatt = sensorhistory.avgwattover5min() | |
cost = kwhused * ENERGY_PRICE | |
cost = "%.2f" % cost | |
message = "Currently using "+str(int(wattsused))+" Watts, "+str(int(whused))+" Wh today so far #tweetawatt" | |
pac = eeml.Pachube(API_URL, API_KEY) | |
pac.update(eeml.Data(6, | |
avgwatt, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='watt', | |
type='derivedSI', | |
symbol='W', | |
) | |
) | |
) | |
# total KWh | |
pac.update(eeml.Data(7, | |
kwhused, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='kilowatthour', | |
type='derivedSI', | |
symbol='KWh', | |
) | |
) | |
) | |
# Cost | |
pac.update(eeml.Data(8, | |
cost, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='cost', | |
type='contextDependentUnits', | |
symbol='$' | |
) | |
) | |
) | |
try: | |
print "pachube update: try" | |
pac.put() | |
print "pachube update: OK" | |
except: | |
print "pachube update failed" | |
# 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" | |
# Lets log it! Seek to the end of our log file | |
if logfile: | |
logfile.seek(0, 2) # 2 == SEEK_END. ie, go to the end of the file | |
logfile.write(time.strftime("%Y %m %d, %H:%M")+", "+ | |
str(sensorhistory.sensornum)+", "+ | |
str(sensorhistory.avgwattover5min())+"\n") | |
logfile.flush() | |
# Or, send it to the app engine | |
if not LOGFILENAME: | |
appengineauth.sendreport(xb.address_16, avgwattsused) | |
# Reset our 5 minute timer | |
sensorhistory.reset5mintimer() | |
if GRAPHIT: | |
# Redraw our pretty picture | |
fig.canvas.draw_idle() | |
# Update with latest data | |
wattusageline.set_ydata(avgwattdata) | |
voltagewatchline.set_ydata(voltagedata) | |
ampwatchline.set_ydata(ampdata) | |
# Update our graphing range so that we always see all the data | |
maxamp = max(ampdata) | |
minamp = min(ampdata) | |
maxamp = max(maxamp, -minamp) | |
mainsampwatcher.set_ylim(maxamp * -1.2, maxamp * 1.2) | |
wattusage.set_ylim(0, max(avgwattdata) * 1.2) | |
if __name__ == "__main__": | |
if GRAPHIT: | |
timer = wx.Timer(wx.GetApp(), -1) | |
timer.Start(100) # run an in every 'n' milli-seconds | |
wx.GetApp().Bind(wx.EVT_TIMER, update_graph) | |
plt.show() | |
else: | |
while True: | |
update_graph(None) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment