Skip to content
Oct 5 12

Power monitor update

by Chris Jennings

Well after setting the original system up it only lasted 4 days before the battery went flat. It appears I shouldn’t leave the radio on all the time on the remote node otherwise it chews through the battery very quickly. So I searched through the JeeNode examples to look for low power options.

One of the issues with low power mode for the ATmega is that it doesn’t maintain time accuracy, and the JeeNode doesn’t have a RTC (I’m thinking of getting one). So the best thing I think I can do is to just turn the radio off when not used. The other option is to tone down update rate, I’ll probably look at that after I’ve collected more baseline data.

So after working with the RadioBlip code from the Jeelib examples I came up with this:

#include <Ports.h>
#include <RF12.h>
#include <avr/sleep.h>
#include “kWh.h”

#define SEND_MODE 1

volatile bool adcDone;

// for low-noise/-power ADC readouts, we’ll use ADC completion interrupts
ISR(ADC_vect) { adcDone = true; }

// this must be defined since we’re using the watchdog for low-power waiting
ISR(WDT_vect) { Sleepy::watchdogEvent(); }

static byte vccRead (byte count =4) {
set_sleep_mode(SLEEP_MODE_ADC);
ADMUX = bit(REFS0) | 14; // use VCC as AREF and internal bandgap as input
bitSet(ADCSRA, ADIE);
while (count– > 0) {
adcDone = false;
while (!adcDone)
sleep_mode();
}
bitClear(ADCSRA, ADIE);
// convert ADC readings to fit in one byte, i.e. 20 mV steps:
//  1.0V = 0, 1.8V = 40, 3.3V = 115, 5.0V = 200, 6.0V = 250
return (55U * 1024U) / (ADC + 1) – 50;
}

class Port;
Port inputPort(1);
static unsigned long last;
static unsigned long totalblinks;

void setup() {
Serial.begin(57600);
inputPort.mode2(INPUT); // Set AIO mode as input
inputPort.digiWrite2(1); // Activate pull-up resistor for AIO

//rf12_config();
rf12_config(); // Apparently this is necessary
rf12_easyInit(1); // Send value at most every 3 seconds
last = millis();
rf12_control(0xC040); // set low-battery level to 2.2V i.s.o. 3.1V
rf12_sleep(RF12_SLEEP);
}

void loop () {

static boolean ledOn = false; // Variable to indicate LED status
int data = inputPort.anaRead();
rf12_easyPoll();
if (!ledOn && data > 760) {        // After testing I found the switching point was 750
ledOn = true;
} else if (ledOn && data < 740) {
ledOn = false;
ledBlink();
totalblinks++;
}
}

void ledBlink() {
static int nBlinks = 0;
unsigned long time = millis();
unsigned long interval = time – last;

nBlinks++;
if (interval < 0) { // millis() overflow
last = time;
nBlinks = 0;
} else if (interval > 1000) { // 1+ sec passed
// Blinks are 1000 per kWh, or 1 Wh each
// One hour has 3.6M milliseconds
long watts = nBlinks * 1 * 3.6E6 / interval;

byte vcc = vccRead();
wattSend(watts,totalblinks,vcc);

last = time;
nBlinks = 0;
}
}

static void wattSend(long watts,long blinks, long vcc) {
Packet_t packet;
packet.lang = LANG_ELECTRICITY;
packet.mesg = MESG_ELEC_CURRENT;
packet.data = watts;
packet.blinks = blinks;
packet.vcc = vcc;
rf12_sleep(RF12_WAKEUP);
while (!rf12_canSend())
rf12_recvDone();
rf12_sendStart(0, &packet, sizeof packet);
rf12_sendWait(SEND_MODE);
delay(10);
rf12_sleep(RF12_SLEEP);
}

The code also contains parts which measures the Vcc voltage so I can determine when the battery pack is starting to get low.

The receiver code needed to change too. I wanted to include not just power, but the number of blinks and the new Vcc measurement. Here is the new code:

#include <Ports.h>
#include <RF12.h>
#include “kWh.h”

void setup() {
Serial.begin(57600);
//rf12_config();
rf12_config();
}

void loop() {
if (rf12_recvDone() && rf12_crc == 0 && rf12_len == sizeof (Packet_t)) {
Packet_t packet = *(Packet_t *) rf12_data;

if (packet.lang == LANG_ELECTRICITY) {
if (packet.mesg == MESG_ELEC_CURRENT) {
wattShow(packet.data,packet.blinks,packet.vcc);
}
}
}
else {
delay(10);
}
}

static void wattShow(long watts,long blinks,long vcc) {
Serial.print(watts);
Serial.print(“,”);
Serial.print(blinks);
Serial.print(“,”);
Serial.println(vcc);
}

I also changed the receiving Perl script to put the data into a MySQL database instead of dumping to a text file.

use strict;
use warnings;
use DBI;
use LWP::Simple;

use Win32::SerialPort qw( :STAT 0.19 );
use POSIX qw/strftime/;

