Created
May 12, 2013 20:05
-
-
Save kaihansen79/5564722 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 | |
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() | |
sensorhistories = sensorhistory.SensorHistories(logfile) | |
print sensorhistories | |
# 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 | |
# 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 | |
#------------------------------------------KaW1-------------------------------------------------------------------- | |
if (((time.time() - sensorhistory.fiveminutetimer) >= 60.0) and (sensorhistory.sensornum == 1) | |
and (currminute % 2 == 0) | |
): | |
wattsused = {} | |
whused = {} | |
for history in sensorhistories.sensorhistories: | |
wattsused[history.sensornum] = history.avgwattover5min() | |
whused[history.sensornum] = history.dayswatthr | |
kwhused1 = whused[1]/1000 | |
avgwatt1 = wattsused[1] | |
cost1 = kwhused1 * ENERGY_PRICE | |
cost1 = "%.2f" % cost1 | |
pac = eeml.Pachube(API_URL, API_KEY) | |
pac.update(eeml.Data(0, | |
avgwatt1, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='watt', | |
type='derivedSI', | |
symbol='W', | |
) | |
) | |
) | |
# total KWh | |
pac.update(eeml.Data(1, | |
kwhused1, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='kilowatthour', | |
type='derivedSI', | |
symbol='KWh', | |
) | |
) | |
) | |
# Cost | |
pac.update(eeml.Data(2, | |
cost1, | |
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 timers | |
sensorhistory.reset5mintimer() | |
## reset5mintimer = {} | |
## for history in sensorhistories.sensorhistories: | |
## reset5mintimer[history.sensornum] = sensorhistory.reset5mintimer() | |
## reset5mintimer[1] | |
## reset5mintimer[2] | |
## reset5mintimer[3] | |
#------------------------------------------KaW2-------------------------------------------------------------------- | |
if (((time.time() - sensorhistory.fiveminutetimer) >= 60.0) and (sensorhistory.sensornum == 2) | |
and (currminute % 2 == 0) | |
): | |
wattsused = {} | |
whused = {} | |
for history in sensorhistories.sensorhistories: | |
wattsused[history.sensornum] = history.avgwattover5min() | |
whused[history.sensornum] = history.dayswatthr | |
kwhused2 = whused[2]/1000 | |
avgwatt2 = wattsused[2] | |
cost2 = kwhused2 * ENERGY_PRICE | |
cost2 = "%.2f" % cost2 | |
pac = eeml.Pachube(API_URL, API_KEY) | |
pac.update(eeml.Data(3, | |
avgwatt2, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='watt', | |
type='derivedSI', | |
symbol='W', | |
) | |
) | |
) | |
# total KWh | |
pac.update(eeml.Data(4, | |
kwhused2, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='kilowatthour', | |
type='derivedSI', | |
symbol='KWh', | |
) | |
) | |
) | |
# Cost | |
pac.update(eeml.Data(5, | |
cost2, | |
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 timers | |
sensorhistory.reset5mintimer() | |
#------------------------------------------KaW3-------------------------------------------------------------------- | |
if (((time.time() - sensorhistory.fiveminutetimer) >= 60.0) and (sensorhistory.sensornum == 3) | |
and (currminute % 2 == 0) | |
): | |
wattsused = {} | |
whused = {} | |
for history in sensorhistories.sensorhistories: | |
wattsused[history.sensornum] = history.avgwattover5min() | |
whused[history.sensornum] = history.dayswatthr | |
kwhused3 = whused[3]/1000 | |
avgwatt3 = wattsused[3] | |
cost3 = kwhused3 * ENERGY_PRICE | |
cost3 = "%.2f" % cost3 | |
pac = eeml.Pachube(API_URL, API_KEY) | |
pac.update(eeml.Data(6, | |
avgwatt3, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='watt', | |
type='derivedSI', | |
symbol='W', | |
) | |
) | |
) | |
# total KWh | |
pac.update(eeml.Data(7, | |
kwhused3, | |
minValue=0, | |
maxValue=None, | |
unit=eeml.Unit(name='kilowatthour', | |
type='derivedSI', | |
symbol='KWh', | |
) | |
) | |
) | |
# Cost | |
pac.update(eeml.Data(8, | |
cost3, | |
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 timers | |
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