Monitoring my house power with an Arduino and Perl

Once I'd familiarised myself with the Arduino development kit I wanted to try and use it to monitor my electricity usage. I know you can buy commercial devices which do this but I wanted more than the current usage; I wanted to log usage over time and I wanted to involve Perl somehow. I saw the Flukso ages ago and even tried to buy one but they were out of stock.

I started looking around for current clamps but there is a bewildering number of these and nothing quite seemed to fit the bill. Eventually I decided I'd just buy one for use by a commercial power monitor (to monitor another phase) and see how I got on. I bought a Current Cost 12mm Sensor Jaw CT DC Plug but it was too big to fit into my consumer unit (my tails are just too close together) so I had to fit it around a tail in the box where the meter is, outside, and feed the cable back inside through the wall. It gave around 13V AC at the time I measured it but I would need to recify that (dropping some volts across the bridge) and then divide it with some resistors to get the 0 to 5v range the Arduino needs. I was unsure how accurate this would be and how I'd calibrate it since I could not find any specifications for the Current Cost device.

It was whilst I was thinking about this I saw someone had put a photo sensitive device over the blinking LED on their meter. This seemed a much better idea as one blink of the LED is 1 Watt and it means what I was reading should match what the electricity company billed me for exactly. Fortunately for me, Yorkshire Electricity had replaced my ancient meter with a new one with a flashing LED only recently.

I bought a TSL 261 IR light-to-voltage optical sensor and set it up on my breadboard with a switched LED sat next to it. Powered off the 5V rail I got around 3.9V when my LED was pushed right up to the sensor and on and covered to stop ambient light. All looked good so I soldered the TSL onto some cable, blue tacked it over the meter LED and ran the wires through the wall back into the house. Unfortunately my problems started here as the voltage developed when the meter LED flashed was no where near the best 3.9V I was getting in my test setup. The LED in the meter is much smaller and there is some clear plastic over the top of it both of which obviously reduced the quantity of sensed light.

Connected to the Arduino digital pin 3 and set with an internal pullup the Arduino would not see a FALLING edge which I assumed was down to the voltage developed by the TSL not getting HIGH enough. I did get it working by using the output from the TSL to drive a much smaller LED in an opto isolator I had sat lying around then using the other side of the optio isolator to give me 5V to the Arduino but I settled on a much more simple solution with a transistor as shown in the diagram below.

The script for the Arduino:

const int  dataLedPin    =  4;  // LED indicating sensor data is received
const int  logLedPin     =  5;  // LED flashes during a log attemp

const int logInterrupt = 1; // ATmega 168 and 328 - interrupt 0 = pin 2, 1 = pin 3
const int interruptPin = 3;

#define LOOPDELAY 60000

// count of pulses from the electricity meter in LOOPDELAY ms
// a byte can hold 255 pulses a minute and if we hit this we'd be
// consuming a hell of a lot. If LOOPDELAY is more than a minute
// a byte might not be big enough but if we increase the size of the
// sensor storage type we'd have to protect all reads/writes to it
// from interrupts - a byte is all that can be read/written in a single instruction
volatile byte sensor = 0;  // Counts power pulses in interrupt, 1 pulse = 1 watt
unsigned long total = 0;  // Total power used since the sketch started ???
//unsigned long last_reading = 0;


void setup(void)
{
  Serial.begin(19200);  // opens serial port, sets data rate to 19200 bps
 
 
  pinMode(dataLedPin, OUTPUT);    // LED interrupt indicator initialization
  pinMode(logLedPin, OUTPUT);

  pinMode(interruptPin, INPUT);  
  // enable 20k pullup resistor:  
  digitalWrite(interruptPin, HIGH);  
  attachInterrupt(logInterrupt, interruptHandler, FALLING);
  interrupts();  
 
  Serial.println("Start");
}

void loop(void)
{
  // flash the data LED
  digitalWrite(dataLedPin, HIGH);
  delay(50);
  digitalWrite(dataLedPin, LOW);  
 
  // reading/writing volatile data shared with an ISR needs to
  // be done with interrupts disabled unless the data can be read
  // in an atomic operation e.g., a byte
  if( sensor != 0 ) {
     digitalWrite(logLedPin, HIGH);
     Log();
     digitalWrite(logLedPin, LOW);
     //last_reading = sensor;
  } else {
      Serial.println(sensor, DEC);
  }

  // wait a while - interrupts do fire during delay
  delay(LOOPDELAY);
}