my $port = Win32::SerialPort->new(‘COM9′);

my $time = time;

# MYSQL CONFIG VARIABLES
my $host = “192.168.1.101″;
my $database = “powermonitor”;
my $tablename = “data”;
my $user = “powermonitor”;
my $pw = [the password];

if( ! defined($port) ) {
die(“Can’t open COM9: $^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(57600);
$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;
if($char =~ m/(\d+),(\d+),(\d+)/){
my $dbh = DBI->connect(‘DBI:mysql:’.$database.’;host=’.$host, $user, $pw, { RaiseError => 1});
my $thedate=strftime(‘%Y-%m-%d %H:%M:%S’,localtime);
my $pvdate = strftime(‘%Y%m%d’,localtime);
my $pvtime = strftime(‘%H:%M’,localtime);
my $pvoutput = “get http://pvoutput.org/service/r2/addoutput.jsp?key=[the api number]&sid=[the sid number]&d=$pvdate&pt=$pvtime&ip=$1″;

#my $content = get $pvoutput;

my $sth=$dbh->prepare(“INSERT INTO $tablename VALUES(‘$thedate’,'$1′,’$2′,’$3′)”)
or die “Can’t prepare SQL statement: $DBI::errstr\n”;
$sth->execute
or die “Can’t execute SQL statement: $DBI::errstr\n”;
$dbh->disconnect();
}
sleep (1);
}
}
$port->close();
exit(0);

This is the database structure:

– Server version: 5.5.24

– Database: `powermonitor`

– Table structure for table `data`

CREATE TABLE IF NOT EXISTS `data` (
`date` datetime NOT NULL,
`watts` int(11) NOT NULL,
`blinks` int(11) NOT NULL,
`vcc` int(11) NOT NULL,
PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Now finally I wanted to graph the data but I haven’t found a good graph program for the front-end. So I’m using PVoutput, I’m not sure how it will work but I wrote this script to dump usage data for 5 minute intervals. It calculates averages and power usage data for the last 5 minute period, I initiate it using cron.

use strict;
use warnings;
use DBI;
use LWP::Simple;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);

use POSIX qw/strftime/;

# MYSQL CONFIG VARIABLES
my $host = “192.168.1.101″;
my $database = “powermonitor”;
my $tablename = “data”;
my $user = “powermonitor”;
my $pw = “powermonitor”;

my $thetime=int(time() / 300) * 300;
my $thetime_5=$thetime-300;
my $thetimestring=strftime(‘%Y-%m-%d %H:%M:%S’,localtime($thetime));
my $thetime_5string=strftime(‘%Y-%m-%d %H:%M:%S’,localtime($thetime_5));

my $dbh = DBI->connect(‘DBI:mysql:’.$database.’;host=’.$host, $user, $pw, { RaiseError => 1});

my $sth=$dbh->prepare(“select DATE_FORMAT(MIN(date), ‘%Y-%m-%d’) as day, max(blinks)-min(blinks) as Wh, round(avg(watts)) as Watts, Date_format(Min(date), ‘%H:%i’) as time from data where date > ‘$thetime_5string’ AND date < ‘$thetimestring’ GROUP BY ( 12 * HOUR( date ) + FLOOR( MINUTE( date ) / 5 ));”)
or die “Can’t prepare SQL statement: $DBI::errstr\n”;
$sth->execute
or die “Can’t execute SQL statement: $DBI::errstr\n”;
my @result = $sth->fetchrow_array();
my $pvdate= substr($result[0], 0, 4).substr($result[0],5,2).substr($result[0],8,2);
my $pvwatth= $result[1];
my $pvwatt= $result[2];
my $pvtime= $result[3];
print “$pvdate $pvwatth $pvwatt $pvtime\n”;
$sth->finish();
$dbh->disconnect();

my $url = “http://pvoutput.org/service/r2/addstatus.jsp”;

my $ua = LWP::UserAgent->new( timeout => 120 );
$ua->default_header(‘X-Pvoutput-Apikey’=> “edaf9593007119df34f393acbd36703b7476457b”);
$ua->default_header(‘X-Pvoutput-SystemId’=> “12265″);

my $req = POST $url, [
d => $pvdate,
t => $pvtime,
v3=> $pvwatth,
v4=> $pvwatt
];

my $content = $ua->request($req)->as_string;

print $content;

I’ll post a link to my PVOutput data once it looks ok.

Oct 1 12

Home Power Monitor

by Chris Jennings

I’ve wanted to do a home power monitoring project for some time. I was using a Clipsal Cent-a-meter a while ago to track power usage when I lived in Victoria but I didn’t have much luck with the newer model. It didn’t have a good range and seemed to lose connection between the base and sending unit. So I thought I would go DIY.

I looked at lots of different systems including PIC microcontrollers but I decided to give the JeeNode and JeeLink combination a try. It seemed like a good choice for the following reasons:

  • Arduino compatible
  • Low power (3.3V)
  • Integrated wireless
  • Easy interfacing with a PC (JeeLink)
  • Expandable

In our house we have a smart meter which has a blinking red light for every Watt Hour of power used. So by measuring the length of the pulse you can have an indication of instantaneous power usage. This saves using clip on CTs which need to be installed by an electrician. The other advantage of using the smart meter is that this is the actual power usage I am being charged for. The only problem with this is that in Tasmania we have two different tariffs, one for normal usage and one for heating (cheaper). My system doesn’t take into account the tariffs and can only measure total usage. It also could not be used with electricity generation because the smart meter only blinks for imported power, if you are generating then the actual power usage will be higher than the imported.

So I went to Modern Device and ordered the following:

The JeeNode will be used to monitor the blinking light, which will send data to the JeeLink (plugged into my HTPC running Ubuntu) and the USB BUB II is required for programming the JeeNode.

I soldered the JeeNode and JeeLink up when they arrived on the coffee table while watching something mindless on TV. It had been a while since I had done proper soldering and with a good temperature controlled iron it was pretty easy. I should get some good quality solder though!

I tested the JeeNode and JeeLink and they worked first time. Now to look for some code.

I ended up using this code http://jeelabs.net/projects/cafe/wiki/Electricity_consumption_meter I modified it to suit the 1Wh blinks and using a LDR.

Here is my version of the transmitter code:

// Reading Comparator input

#include <Ports.h>
#include <RF12.h>
#include "kWh.h"

class Port;
Port inputPort(1);
static unsigned long last;

void setup() {
    inputPort.mode2(INPUT); // Set AIO mode as input
    inputPort.digiWrite2(1); // Activate pull-up resistor for AIO
  
    rf12_config();
    rf12_config(); // Apparently this is necessary
    rf12_easyInit(3); // Send value at most every 3 seconds
    last = millis();
}

void loop () {
    static boolean ledOn = false; // Variable to indicate LED status
    int data = inputPort.anaRead();
    rf12_easyPoll();
    if (!ledOn && data > 750) {        // After testing I found the switching point was 750
        ledOn = true;
    } else if (ledOn && data < 750) {
        ledOn = false;
        ledBlink();
    }
}

void ledBlink() {
    static int nBlinks = 0;
    unsigned long time = millis();
    unsigned long interval = time - last;

    nBlinks++;
    if (interval < 0) { // millis() overflow
        last = time;
        nBlinks = 0;
    } else if (interval > 1000) { // 1+ sec passed
        // Blinks are 1000 per kWh, or 1 Wh each
        // One hour has 3.6M milliseconds
        long watts = nBlinks * 1 * 3.6E6 / interval;

        wattSend(watts);
        last = time;
        nBlinks = 0;
    }
}

static void wattSend(long watts) {
    Packet_t packet;
    packet.lang = LANG_ELECTRICITY;
    packet.mesg = MESG_ELEC_CURRENT;
    packet.data = watts;
    rf12_easySend(&packet, sizeof packet);
}

And here is my version of the JeeLink receiver code

#include <Ports.h>
#include <RF12.h>
#include "kWh.h"

void setup() {
    Serial.begin(57600);
    rf12_config();
    rf12_config();
}

void loop() {
    if (rf12_recvDone() && rf12_crc == 0 && rf12_len == sizeof (Packet_t)) {
        Packet_t packet = *(Packet_t *) rf12_data;

        if (packet.lang == LANG_ELECTRICITY) {
            if (packet.mesg == MESG_ELEC_CURRENT) {
                wattShow(packet.data);
            }
        }
    } 
    else {
        delay(10);
    }
}

static void wattShow(long watts) {
    Serial.print("Usage: ");
    Serial.print(watts);
    Serial.println(" W");
}

I connected 4 AA NiMh batteries as the power supply to the JeeNode and sat it on top of the smart meter. The LDR is connected between the AI pin and ground of one of the ports.

Pretty isn’t it?

The LDR is right next to the LED

The JeeLink is connected to my PC running Mythbuntu, which is my HTPC.

Ubuntu is running Perl code to read the serial port and dump it to a file which timestamps the incoming data.

use strict;
use warnings;
use Device::SerialPort;
use POSIX qw/strftime/;

my $port = Device::SerialPort->new('/dev/ttyUSB0');
my $time = time;

if( ! defined($port) ) {
        die("Can't open /dev/ttyUSB0 $^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->baudrate(57600);
$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 ($watts)= $char =~ /(\d+) W/;
        print strftime('%d-%m-%Y %H:%M:%S',localtime);
        print ",$watts\n";
        print $outfd strftime('%d-%b-%Y %H:%M:%S',localtime);
        print $outfd ",$watts\n";
    }
}
$port->close();
exit(0);

It works and the next step is to make a nice graphing utility. I’ll post again when I have some pretty graphs. At the moment I’m just dumping all the data into Excel.

Performance Optimization WordPress Plugins by W3 EDGE