Reading IrDA data from an Elster A100C electricity meter

Hi

I've recently installed solar panels and wanted to read the data from the generation meter, an Elster A100C. This project uses an Arduino sending data via USB serial to a server. I hope it is of use.

http://www.rotwang.co.uk/projects/meter.html

Dave

TrystanLea's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Thankyou for sharing this daveb, I have added a link from the community builds section

DaveLloyd's picture

Re: Reading IrDA data from an Elster A100C electricity meter

I ported the code to run on an emonTX:

Network ID is set to 11 as I am using another emonTX for live energy monitoring.

Dave

 

 

/*
 EmonTx Pulse example combined with Arduino code to read data from an Elster A100C electricity meter.
 
 An example sketch for the emontx module for
 CT only electricity monitoring.
 
 Part of the openenergymonitor.org project
 Licence: GNU GPL V3
 
 Authors: Glyn Hudson, Trystan Lea
 Builds upon JeeLabs RF12 library and Arduino
 
 Arduino code to read data from an Elster A100C electricity meter.
 
 Copyright (C) 2012 Dave Berkeley [email protected]
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 USA
 
 Modified for emonTX by Dave Lloyd
 Added temperature monitor 
 
 
*/
 
#define freq RF12_868MHZ                                                // Frequency of RF12B module can be RF12_433MHZ, RF12_868MHZ or RF12_915MHZ. You should use the one matching the module you have.433MHZ, RF12_868MHZ or RF12_915MHZ. You should use the one matching the module you have.
const int nodeID = 11;                                                  // emonTx RFM12B node ID
const int networkGroup = 210;                                           // emonTx RFM12B wireless network group - needs to be same as emonBase and emonGLCD needs to be same as emonBase and emonGLCD
 
const int UNO = 1;                                                      // Set to 0 if your not using the UNO bootloader (i.e using Duemilanove) - All Atmega's shipped from OpenEnergyMonitor come with Arduino Uno bootloader
#include <avr/wdt.h>                                                    // the UNO bootloader 
 
#include <OneWire.h>    // http://www.pjrc.com/teensy/td_libs_OneWire.html
#include <DallasTemperature.h>      // http://download.milesburton.com/Arduino/MaximTemperature/ (3.7.2 Beta needed for Arduino 1.0)
 
#include <JeeLib.h>                                                     // Download JeeLib: http://github.com/jcw/jeelib
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
  
typedef struct { unsigned long meter; int temperature;} PayloadTX;
PayloadTX emontx;                                                     // neat way of packaging data for RF comms
 
// set pin numbers:
const int ledPin = 9;    // remapped
const int intPin = 3;    // remapped
#define ONE_WIRE_BUS 4              // temperature sensor connection - hard wired 
#define BIT_PERIOD 416 // us
#define BIT_MARGIN 10 // us
 
//--------------------------------------------------------------------------------------------
// DS18B20 temperature setup - onboard sensor 
//--------------------------------------------------------------------------------------------
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
double temp;
 
 
 
  /*
   *  Buffer
   */
 
#define BUFF_SIZE 128
 
struct Buffer
{
  int data[BUFF_SIZE];
  int in;
  int out;
};
 
static void buff_init(struct Buffer* b)
{
  b->in = b->out = 0;
}
 
static int buff_full(struct Buffer* b)
{
  int next = b->in + 1;
  if (next >= BUFF_SIZE)
    next = 0;
  return next == b->out;
}
 
static int buff_add(struct Buffer* b, int d)
{
  int next = b->in + 1;
  if (next >= BUFF_SIZE)
    next = 0;
  if (next == b->out)
    return -1; // overfow error
 
  b->data[b->in] = d;
  b->in = next;  
  return 0;  
}
 
static int buff_get(struct Buffer* b, int* d)
{
  if (b->in == b->out)
    return -1;
  int next = b->out + 1;
  if (next >= BUFF_SIZE)
    next = 0;
  *d = b->data[b->out];
  b->out = next;
  return 0;  
}
 
  /*
   *  Called on every InfraRed pulse, ie. every '1' bit.
   */
 
typedef unsigned long stamp;
 
static stamp last_us;
 
