projects | about

Home power Monitoring

About | Current power usage | Summary by hour | Summary by weekday

Note: new section added to discuss the accuracy of this system.

power graph

This is a DIY project using current transducers, an arduino, and some custom software to monitor power consumption for an entire house. The power measurements are graphed and made public on a webserver.

The end result is a slightly cheaper hack of the The Energy Detective.

This data shared online is currently represented as both current power consumption as well as as summary charts.

Please monitor my rss feed if you want to know when this project is updated. If you have questions or comment feel free to send me mail or leave a message on the hack-a-day comment thread.

Similar projects

There are a number of similar projects already out there for home power monitoring. If you have a project similar to this one not listed here I would love to hear about it.

Overview Diagram

pwr diagram

Materials

Total cost ~ $110 (not counting what I already had lying around) If money is tight there are a few things that can make this cheaper by about fifty bucks:

Accuracy

Here are some factors that will influence accuracy for all current transducer monitoring systems like this one.

Power Factor

Power factor is a number between 0 and 1 and is the ratio of real power to the product of the rms current times the rms voltage. Power factor differs from device to device and will need to be approximated to caculate over all power usage. A power factor of .60 will be used as an approximation for the overall power calculation. In order to calculate power factor the voltage would need to be monitored as well as the current for each phase.

Voltage

Voltage can vary depending on what is connected to the home. For calculating power the voltage will be approximated to be 120V(rms).

Accuracy of the ADC

Measuring the voltage from the CT is done using an Arduino microcontroller. With 3.3V as the analog reference the resolution of the ADC is .0032mV. This voltage on the CT is 213mA of current which corresponds to an apparent power resolution of ~ 25Watts. This means the apparent power calculations on the arduino will only be as accurate as the resolution.

RMS vs Peak Approximations

In the first version of this project a very simple peak voltage calculation was being used with a fudge factor to make a guess at the scaling factor for overall power.

To improve this slightly the arduino is now making an RMS caluculation instead of using the peak value.

Because the signal from the CT is not level shifted the arduino only sees the signal when it is above zero volts. Instead of applying a DC bias to the signal only half the samples will be used for the RMS calculation. Assuming the signal is symetrical on the X-axis the following code calculates an approximate RMS value for a specified number of evenly spaced samples.

float rms_val1 = 0;
float rms_val2 = 0;
for (int cnt=0; cnt<SAMPLES; cnt++) {

        val1 = analogRead(S1_PIN);
        val2 = analogRead(S2_PIN);

        rms_val1 = rms_val1 + sq( (float)val1 );
        rms_val2 = rms_val2 + sq( (float)val2 );
}

rms_val1 = sqrt(rms_val1 / (SAMPLES/2) );
rms_val2 = sqrt(rms_val2 / (SAMPLES/2) );

A 100W spotlight, a fan, a toaster oven and a computer were used as devices to test the accuracy of the home power monitor.

test_subjects

For each device the CT was hooked up to an oscilliscope as well as a DMM to measure the RMS voltage of the CT. During the testing one of the power phases was shut completely off with the exception of the device being tested. (the scope and arduino were on the other phase, isolated from the device being tested) The toaster oven also makes a good firestarter.

Kill-a-watt measuremnts

Arduino measurements

Device / Measurements Measurements on the Kill-a-Watt Measurements on the Arduino
Spotlight

spotlight

CT DMM measurement = .015Vrms
  • Voltage = 124.8Vrms
  • Current = .85Arms
  • Power = 104W
  • Power factor = .99
  • RMS measurement = 3.38
  • Voltage = .0108Vrms
  • CT Voltage Accuracy = 72%
  • Calculated Current = .7211Arms
  • Current Accuracy = 85.8%
Fan

fan

CT DMM measurement = .022Vrms
  • Voltage = 124.5Vrms
  • Current = 1.18Arms
  • Power = 134W
  • Power factor = .92
  • RMS measurement = 5.12
  • Voltage = .0164Vrms
  • CT Voltage Accuracy = 74%
  • Calculated Current = 1.092Arms
  • Current Accuracy = 92.5%
