Power Meter Monitor

A recent problem with the air conditioned inspired me to monitor the power usage in my house. The problem is rather simple, count infrared pulsed coming out of the utility power meter. Each time a kilowatt of power is consumed, the power meter emits a 10ms infrared pulse.

This problem has been solved in a variety of ways already. My approach using the mbed board has the following advantages:

  • mbed connects to router directly via ethernet port and gets IP using DHCP.
  • mbed interfaces directly to pachube.com for data collection and graphing (no need to have computers in-house to collect data).
  • Cost is lower that arduino + ethernet shield.

Here is the result:

https://api.pachube.com/v2/feeds/52908/datastreams/1.png?width=530&height=250&colour=%23f15a24&duration=3hours&show_axis_labels=true&detailed_grid=true&timezone=UTC

The hardware is trivial. Connect an ethernet socket to the mbed board. Connect a Radio Shack IR photodiode to pin 30 via 1K pull-up resistor to 3.3v. Using a 12V supply into the Vin pin.

The software is simple as well:

#include "mbed.h"
#include "EthernetNetIf.h"
#include "HTTPClient.h"

extern "C" void mbed_reset();

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);

InterruptIn pulse(p30);
static volatile long count = 0;

Timer timer;
Ticker ticker;
Ethernet link;
EthernetNetIf eth;

void read_pulses();
void post_update(int value);
void count_pulse();
void check_link();

#define USE_ETH 1

// ****************************************************
//  Watchdog timer
// ****************************************************

class Watchdog 
    {
public:
	// Load timeout value in watchdog timer and enable
    void kick(float s) 
        {
        LPC_WDT->WDCLKSEL = 0x1;                // Set CLK src to PCLK
        uint32_t clk = SystemCoreClock / 16;    // WD has a fixed /4 prescaler, PCLK default is /4
        LPC_WDT->WDTC = s * (float)clk;
        LPC_WDT->WDMOD = 0x3;                   // Enabled and Reset
        kick();
        }

	// "kick" or "feed" the dog - reset the watchdog timer
	// by writing this required bit pattern
    void kick() 
        {
        LPC_WDT->WDFEED = 0xAA;
        LPC_WDT->WDFEED = 0x55;
        }
    };

// Setup the watchdog timer
Watchdog wdt;


// ****************************************************
//  
// ****************************************************
int main() 
    {

    //
    //  Load the watchdog to expire in 10 sec
    //
    wdt.kick(15);
    
    //
    //  Start the program
    //
#ifdef USE_ETH
    printf("main: setting up eth...\r\n");
    EthernetErr ethErr = eth.setup();
    printf("\r\n");

    if(ethErr)
        {
        printf("main: eth %d in setup.\r\n", ethErr);
        while(1);
        }
#endif

    //
    //  Schedule the reading and update of counter
    //
    wdt.kick(10);
    timer.start();
    ticker.attach(&read_pulses, 60);
    pulse.rise(&count_pulse);

    //
    //  Listen indefinitely
    //
    printf("main: staritng idle loop\r\n");
    while(true)
        {   
#ifdef USE_ETH
        Net::poll();
#endif
        wdt.kick();

        wait(1);
        led1 = !led1;
        }
    }

// ****************************************************
//
//	
//
// ****************************************************
void read_pulses()
    {
    static int already_reading = 0;
    
    printf("read_pulses: at %f\r\n", timer.read());
        
   if (already_reading)
        {
        printf("read_pulses: reading already in progress\r\n");
        mbed_reset();
        return;
        }

    already_reading = 1;     

    led2 = 1;
    int pulse_count = count;
    count -= pulse_count;
    
    printf("read_pulses: found %d pulses\r\n", pulse_count);

    post_update(pulse_count);
    led2 = 0;

    already_reading = 0;     
    } 

// ****************************************************
//
//	Send data over to patchub
//
// ****************************************************
void post_update(int value)
    {
#ifdef USE_ETH

    if (!link.link())
        {
        printf("read_pulses: lost ethernet connection\r\n");
        mbed_reset();
        }

    // copy API key from settings
    const string apiKey = "xxxxxxxxxxxxxxx";

    // use feed ID
    const string feedID = "nnnnn";

    // Create a value 
    char  value_str[100];
    sprintf(value_str, "%d", value); 
    string data(value_str);

    // for authentication, API key is set in client header
    HTTPClient client;
    client.setRequestHeader("X-PachubeApiKey", apiKey);
    
    // text object holds data to be posted
    HTTPText csvContent("text/csv");
    csvContent.set(data);
    
    // uri for post includes feed ID and datastream ID
    string uri = "http://api.pachube.com/v1/feeds/" + feedID + ".csv?_method=put";
        
    // result should be 0 and response should be 200 for successful post
    HTTPResult result = client.post(uri.c_str(), csvContent, NULL);
    
    int response = client.getHTTPResponseCode();
    if (response != 200)
        {
        printf("post_update: error in  responce %d\r\n", response);
        mbed_reset();
        }
#endif
    }

// ****************************************************
//
//	Interrupt which counts pulses
//
// ****************************************************
void count_pulse()
    {
    led3 = !led3;
    count++;
    }

Here is how the project looks right now:

/media/uploads/igfarm/screen_shot_2012-03-24_at_5.11.35_pm.png

and a close up of the weatherproof case:

/media/uploads/igfarm/screen_shot_2012-03-24_at_5.12.56_pm.png


Please log in to post comments.