#define BITS(t) (((t) + (BIT_PERIOD/2)) / BIT_PERIOD)
 
static struct Buffer bits;
 
void on_change(void)
{
  //  push the number of bit periods since the last interrupt to a buffer.
  const stamp us = micros();
  const int diff = us - last_us;
  last_us = us;
 
  const int bit_periods = BITS(diff);
  buff_add(& bits, bit_periods);
}
 
  /*
   *  Decode bit stream
   */
 
static struct Buffer bytes;
 
static int bit_data;
static int bit_index;
 
int add_bit(int state)
{
    bit_data >>= 1;
 
    if (state)
        bit_data += 0x200;
 
    if (bit_data)
        bit_index += 1;
 
    if (bit_index < 10)
        return 0;
 
    if (bit_data & 1) // start bit
    {
        if (!state) // stop bit
        {
            const int d = (bit_data >> 1) & 0xFF;
            // high represents '0', so invert the bits
            buff_add(& bytes, (d ^ 0xFF));
            bit_data = bit_index = 0;
            return 1;
        }
    }
 
    //  bad frame
    bit_data = bit_index = 0;
    return 0;
}
 
static void on_timeout()
{
  // too long since last bits count
  
  if (bit_index)
    while (bit_data) // flush with trailing '0' bits
      add_bit(0);  
  bit_data = bit_index = 0;
}
 
static void on_bits(int bits)
{
  // called on each bits count.
  // the bit periods represent a '1' followed by N '0's.
 
  if (bits < 1)
  {
    on_timeout();
    return;
  }
 
  // the elapsed bit periods since last interrupt
  // represents a '1' followed by 0 or more '0's.  
  add_bit(1);
  for (; bits > 1; bits--)
    add_bit(0);
}
 
  /*
   *
   */
   
static int decode_bit_stream(void)
{
  // look for a pause in the bit stream
  const stamp us = micros();
  const int diff = us - last_us;
  if (BITS(diff) > 20)
    on_timeout();
 
  // read the bit stream  
  int bit_count;
  if (buff_get(& bits, & bit_count) < 0)
    return -1;
 
  on_bits(bit_count);
 
  // read the byte stream decoded above
  int byte_data;
  if (buff_get(& bytes, & byte_data) < 0)
    return -1;
 
  return byte_data;
}
 
  /*
   *
   */
 
unsigned long bcdtol(const unsigned char* data, int bytes)
{
    unsigned long result = 0;
    for (int i = 0;  i < bytes; i++)
    {
        const unsigned char digit = *data++;
        result *= 100;
        result += 10 * (digit >> 4);
        result += digit & 0xF;
    }
    return result;
}
 
typedef void (*on_reading)(unsigned long reading);
 
class ElsterA100C
{
    // see Appendix B of the product manual
    struct info {
        char product[12]; // eg. "Elster A100C..."
        char firmware[9];
        unsigned char mfg_serial[3];
        unsigned char config_serial[2];
        char utility_serial[16];
        unsigned char meter_definition[3];
        unsigned char rate_1_import_kWh[5]; // the reading we are interested in!
        unsigned char rate_1_reserved[5];
        unsigned char rate_1_reverse_kWh[5];
        unsigned char rate_2_import_kWh[5];
        unsigned char rate_2_reserved_kWh[5];
        unsigned char rate_2_reverse_kWh[5];
        unsigned char reserved_01[1];
        unsigned char status;
        unsigned char error;
        unsigned char anti_creep[3];
        unsigned char rate_1_time[3];
        unsigned char rate_2_time[3];
        unsigned char power_up[3];
        unsigned char power_fail[2];
        unsigned char watchdog;
        unsigned char reverse_warning;
        unsigned char reserved_02[10];
    };
 
    unsigned char data[sizeof(info)];
    unsigned int idx;
    int reading;
    unsigned char last_4[4];
    on_reading handler;
public:
    ElsterA100C(on_reading cb)
    : idx(0), reading(0), handler(cb)
    {
    }
 