Toaster Oven

toaster

CT DMM measurement = .166Vrms
  • Voltage = 117.0Vrms
  • Current = 10.77Arms
  • Power = 1250W
  • Power factor = .99
  • RMS measurement = 55.88
  • Voltage = .1788Vrms
  • CT Voltage Accuracy = 107.8%
  • Calculated Current = 11.92Arms
  • Current Accuracy = 110.7%
Computer

computer

CT DMM measurement = .032Vrms
  • Voltage = 124.6Vrms
  • Current = 2.02Arms
  • Power = 160W
  • Power factor = .64
  • RMS measurement = 10.36
  • Voltage = .0332Vrms
  • CT Voltage Accuracy = 103.8%
  • Calculated Current = 2.21Arms
  • Current Accuracy = 109.4%



Hardware

electrical box
The analog to digital converter on the Arduino has a 10bit resolution. This means it can measure 2^10 or 1024 distinct states. A spec sheet wasn't available from the CT which makes it difficult to calculate the power conversion. The output of the CT is 0-3V and since the default range of the ADC is 0-5V the analog reference was tied to 3.3V through a 5k resistory (pictured below). For a 0-3.3V input on the ADC / 1024 bits the arduino will measure 3.2mv for every bit.

Installation of the clamp-on current transducers and mounting the arduino was the easiest part of this project. Using plexiglass, screws and stand-offs the arduino was mounted next to the breaker box. The CTs were clamped on the mains and fed through the side down to the arduino analog inputs.

The waveforms that come off the CTs are pictured on the right below. (right click for higher resolution). Between the two phases there 180degree difference as expected.

plexiglass mounts mounted board scope

Software

Arduino Sketch

The arduino samples the the input from the ADC and posts it to a cgi sitting on the linksys router using the ethernet shield. The input is sinusoidal so the rms value is calculated for 10k samples. Once the sampling is complete the arduino takes NUM_READINGS and averages them together which is posted to the cgi.

The rate at which the arduino sends data to the webserver tobe recorded in the database is about 1 measurement every 20 seconds.

#include <Ethernet.h>

#define S1_PIN 0    // input pin for first inductive sensor
#define S2_PIN 5    // input pin for second inductive sensor
#define SAMPLES 10000 // number of samples to take
#define NUM_READINGS 10 // number of readings to average
int val1 = 0;
int val2 = 0;
int reading_cnt = 0; // counter for 1 second samples
float total_rms1 = 0;
float total_rms2 = 0;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 177 };
byte gateway[] = { 192, 168, 0, 1 };
byte subnet[] = { 255, 255, 255, 0 };
byte server[] = { 192, 168, 0, 1 }; // router.springfield

Client client(server, 80);

void setup() {
  Ethernet.begin(mac, ip, gateway, subnet);
  analogReference(EXTERNAL);
  Serial.begin(9600);

  delay(1000);
  Serial.println("Connecting...");

}

void loop() {

  int max_val1 = 0;
  int max_val2 = 0;
  float rms_val1 = 0;
  float rms_val2 = 0;

  for (int cnt=0; cnt < SAMPLES; cnt++) {

    val1 = analogRead(S1_PIN);
    val2 = analogRead(S2_PIN);
    rms_val1 = rms_val1 + sq((float)val1);
    rms_val2 = rms_val2 + sq((float)val2);

  }

  rms_val1 = sqrt(rms_val1 / (SAMPLES/2) );
  rms_val2 = sqrt(rms_val2 / (SAMPLES/2) );

  total_rms1 = total_rms1 + rms_val1;
  total_rms2 = total_rms2 + rms_val2;


  reading_cnt++;
  if (reading_cnt >= NUM_READINGS) {

    float average_rms1 = total_rms1 / NUM_READINGS;
    float average_rms2 = total_rms2 / NUM_READINGS;

    Serial.print("AVG VALUES *RMS* - ");
    Serial.print(average_rms1);
    Serial.print("   ");
    Serial.println(average_rms2);

    if (client.connect()) {
      Serial.println("Sending data");

      client.print("GET /cgi-bin/relay.pl?value1=");
      client.print(average_rms1);
    Serial.print("   ");
    Serial.println(average_rms2);

    if (client.connect()) {
      Serial.println("Sending data");

      client.print("GET /cgi-bin/relay.pl?value1=");
      client.print(average_rms1);
      client.print("&value2=");
      client.print(average_rms2);
      client.println(" HTTP/1.1");
      client.println();
      client.stop();
    } else {
      Serial.println("Failed to connect to client");
    }

    total_rms1 = 0;
    total_rms2 = 0;
    reading_cnt = 0;
  }

}


Moving the data from the arduino to shared web hosting

Router on the local network

The arduino ethernet shield is not capable of DNS lookups which means it needs to talk to something that has a static ip address. Most shared web hosting consist of multiple servers that use the dns name to route http requests. To work around this issue a linksys router running the openwrt linux operating system was used to host a cgi that relays the data from the arduino to my web hosting.

use Socket;
use strict;                                               


print "Content-Type: text/html; charset=ISO-8859-1\n\n";

# Read the reading from the GET request

my @qstring = split ('&', $ENV{'QUERY_STRING'});
die "wrong number of values"  unless (scalar @qstring == 2);

# Relay the readings to power.jarv.org

socket(SH, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die $!;
my $dest = sockaddr_in (80, inet_aton('power.jarv.org'));
connect (SH, $dest) or die $!;
print SH "\nGET /post.pl?$qstring[0]&$qstring[1] HTTP/1.1\n" .
           "Host: power.jarv.org\n" .
            # BASE64 encoded username:password
           "Authorization: Basic ************************\n\n";
close SH;


The base64 encoded username-password is for minimal security on the shared web hosting server.

Storing the data in an sqlite db

For the purpose of posting the power readings on my shared web hosting a subdomain was created called power.jarv.org. This is just a paranoid step as it keeps it isolated from my main web directory.

This is the database schema. There is one table that stores the epoch time and the two values recorded for each power phase.

CREATE TABLE data (
        timestamp          INTEGER UNIQUE,
        value1             INTEGER,
        value2             INTEGER
);

Very simple, one table and one row per entry. If you can think of better ways to store this data and report please let me know, I considered using rrdtool but thought this offered the most flixibility.

!/usr/bin/perl -w
use strict;
use Data::Dumper;
use CGI qw/:standard/;
use DBI;
use constant DBNAME => [path to sqlite database]

print header;

my $ts = time();
my $v1 = param('value1')    or die "Value1 not in parameter list";
my $v2 = param('value2')    or die "Value2 not in parameter list";

my $dbargs = {AutoCommit =>1, PrintError =>1};
my $dbh = DBI->connect("dbi:SQLite:dbname=" . DBNAME, "", "",$dbargs)
                or die "Unable to connect to the db";
$dbh->do(
        "insert or ignore into data values ($ts, $v1, $v2)");
$dbh->disconnect;

This simple script will take the data which is encoded in the http query string sent from the arduino ethernet shield and relay it to another cgi sitting on power.jarv.org.

Generating the Flash Charts

Finally once the data is collected into the sqlite database the charts are periodically generated. To do this a perl script is run in a cron job that connects to the database and outputs json files for open flash charts.

I have two types of charts that are created, one is a regular line chart and the other is a summary chart that averages data over time and presents it as a bar graph.

The perl script is set up as a cron job that runs on the shared web hosting server every 10 minutes or so. Runtime statistics are presented at the top of each page.

Perl script for generating flash charts




Copyright (c) jarv.org Verbatim copying and redistribution of this entire page are permitted provided this notice is preserved.
Validate (nerd)