Skip to content

Instantly share code, notes, and snippets.

@mattiarossi
Last active March 30, 2017 10:50
Show Gist options
  • Select an option

  • Save mattiarossi/6800571 to your computer and use it in GitHub Desktop.

Select an option

Save mattiarossi/6800571 to your computer and use it in GitHub Desktop.
Uses an arduino and the pinchangeint library to implement an energy monitor that reads pulses from a power meter that has an open collector pulse output like the Finder 7E.23.8.230.0001: http://www.findernet.com/media/series/7E/EN/S7EEN.pdf Logs power readings to an emoncms instance (http://www.emoncms.org)
/*
Many meters have pulse outputs, including electricity meters: single phase, 3-phase,
import, export.. Gas meters, Water flow meters etc
The pulse output may be a flashing LED or a switching relay (usually solid state) or both.
In the case of an electricity meter a pulse output corresponds to a certain amount of
energy passing through the meter (Kwhr/Wh). For single-phase domestic electricity meters
(eg. Elster A100c) each pulse usually corresponds to 1 Wh (1000 pulses per kwh).
The code below detects the falling edge of each pulse and increment pulseCount
It calculates the power by the calculating the time elapsed between pulses.
Read more about pulse counting here:
http://openenergymonitor.org/emon/buildingblocks/introduction-to-pulse-counting
-----------------------------------------
Parts of the code courtesy of the open energy monitor project
http://openenergymonitor.org/
Licenced under GNU GPL V3
http://www.gnu.org/copyleft/gpl.html
THIS SKETCH REQUIRES:
An arduino with ethernet capability supported by the standard Ethernet library
Libraries in the standard arduino libraries folder:
- PinChangeInt https://code.google.com/p/arduino-pinchangeint/downloads/list
- HttpClient https://github.com/amcewen/HttpClient
*/
#include <SPI.h>
#include <HttpClient.h>
#include <Ethernet.h>
#include <EthernetClient.h>
#include <PinChangeInt.h>
// Setup input pins and led pins
#define PIN1 A0
#define PIN2 A1
#define PIN3 A2
#define PIN4 A3
#define PIN5 A4
#define PIN6 A5
#define LEDPIN1 2
#define LEDPIN2 3
#define LEDPIN3 5
#define LEDPIN4 6
#define LEDPIN5 7
#define LEDPIN6 8
#define debounceDelay 100 // the debounce time in ms; decrease if quick button presses are ignored, increase
// if you get noise (multipule button clicks detected by the code when you only pressed it once)
// Network Stuff
// Name of the server we want to connect to
const char kHostname[] = "172.30.2.207";
// 2) If your emoncms install is in a subdirectory add details here i.e "/emoncms3"
char basedir[] = "/v6/emoncms";
// 3) Set to your account write apikey
char apikey[] = "7ad5e973fceb73e8b14340d2de3c3e2a";
// 4) Node ID
const char node[]="801";
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Number of milliseconds to wait without receiving any data before we give up
const int kNetworkTimeout = 30*1000;
// Number of milliseconds to wait if no data is available before trying again
const int kNetworkDelay = 100;
// Power calc stuff
volatile int pulseCount[6] = {0}; // Number of pulses, used to measure energy.
volatile int tempPulseCount[6] = {0};
volatile unsigned long pulseTime[6] = {0}; // Time between pulses, used to calculate power
volatile unsigned long lastTime[6] = {0};; // Used to measure power.
volatile double power[6] = {0}; // Used to store calculated power
boolean mutex = false;
int ppwh = 1; // 1000 pulses/kwh = 1 pulse per wh - Number of pulses per wh - found or set on the meter.
// Interrupt stuff
uint8_t latest_interrupted_pin;
volatile long lastDebounceTime[20]={0}; // 20 possible arduino pins, used for debouncing
// led contais a mapping between which input pin must flash which led
// ins contains a mapping between which input pin must be used to calculate which power value
//A0 A1 A2 A3 A4 A5
int led[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0, 2, 3, 5, 6, 7, 8};
int ins[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0, 1, 2, 3, 4, 5};
// Functions
// Callback function for a pulse event
void onPulse() {
latest_interrupted_pin=PCintPort::arduinoPin;
int i = ins[latest_interrupted_pin];
if ((millis() - lastDebounceTime[latest_interrupted_pin]) > debounceDelay) //if current time minus the last trigger time is greater than
{ //the delay (debounce) time, button is completley closed.
lastDebounceTime[latest_interrupted_pin] = millis();
lastTime[i] = pulseTime[i] ;
pulseTime[i] = micros();
if (mutex){
tempPulseCount[i]++;
}else{
pulseCount[i]++;
}
power[i]=int((3600000000.0 / (pulseTime[i] - lastTime[i]))/ppwh);
}
};
// The PacketBuffer class is used to generate the json string that is send via ethernet - JeeLabs
class PacketBuffer : public Print {
public:
PacketBuffer () : fill (0) {}
const char* buffer() { return buf; }
byte length() { return fill; }
void reset()
{
memset(buf,NULL,sizeof(buf));
fill = 0;
}
virtual size_t write (uint8_t ch)
{ if (fill < sizeof buf) buf[fill++] = ch; }
byte fill;
char buf[512];
private:
};
PacketBuffer str;
// Blink a given led
void blink(int led){
digitalWrite(led, HIGH); delay(10);
digitalWrite(led, LOW);
}
// Initialization
void setup()
{
// initialize serial communications at 9600 bps:
Serial.begin(115200);
Serial.println("Reset...");
pinMode(LEDPIN1, OUTPUT); digitalWrite(LEDPIN1, HIGH);
pinMode(LEDPIN2, OUTPUT); digitalWrite(LEDPIN2, HIGH);
pinMode(LEDPIN3, OUTPUT); digitalWrite(LEDPIN3, HIGH);
pinMode(LEDPIN4, OUTPUT); digitalWrite(LEDPIN4, HIGH);
pinMode(LEDPIN5, OUTPUT); digitalWrite(LEDPIN5, HIGH);
pinMode(LEDPIN6, OUTPUT); digitalWrite(LEDPIN6, HIGH);
delay(500);
digitalWrite(LEDPIN1, LOW);
digitalWrite(LEDPIN2, LOW);
digitalWrite(LEDPIN3, LOW);
digitalWrite(LEDPIN4, LOW);
digitalWrite(LEDPIN5, LOW);
digitalWrite(LEDPIN6, LOW);
pinMode(PIN1, INPUT); digitalWrite(PIN1, HIGH);
PCintPort::attachInterrupt(PIN1, &onPulse, FALLING); // add more attachInterrupt code as required
pinMode(PIN2, INPUT); digitalWrite(PIN2, HIGH);
PCintPort::attachInterrupt(PIN2, &onPulse, FALLING);
pinMode(PIN3, INPUT); digitalWrite(PIN3, HIGH);
PCintPort::attachInterrupt(PIN3, &onPulse, FALLING);
pinMode(PIN4, INPUT); digitalWrite(PIN4, HIGH);
PCintPort::attachInterrupt(PIN4, &onPulse, FALLING);
pinMode(PIN5, INPUT); digitalWrite(PIN5, HIGH);
PCintPort::attachInterrupt(PIN5, &onPulse, FALLING);
pinMode(PIN6, INPUT); digitalWrite(PIN6, HIGH);
PCintPort::attachInterrupt(PIN6, &onPulse, FALLING);
while (Ethernet.begin(mac) != 1)
{
Serial.println("Error getting IP address via DHCP, trying again...");
delay(15000);
}
}
void loop()
{
int err =0;
double p0 = power[0];
double p1 = power[1];
double p2 = power[2];
double p3 = power[3];
double p4 = power[4];
double p5 = power[5];
int pulse0 = pulseCount[0];
pulseCount[0] = 0;
int pulse1 = pulseCount[1];
pulseCount[1] = 0;
int pulse2 = pulseCount[2];
pulseCount[2] = 0;
int pulse3 = pulseCount[3];
pulseCount[3] = 0;
int pulse4 = pulseCount[4];
pulseCount[4] = 0;
int pulse5 = pulseCount[5];
pulseCount[5] = 0;
if (pulse0 > 0 ){
blink(LEDPIN1);
}
if (pulse1 > 0 ){
blink(LEDPIN2);
}
if (pulse2 > 0 ){
blink(LEDPIN3);
}
if (pulse3 > 0 ){
blink(LEDPIN4);
}
if (pulse4 > 0 ){
blink(LEDPIN5);
}
if (pulse5 > 0 ){
blink(LEDPIN6);
}
EthernetClient c;
HttpClient http(c);
str.reset();
str.print(basedir);
str.print("/api/post.json?");
str.print("apikey=");
str.print(apikey);
str.print("&node=");
str.print(node);
str.print("&csv=");
str.print(p0);
str.print(",");
str.print(pulse0);
str.print(",");
str.print(p1);
str.print(",");
str.print(pulse1);
str.print(",");
str.print(p2);
str.print(",");
str.print(pulse2);
str.print(",");
str.print(p3);
str.print(",");
str.print(pulse3);
str.print(",");
str.print(p4);
str.print(",");
str.print(pulse4);
str.print(",");
str.print(p5);
str.print(",");
str.print(pulse5);
str.print("\0"); // End of string
Serial.println(str.buf);
err = http.get(kHostname, str.buf);
if (err == 0)
{
Serial.println("startedRequest ok");
err = http.responseStatusCode();
if (err >= 0)
{
Serial.print("Got status code: ");
Serial.println(err);
// Usually you'd check that the response code is 200 or a
// similar "success" code (200-299) before carrying on,
// but we'll print out whatever response we get
err = http.skipResponseHeaders();
if (err >= 0)
{
int bodyLen = http.contentLength();
Serial.print("Content length is: ");
Serial.println(bodyLen);
Serial.println();
Serial.println("Body returned follows:");
// Now we've got to the body, so we can print it out
unsigned long timeoutStart = millis();
char c;
// Whilst we haven't timed out & haven't reached the end of the body
while ( (http.connected() || http.available()) &&
((millis() - timeoutStart) < kNetworkTimeout) )
{
if (http.available())
{
c = http.read();
// Print out this character
Serial.print(c);
bodyLen--;
// We read something, reset the timeout counter
timeoutStart = millis();
}
else
{
// We haven't got any data, so let's pause to allow some to
// arrive
delay(kNetworkDelay);
}
}
}
else
{
Serial.print("Failed to skip response headers: ");
Serial.println(err);
}
}
else
{
Serial.print("Getting response failed: ");
Serial.println(err);
}
}
else
{
Serial.print("Connect failed: ");
Serial.println(err);
}
http.stop();
// And just stop, now that we've tried a download
delay(1000);
}
@nuedjidee
Copy link

This Code can use for XTM18SA or IEC62053-21 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment