Nov 14

Electricity Meter Usage over MQTT

Last week some guy came around and switched out our old meters for new ones. Not "smart" meters, just new dumb meters, which is fine as the current crop of "smart" meters don't seem to benefit the customer, rather just the company.

A couple years back we used to have a Currentcost device which I had hacked with an Arduino to put data on the network but I always had issues with phantom loads so the transmitter stopped working I never did anything about sorting it out.

Anyway, the new meters have a red LED impulse output that flashes 1600 times per kWh used. These are just about the perfect way to hack into your meter - no dangerous (and potentially illegal) connections required and super easy to do. It should also be very accurate since the meters are certified correct.

Here's a photo of the meter box showing the new meters and the sensors (with black tape over them). The two meters are on the outside and the ripple control in the middle. The Arduino and enclosure are just outside the photo to the right mounted on the wall, I thought it best to keep "odd looking things" out of way of the power company.

Electricity meter box with Arduino light sensors counting impulse flashes then broadcasting over MQTT

For the controller I used a Freetronics EtherTen which is an Arduino Uno compatible with built in Ethernet, these are great boards that we use a lot of here. The sensor is just a Photocell on the end of a small twin core wire connected back to a Prototyping Shield with a resistor.

The sensors are covered in black duct tape to keep out ambient light and connected up to the two interrupt digital input pins on the Arduino to make counting the pulses really easy (see the code below).

I already use MQTT (which is a simple network publish/subscribe system, great for machine to machine (M2M) communication) for some home automation around the house so publishing the electricity usage there made perfect sense. The EtherTen publishes the data on the network and any number of clients can subscribe to this and receive updates. Currently there's just a Python server which logs the data into a redis db for historic graphing. Here's an example graphs;

Example electricty usage

You can see that our house and its young family has a long way to go with saving energy. The blue graph is the standard meter and the red graph is the water heater circuit. This graph is actually higher than normal, it was a cold day but not cold enough to have the log burner on which heats up the entire house. You can see overnight the electric heaters in the children's bedrooms cycling on and off and at 6am the heat pump (air conditioner) ramping up to heat up the living area.

Of course you could also add any other network device and have it subscribe to the electricity usage MQTT topic. Perhaps I'll build a little RGB LCD display and have it show the current usage with a colour that reflects how well (or badly) we're doing at saving energy.

Here's the code I've been playing around with, suitable for Arduino 1.0+ and the not quite latest Arduino MQTT client (there was an update released this weekend which I has a small API change - easy fix).

/*
* By Hadley Rich 
* Released under the MIT license.
* Some ideas taken from arduino.cc example code.
*/

#include <stdio.h>
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>

byte mac[6] = { 0x90, 0xA2, 0xDA, 0x44, 0x34, 0x28 };
char macstr[18];

const int chan1_pin = 2;
const int chan2_pin = 3;
const float w_per_pulse = 0.625;
const unsigned long ms_per_hour = 3600000UL;

unsigned int chan1_count = 0;
unsigned int chan2_count = 0;
unsigned long report_time = 0;
unsigned long chan1_first_pulse = 0;
unsigned long chan1_last_pulse = 0;
volatile byte chan1_pulse = 0;
unsigned long chan2_first_pulse = 0;
unsigned long chan2_last_pulse = 0;
volatile byte chan2_pulse = 0;

PubSubClient client("sodium.nice.net.nz", 1883, callback);

void callback(char* topic, byte* payload, unsigned int length) {
}

void chan1_isr() {
  chan1_pulse = 1;
}

void chan2_isr() {
  chan2_pulse = 1;
}

boolean connect_mqtt() {
  Serial.print("MQTT...");
  if (client.connect(macstr)) {
    Serial.println("connected");
    char topic[35];
    snprintf(topic, 35, "house/node/%s/state", macstr);
    client.publish(topic, "start");
    return true;
  }
  Serial.println("Failed.");
  return false;
}

void setup() {
  pinMode(chan1_pin, INPUT);
  pinMode(chan2_pin, INPUT);
  attachInterrupt(0, chan1_isr, RISING);
  attachInterrupt(1, chan2_isr, RISING);
  Serial.begin(9600);
  delay(1000);
  snprintf(macstr, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.print("DHCP (");
  Serial.print(macstr);
  Serial.print(")...");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed.");
    while(true);
  }
  Serial.println(Ethernet.localIP());
}

