Skip to content

Instantly share code, notes, and snippets.

@kaihansen79
Created May 12, 2013 20:05
Show Gist options
  • Save kaihansen79/5564722 to your computer and use it in GitHub Desktop.
Save kaihansen79/5564722 to your computer and use it in GitHub Desktop.
#!/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