void Log()
{
    unsigned long sensor_count;
    uint8_t oldSREG = SREG;   // save interrupt register
    cli();                    // prevent interrupts while accessing the count  
    sensor_count = sensor; //get the count from the interrupt handler
    sensor = 0;           //reset the watts count
    //last_reading = 0;
    SREG = oldSREG;           // restore interrupts

    total += sensor_count; //total watts counter
    Serial.print(sensor_count, DEC);
    Serial.print(',');
    Serial.println(total);      
}

// interrupt from interruptPin
// a pulse from the meter LED, just increment sensor count
// one pulse = 1 watt
// NB interrupts are automatically disabled in a ISR
void interruptHandler() {
  sensor += 1;
}

Some Perl to read the serial port and write the data to a file:

use strict;
use warnings;

use Win32::SerialPort qw( :STAT 0.19 );

my $port = Win32::SerialPort->new('COM12');

my $time = time;

if( ! defined($port) ) {
        die("Can't open COM12: $^E\n");
}

my $outfd;
open ($outfd, ">>", "log.txt") or die "Failed to open output file - $!n";

my $output = select(STDOUT);
$|++;
select($outfd);
$|++;
select $output;

$port->initialize();

$port->baudrate(19200);
$port->parity('none');
$port->databits(8);
$port->stopbits(1);
$port->write_settings();
$port->are_match("\n");


while(1) {
    my $char = $port->lookfor();
    if ($char) {
        $char =~ s/\xd//g;
        my $now = time;
        print $char, "\n";
        my ($period_watts, $total_watts) = split(",", $char);
        if ($total_watts) {
            print $outfd "$now,$period_watts,$total_watts\n";
        }
    }
}
$port->close();
exit(0);

Pushing the log file Perl generates into a spreadsheet I get some interesting results:

Obviously we were at home between 7pm and 10:15am next day. The large usage between 7pm and 8:15pm then again between 10:15pm and 10:40pm which reaches 60W per minute are the spin drier (what a surpise). You can see the kettle going on at around 9:30am (not me, I was already at work - honestly). Between 10:30pm and 07:00am we were asleep with no lights on and you can see we are using around 5W per minute (2 televisions and 2 hard disc recorders on standby, cisco router, wireless router, 2 NAS devices, broadband modem, my laptop running the Perl, a few clocks in microwave and cooker, a couple of dawn to dusk lights and the fridge) then at 07:00am the usage goes up slightly as the central heating pump starts, I get out of bed shortly after and turn some lights on etc. There is an interesting little pattern at 12:30am, 3:30am, 5:50am, 11:00am and again at 01:50pm - perhaps the fridge.

Where to next? At present to get the readings I need to connect my laptop to the USB on the Arduino and leave it running. I've got a data logger shield including a real time clock and an SD card slot on order however, I like the Flukso idea so... I've got a couple of Fonera wireless routers which run Linux and are apparently hackable. If I could hack into these I could connect the Arduino serial port to the Fonera serial port and wirelessly upload the power readings anywhere and graph them real time. Here is where I've run into a problems as I've not been able to hack into my more modern Fonera as yet - see hacking a fonera 2100A/B/C with firmware 0.7.0 r4. I have a USB to serial lead which I could use for the serial hack but I've not tried that yet and the Kolofonium hack did not work. If you know who to hack a Fonera firmeware 0.7.0 r4 I'd love to hear from you. Obviously when I get the data uploaded wirelessly to a machine then I can look at Perl graphing modules to plot the results then I'd like to look for patterns in usage.

When I get this working wirelessly I'd like to move away from the Arduino Duemilanove development board and just take an ATMega chip and build this standalone (and pointers appreciated). Also, since attending the LPW, I'd like to experiment more with the Perl and IO::Async after the excellent talk by Paul Evans (and thanks for the pointers in the pub afterwards Paul).

Merry Christmas.

Christmas day:

Christmas day usage

The cost of cooking a Christmas Turkey in an electric oven - oven on 8:00am to 01:30pm! It was a 6kb Turkey balentine - 2 pheasants inside a 5lb duck inside a turkey - very nice. It would have been nice to know all the stuffing was at one end (the other end to which I started cutting!).

Comments

Data Shield Additions

There are updates to this at New Data Shield.