    int good_cs(unsigned char cs, unsigned char check)
    {
      // note : should be 'cs == check', but I'm seeing
      // systematic 1-bit errors which I can't track down.      
 
      /*
      Serial.print("bcc=");
      Serial.print(check, HEX);
      Serial.print(" cs=");
      Serial.print(cs, HEX);
      Serial.print(" xor=");
      Serial.print(cs ^ check, HEX);
      Serial.print("\r\n");
      */
 
      int bits = 0;
      int delta = cs ^ check;
      for (; delta; delta >>= 1)
        if (delta & 0x01)
          bits += 1;
 
      return bits < 2;
    }
 
    void good_packet()
    {
        struct info* info = (struct info*) data;        
        handler(bcdtol(info->rate_1_import_kWh, 5));
    }
 
    unsigned char bcc(unsigned char cs, const unsigned char* data, int count)
    {
        for (int i = 0; i < count; ++i)
            cs += *data++;
        return cs;
    }
 
    void on_data(unsigned char c)
    {
        // match the packet header
        const unsigned char match[] = { 0x01, 0x00, sizeof(info), 0x02 };
 
        if (!reading)
        {
            // keep a log of the last 4 chars
            last_4[0] = last_4[1];
            last_4[1] = last_4[2];
            last_4[2] = last_4[3];
            last_4[3] = c;
 
            if (memcmp(match, last_4, sizeof(match)) == 0)
            {
                idx = 0;
                reading = 1;
                return;
            }
 
            return;
        }
 
        if (idx < sizeof(info))
        {
            data[idx++] = c;
            return;
        }
 
        const unsigned char etx = 0x03;
        if (idx == sizeof(info))
        {
            if (c != etx)
            {
                printf("etx=%02X\n", c);
                reading = idx = 0;
                return;
            }
            idx++;
            return;
        }                    
 
        unsigned char cs = 0x00; // why 0x40?
        cs = bcc(cs, match, sizeof(match));
        cs = bcc(cs, data, sizeof(data));
        cs = bcc(cs, & etx, 1);
            
        if (good_cs(cs, c))
          good_packet();
 
        reading = 0;
    }
};
 
  /*
   *
   */
 
void meter_reading(unsigned long reading)
{
  Serial.print(reading);
  Serial.print(", ");
  Serial.print(temp);
  Serial.print("\r\n");
 
  if (emontx.meter != reading)
  {
    emontx.meter = reading;
    sensors.requestTemperatures();
    temp = (sensors.getTempCByIndex(0));
    emontx.temperature = (int) (temp * 100); 
 
    delay (50);
    send_rf_data();                                                       // *SEND RF DATA* - see emontx_lib
    delay (50);
  } 
  if (UNO) wdt_reset();
 
 
  static int state = 0;
  digitalWrite(ledPin, state = !state);
}
 
ElsterA100C meter(meter_reading);
 
 
void setup() 
{
  buff_init(& bits);
  buff_init(& bytes);
  
  Serial.begin(9600);
  Serial.println("Dave's emonTX meter reading");
             
  rf12_initialize(nodeID, freq, networkGroup);                          // initialize RF
  rf12_sleep(RF12_SLEEP);
 
  pinMode(ledPin, OUTPUT);
  pinMode(intPin, INPUT);
 
  sensors.begin();                         // start up the DS18B20 temp sensor onboard  
  sensors.requestTemperatures();
  temp = (sensors.getTempCByIndex(0));     // get inital temperture reading
 
 
  attachInterrupt(1, on_change, RISING);   // remap
 
 if (UNO) wdt_enable(WDTO_8S);  
}
 
void loop() 
  int byte_data = decode_bit_stream();
  if (byte_data == -1)
    return;
 
  meter.on_data(byte_data);
//  emontx_sleep(10);                                                     // sleep or delay in seconds - see emontx_lib
//  digitalWrite(LEDpin, HIGH); delay(2); digitalWrite(LEDpin, LOW);      // flash LED
}
 
jack_kelly's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Does anyone know if it's possible to read kvarh measurements (i.e. reactive power) over IrDA from an Elster A102C?

