serialKAW: Serial readout of Kill-A-Watt

A few weeks ago I started comparing the power consumption of low power x86(Atom) and ARM systems under load. For this I was interested in accurately measuring the power consumption at a high sample rate. At first I started with Ladyada's "Tweet-A-Watt" project which piggy backs the main op-amp of a P4400 Kill-A-Watt(KAW) by connecting it's sense-lines to the Analog-to-Digital Converter(ADC) inputs of an XBee transmitter.
SerialKAW in Action

Ladyada's Tweet-A-Watt approach is great for hassle-free wireless long-term monitoring. But the Tweet-A-Watt's transmitter(XBee) is constrained by the KAW's very limited internal power supply. This prevents it from continuously transmitting and therefore limits it's sampling rate to approximately 2 x 19 samples every 2 seconds. The Tweet-A-Watt "buffers" power in a large 10k mircofarad capacitor, allowing it to charge for 2 seconds and then transmit 2x19 samples(Volt & Amp) in one burst. As mentioned previously this is great for long-term monitoring of devices, but for this project I wanted a much higher sample rate.

Inside the Kill-A-Watt

To remedy this problem I replaced the Xbee with an Arduino. There is nothing revolutionary about this, it uses a simple voltage divider between the sense leads going to the op-amp and the Arduio's ADCs. It is important for the Arduino to have a steady power supply, as that the ADC's reference voltage depends on it. Ultimately I found it essential to provide external power as the voltage supplied over USB varied between different computers, making calibration unreliable. I am purposely not handing out a schematic to ensure only people that know what they are doing attempt this. If you screw up, you could fry your computer, yourself or others.

During operation, the Arduino samples its ADC as fast as possible, alternating between the two input channels, Voltage and Ampear. It takes roughly 0.225ms to read the ADC and store the result in a temporary array. This gives us ~74.07 samples per Cycle of AC or a sampling rate of about 4444.44hz. The Arduino collects a bit over 2 cycles worth of samples and then pushes them out over serial to our connected host computer where a python script is waiting to receive its payload.

power consumption during Benchmark

I have made public all associated code and put together a python monitor which sits on the host computer and catching the samples fired from the Arduino over serial. This project is available at github.com/madmaze/serialKAW. serialKAW provides a bunch of useful features when it comes to monitoring the power consumption of devices. "powerMeterDaemon.py" provides an object which can be used to asynchronously monitor power consumption while also running load tests on the target system. This is a work in progress and mostly tailored to what I need it for. Please do give me feedback and file bug reports.

Download on GitHub

Code examples:

Most basic usage:

$>./powerMeterDaemon.py

This will spawn a simple applet which plots both the "Wattage Trend" and a Volt/Amp graph. The plotting bits are heavily based on Ladyada's Tweet-a-Watt code, but the rest is mostly a rewrite.

Here is a more advanced code sample for using serialKAW to test the power consumption of a computer:

#!/usr/bin/env python
import powerMeterDaemon as pm
import time
import os


def run(cmd):
	f=os.popen(cmd)
	buf=""
	for i in f.readlines():
		buf+=i
	return buf.strip()


# Lets get a powerMeter without the GUI and dont start monitoring immediately
meter = pm.powerMeter(plotGraph=False,powerLogFile="",MonitorOnStart=False, verbose=False)

# testMachine
testHostname = "user@testmachine"
testKeys = "~/.ssh/test"

# path to test on testMachine
testPath = "./Code/Benchmark/test.sh"

# start the monitor and give it time to record a baseline power consumption
meter.startMonitoring()
time.sleep(10)

# run test on remote machine
cmd = "ssh -i " + testKeys + " " + testHostname + " '" + testPath + "'"
#print cmd
run(cmd)

# make sure the test is done then stop monitoring
time.sleep(10)
meter.stopMonitoring()

# Get get the power consumption log 
log = meter.getDataLog()

now = datetime.datetime.now()
nowstr = now.strftime("%Y%m%d_%H%M%S")
curdir = "./logs/"+nowstr
os.mkdir(curdir)

# write out power consumption in CSV
logFile = curdir+"/powerlog.csv"
f = open(logFile,"a")
f.write(testHostname+"\n")
for k in log:
	f.write(k[0]+","+str(k[3])+"\n")
	
f.close()