Project

General

Profile

Electricity consumption meter

by Petri Raitio

General setup

Some people (including jcw) have shared their experiences reading a rotating disc type electricity meter. My house happens to have a remotely readable electronic meter so I could not use the existing projects as a start. My meter indicates cumulative consumption with an LCD and current consumption with a LED that blinks 10,000 times per kWh consumed. So by counting the blinks and timing their interval I could determine power usage.

Enermet E120Lt
Enermet E120Lt

Choosing a sensor

I first tried to read the light pulses with a Lux Plug, but the pulses are short (I’d guess some milliseconds) and I was missing some of them — at 10 kW consumption, 100,000 pulses are produced per hour, or almost 30 per second. So I looked at LDRs and photodiodes and finally settled on a phototransistor; they have very fast turn-on times in the microsecond range. The model I chose was BPV11 (mostly because it was the only one in stock at a local shop).

BPV11 phototransistor
BPV11 phototransistor

Prototyping

After (rather unsuccessfully) trying a number of simple transistor circuits to create a digital indicator of the LED state I finally figured out that a JeeNode analog input converts to digital fast enough to recognize short pulses. I connected the emitter to GND and collector to AIO, and I cut off the base pin. Then I taped the phototransistor in front of the blinking LED. The pull-up resistor of the analog input keeps the signal high when it is dark (LED is off) and the phototransistor pulls it down when it is light (LED is on). After some testing, I decided that values under 100 indicate switching on and values over 200 switching off.

Mathematics

After reaching this point, calculating current power consumption was simple. I used millis() to time the interval between blinks. To get good precision, I wait until at least 1000 milliseconds have passed and count how many blinks have occurred. Then wattage equals the number of blinks times 0.1 (10,000 blinks/kWh is 0.1 Wh/blink) times 3.6E6 (3,600,000 milliseconds per hour) divided by interval (length in milliseconds).

Final setup

I wanted to keep final installation just as modular as everything in Jee Labs. I packed a JeeNode in a small plastic case. A single 6-pin male header supplies power via PWR and GND as well as the phototransistor connection via AIO and GND. I used round connectors because they are simple to mount through a hole drilled in the plastic. For power I used a 5.5 mm power plug.

JeeNode case
JeeNode case

The phototransistor is in a separate small case, connected via a 3.5 mm stereo jack — I’m using a standard audio cable to connect the cases. The system is completed with a simple 600 mA multi-voltage power supply that I set at 5 Volts. The phototransistor shares dimensions with 5 mm LEDs and was mounted in its case using a LED collar. Double-sided tape keeps the case in place in front of the blinking LED, right next to the LCD.

Whole setup
Whole setup

Sensor end code

This is the code to read the phototransistor, convert readings into current power usage in Watts, and to send it forward over the radio. First, the include file kWh.h contains a few definitions:

#define LANG_ELECTRICITY 'E'
#define MESG_ELEC_CURRENT 'c'

typedef struct {
    char lang;
    char mesg;
    long data;
} Packet_t;

The main program is relatively simple and uses the rf12_easySend() mechanism. I originally had a delay() in the loop but I noticed that sometimes pulses were missed, so I removed it, as power usage is not an issue.

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

Port inputPort(4);

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(); // Yes, twice - see http://talk.jeelabs.net/topic/169
    rf12_easyInit(3); // Send value at most every 3 seconds

    last = millis();
}

void loop() {
    static boolean ledOn = false;
    int data = inputPort.anaRead();

    rf12_easyPoll();
    if (!ledOn && data < 100) {
        ledOn = true;
    } else if (ledOn && data > 200) {
        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 10000 per kWh, or 0.1 Wh each
        // One hour has 3.6M milliseconds
        long watts = nBlinks * 0.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);
}

Display code

The code for receiving Watt readings via the radio and displaying them on an LCD is shown below. I removed many details (hourly and daily averages etc) to keep the program more understandable.

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

PortI2C lcdPort(2);
LiquidCrystalI2C lcd(lcdPort);

void setup() {
    lcd.begin(16, 2);
    rf12_config(); rf12_config();

    lcd.setCursor(0, 0);
    lcd.print("Power meter");
}

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) {
    lcd.setCursor(0, 1);
    lcd.print("Usage: ");
    lcd.print(watts);
    lcd.print(" W");
}