TrystanLea's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Dont know the answer im afraid, I think the only items that can be read are the ones described in the class:

        char product[12]; // eg. "Elster A100C..."
        char firmware[9];
        unsigned char mfg_serial[3];
        unsigned char config_serial[2];
        char utility_serial[16];
        unsigned char meter_definition[3];
        unsigned char rate_1_import_kWh[5]; // the reading we are interested in!
        unsigned char rate_1_reserved[5];
        unsigned char rate_1_reverse_kWh[5];
        unsigned char rate_2_import_kWh[5];
        unsigned char rate_2_reserved_kWh[5];
        unsigned char rate_2_reverse_kWh[5];
        unsigned char reserved_01[1];
        unsigned char status;
        unsigned char error;
        unsigned char anti_creep[3];
        unsigned char rate_1_time[3];
        unsigned char rate_2_time[3];
        unsigned char power_up[3];
        unsigned char power_fail[2];
        unsigned char watchdog;
        unsigned char reverse_warning;
        unsigned char reserved_02[10];

I will send Dave Berkeley who wrote the library an email to ask.

http://www.rotwang.co.uk/projects/projects.html

daveb's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Hi Kevin

Trystan is right, the data from the meter is described by the class above, so no analysis of reactive power I'm afraid.

 

jack_kelly's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Many thanks for the reply.  And thanks for emailing Dave, Trystan!

Just to confirm: are you guys talking about the Elster A100C rather than the A102C?  The Elster website suggests that the A102C does measure reactive power, although it doesn't specify whether that measurement is exposed on the IrDA port.  I've sent several emails to Elster but they haven't replied.

Have you guys tinkered with an A102C or A220?

jack_kelly's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Sorry, one more question: does anyone know where I can get hold of Elster's documentation for the output of their IrDA ports?

DaveLloyd's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Found this on Google www.meterspec.com/143.pdf

jack_kelly's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Brilliant, thank you!  (I clearly wasn't trying hard enough when I googled for the same info!)

I've also spoken to a very nice man at Elster.

The conclusion seems to be that yes, the A102C does spit out both kWh and kvarh readings over the IrDA port.  Hurray!  I'll almost certainly buy an A102C over the next few days and I'll report back.

jack_kelly's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Humph.  Turns out the A102C isn't available in the UK.  The AS230 might be a possibility though...

giorgio's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Hello,

I am new in arduino! I want to ask if this

http://www.ebay.com/itm/Duemilanove-USB-Board-2009-ATMega328P-PU-microco...

is the only thing I need in order to program the Arduino?

 

Also does the above code mentioned above, about reading from the Elster, really work?

 

Thnak you

 

giorgio's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Hello, does the code work?

Also inside the article it says;

"I came across this article by Richard Gregory who built his own meter reader back in 2009.". Because the page is down, has anyone this article ?

 

Thanx

Robert Wall's picture

Re: Reading IrDA data from an Elster A100C electricity meter

I have not seen any reports saying that the code does not work, so I assume that it does.

giorgio's picture

Re: Reading IrDA data from an Elster A100C electricity meter

I just plug the Elster A100C to the electricity and it starts displaying results? Don't I need to program the device or send commands to it?

 

Thank you

Robert Wall's picture

Re: Reading IrDA data from an Elster A100C electricity meter

All I know is what you too can read. I think it is very unlikely that you will be allowed to change anything inside the meter. I do not have an Elster meter, so I cannot confirm anything about the meter.

FroniusUser's picture

Re: Reading IrDA data from an Elster A100C electricity meter

I have a Fronius Symo 4.5-3-M inverter, now I need a way to monitor grid meter consumption, my solar tarrif meter which is to be installed will not have monitoring/rs485 support, I have been researching ideas for IrDa monitoring, I like the solutions that you have available, just asking about RS485/ModBus support to hook into a Fronius Inverter

 

Is there anyway to get the data from Raspberry Pi/Ardunio to export via RS485 so I can monitor it via the Fronius Solar Web?

https://www.solarweb.com/

An example is a Fronius Smart Meter with RS485 direct connect to Fronius Symo Smart Meter providing this type of data:

 

https://www.solarweb.com/Home/System/20bb600e-019b-4e03-9df3-a0a900cda689

 