void loop() {
  char topic[35];
  char value[6];
  static unsigned int chan1_watts;
  unsigned long chan1_delta; // Time since last pulse
  static unsigned int chan2_watts;
  unsigned long chan2_delta; // Time since last pulse

  if (!client.connected() && !connect_mqtt()) {
    return;
  }
  client.loop();

  if (millis() - chan1_last_pulse > 60000) {
    chan1_watts = 0;
  }
  if (millis() - chan2_last_pulse > 60000) {
    chan2_watts = 0;
  }

  if (millis() - report_time > 5000) {
    chan1_delta = chan1_last_pulse - chan1_first_pulse;
    if (chan1_delta && chan1_count) {
      chan1_watts = (chan1_count - 1) * w_per_pulse * ms_per_hour / chan1_delta;
      chan1_count = 0;
    }
    chan2_delta = chan2_last_pulse - chan2_first_pulse;
    if (chan2_delta && chan2_count) {
      chan2_watts = (chan2_count - 1) * w_per_pulse * ms_per_hour / chan2_delta;
      chan2_count = 0;
    }
    if (!(report_time == 0 && chan1_watts == 0 && chan2_watts == 0)) {
      snprintf(topic, 35, "house/power/meter/1/current");
      snprintf(value, 6, "%d", chan1_watts);
      client.publish(topic, value);
      snprintf(topic, 35, "house/power/meter/2/current");
      snprintf(value, 6, "%d", chan2_watts);
      client.publish(topic, value);
      Serial.print("Chan1 ");
      Serial.println(chan1_watts);
      Serial.print("Chan2 ");
      Serial.println(chan2_watts);
      report_time = millis();
    }
  }

  if (chan1_pulse == 1) {
    chan1_count++;
    chan1_pulse = 0;

    chan1_last_pulse = millis();
    if (chan1_count == 1) { // was reset
      chan1_first_pulse = chan1_last_pulse;
    }

    snprintf(topic, 35, "house/power/meter/1/usage");
    client.publish(topic, "0.625");
  }
  if (chan2_pulse == 1) {
    chan2_count++;
    chan2_pulse = 0;

    chan2_last_pulse = millis();
    if (chan2_count == 1) { // was reset
      chan2_first_pulse = chan2_last_pulse;
    }

    snprintf(topic, 35, "house/power/meter/2/usage");
    dtostrf(w_per_pulse, 4, 1, value);
    client.publish(topic, "0.625");
  }

  int dhcp_status = Ethernet.maintain();
  /*
    returns:
    0: nothing happened
    1: renew failed
    2: renew success
    3: rebind fail
    4: rebind success
  */
  if (dhcp_status) {
    long now = millis();
    Serial.println("DHCP Lease");
  }
}

All that's left to do now is tidy up the Ethernet cable... Any questions or comments please feel free to leave a comment below.

14 responses to "Electricity Meter Usage over MQTT"

  1. Hey Hads, interesting stuff. We've got the smart meters too but no flashing led unfortunately.
    I'd love to see your hot water usage over a full 24 hour period though.
    I've got a Currentcost meter connected to our hot water cylinder too. I'd be interested to know if our power usage is similar to yours. See the charts on the bottom of http://www.spudooli.com/house.php
    Even though our cylinder is wrapped it still cycles on and off about 3 times an hour. It does it all through the night when we're not using any hot water. I think there must be something wrong but have nothing to compare it to.
  2. Hey Spudooli,

    Thanks for the comment. Those two red blips are the hot water usage for an entire 24 hours. Our hot water is on night rate so only comes on at 11pm and switches off at 7am.

    Cheers,

    hads
  3. If that's your total hot water usage I need to move my family to your place, because we use way more than that.
  4. That's about the highest it gets really. In winter months with the log burner on we only use around 20kWh per month. It's an old (30y) low pressure cylinder.
  5. Hi Hads,

    Would you consider posting your server side? Been tossing up what I would do on the there side. Currently I just have been logging MQTT to a text file using a bash script and python + redis sounds much nicer ;)

    Wayne.
  6. Hi Wayne,

    No worries, send me through an email and I'd be happy to share, it's all in a state of flux for now.

    Cheers,

    hads
  7. My dumb meter (I have yet to see a smart meter - my ArcInnovations job is so dumb, when I complained, Meridian had to install an attachment with buttons and LCD so I could see when the night rate is on) has only one blink LED, but no visual indication of whether the day or night rate is in effect.

    I plan on using an AC output plugpack with a bandpass filter to obtain the ripple control bit stream used for switching rates, street lights, and a few other things. I designed the filter, and the bitstream shouldn't be that hard to decode. Documentation is on the Orion website. The downside of this system is that it only works on the Orion network in Christchurch that uses this encoding. Some Plains parts use a different encoding, which is however also documented. Dunno about other parts of the country.

    If anyone has other suggestions for obtaining the night/day information I'd like to hear them.

    -Volker
  8. Very interesting.

    We have a separate ripple control box here which turns on and off the night rate meter.
  9. I have just installed a big solar system to stop the high power bills. Only been in two months but in that time elements in cylinder have not turned on once. Hooray I am going to drive the power company out of business <g> use Current Cost to monitor power and to graph the results
  10. Hi Hads,

    I would also be interested in hearing about the server side. I was thinking of building my database logger around MySQL so would be interested to hear about the Redis setup.

    You might also be interested in this, which I was working on the other week: http://webworxshop.com/2012/11/03/tiny-mqtt-broker-with-openwrt

    Cheers,

    Rob
  11. Hads,

    I know that ripple controller you have in the middle from previous properties. Meridian replaced the meter twice (with a dumb meter), now what you have in those 3 boxes is all in one box. Expect that for anything newer. As the box looks just like the ones on your left/right I'll save myself posting a photo.
  12. @Ron Wilson - Great work, solar is loads of fun.

    @Rob Connolly - Nice little MQTT broker box you put together there, neat idea.

    It sounds like I need to do a post with the server side of things. I'll try and put something together. The script I put together using redis as a backend was more of a temporary solution. Keeping all that data from every 5 seconds in memory for years isn't a great use of resources. I'll probably move to MongoDB or SQL for the long term solution.
  13. Good work Hadley.
    Just a warning to stay on the right side of the electrical regulations though.
    Low voltage wiring, should 50mm away from 230v wiring.

    You can use mains rated sleeving, or use a piece of light mains flex, in order to obtain the protection needed.

    The reason is the 230v can't break through the insulation into the board or anyone holding onto them.

    Mark
  14. Pending moderation

Leave a comment