Here the feed in tarrif, energy consumption are all monitored by the Fronius Smart meter via RS485 to the Fronius Symo DataManager 2.0

nothing clever's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Anyone with a 3D printer going down the A100C route may be interested in this.

http://www.thingiverse.com/thing:139775

It works OK, but if bright sunlight gets on the IrDa stuff readings all go haywire. I had to add a bit of card on the back of the bracket to stop the sunlight.

E-Mike's picture

Re: Reading IrDA data from an Elster A100C electricity meter

When using this code, in the serial monitor i get the correct reading from the meter. When it submits to emoncms it has three inputs, one of which is temperature and the other two are:-

629 (never changes)

-4248 (counts up toward zero when the meter increments a unit)

i have tried editing the code at https://github.com/openenergymonitor/ElsterMeterReader to send the data to my nanoderf and it does the exact same thing.

 

Am i missing something?

Thanks 

Mike

E-Mike's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Here is a screenshot, really odd... i've checked and checked the code and cannot see how this is being calculated...

 

if anyone can help that would be great.

 

Mike

daveb's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Hi Mike

I've not used my library with OEM so I can't really tell. It sounds like a signed / unsigned error, or perhaps a data alignment problem or both. I always used to print the relevant numbers in hex and see if they have common parts.

Hope this helps.

Dave

Robert Wall's picture

Re: Reading IrDA data from an Elster A100C electricity meter

How are you submitting to emoncms? and which one? What format are you using? There's nothing in daveb's code that tells us the whole story, so you need to reveal that if anyone is to help you.

E-Mike's picture

Re: Reading IrDA data from an Elster A100C electricity meter

I am using the multinode sketch from:

https://github.com/openenergymonitor/NanodeRF/tree/master/NanodeRF_multi...

I can't see anything there which would affect it, I have added a line to the data received from a node to print it to the serial console, and the data there is wrong. I would assume by this the data is being sent from the emonTx wrong.

NanodeRF serial console :

Data Rcvd: 16699
Data Rcvd: 630
Data Rcvd: 1787
Data sent: /api/post.json?apikey=<api key>&node=11&csv=16699,630,1787
OK recieved

​The emontx seems to be printing the correct reading to the emontx serial console but not sending the correct info via rf as per my image above.

Thanks

Mike

 

Robert Wall's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Yes we read your post the first time, there's no need to post twice.

So what about the emonTx sketch?

E-Mike's picture

Re: Reading IrDA data from an Elster A100C electricity meter

The emonTx sketch is the same as the one posted by DaveLloyd in this thread.

pb66's picture

Re: Reading IrDA data from an Elster A100C electricity meter

I think what you are seeing is a 4 byte Long (eg 41294188, the fist entry on your screenshot serial output) being passed as 2 signed integers.( eg 630*256*256 + 6508 = 41294188)

You need to change the nanode sketch to not assume all received packets consist of signed ints only at https://github.com/openenergymonitor/NanodeRF/blob/master/NanodeRF_multinode/NanodeRF_multinode.ino#L177 (that was the old standard) or you could just multiply input 2 by 65536 and add it to input 1 in emoncms to get the "correct" value.

emonHub handles Longs no problem, if you were using emonhub (for the benefit of other users) you could just define the node as

datacodes = L, h
scales = 1, 0.01

which would present the data exactly as seen in your screen print a long and an integer scaled by 2 decimal places 

Paul

Robert Wall's picture

Re: Reading IrDA data from an Elster A100C electricity meter

Not "I think" - but definitely. The data transmitted is an unsigned long (meter), plus an int (temperature).

E-Mike's picture

Re: Reading IrDA data from an Elster A100C electricity meter

thanks guys, been really helpful and knowledgeable.

 

I used your idea of multiplying the input in emoncms, which worked well. But i wasn't happy with that. But i don't have the know how to convert the Tx or Rx from a unsigned long to ints or vice a versa so i have dug out an RPi and put the latest emonHub image on it.

So far it's working well, the reason i went for NanodeRF over the raspberry pi, was becuase i wanted to set it up and forget it and was worried about the SD card writes. I'll give this a go now though seeing as the Nanode is pretty much discontinued now.

Many Thanks

Mike

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.