How to count pulses from energy meter on MKR 1500 NB

Hi

I am a newbie In Arduino programming, and I need some guidance on how to count the pulses from a Carlo Gavazzi em340 energy meter. (Open collector NPN)

I have a working sketch ready, but remain to be able to count number of pulses every hour (I use the RTC)

Is there some example code out there. (Timer/Interrupt)

Would be great if I was able to simulate the energy meter, by outputting pulses at one pin, and connecting this to another pin for counting

Any help is highly appreciated

Regards Claus

Post what you got so far.

I am a newbie In Arduino programming... [would] be great if I was able to simulate the energy meter, by outputting pulses at one pin, and connecting this to another pin for counting

This is entirely possible, but would require that you do some programming that might be slightly more than noob territory.

Easier would be to have a second Arduino board making the simulated pulses, or some other external pulse generator. Dunno what you have going for you equipment-wise.

You mention "(Timer/Interrupt)" - there is no need to complicate things with timers or interrupts. If you caught the idea that it would be, start catching your ideas in a better pond! In particular interrupts and timers are not only unnecessary here, but def not noob territory.

So post what you got so far.

Please use the "Autoformat" tool in the IDE to put your code into one of the standard formats so it is easier to read.

Then use the "copy for forum" tool in the IDE and paste it here, it will end up

in a nice grey rectangle, formatted and  ready for
anyone to cut. paste and examine.

a7

Do you know what the pulse stream from your meter looks like?

Hi Alto777

Thanks for writing :slight_smile:

What I meant was the rest of my program is running, like connecting to a web server, and receive the current time to adjust the RTC clock. so no real value in posting my large program.

I do have 2 MKR 1500 at my disposal, so if I could create/find a simple pulsing sketch for one, and a simple pulse count sketch for the other it would be great.

I totally agree that a simple solution, but still moderately accurate solution would be preferable.

Could you help me to a couple of simple sketches

Thanks in advance :slight_smile:

Not sure, this is the data sheet: http://stockshed.uk/files/carlo-gavazzi/EM340DS.pdf
I don’t have a meter right now, so would like to be able to simulate it :slight_smile:

Hi @itgain

Here's some example code that input counts pulses. It was originally written for the Arduino Zero with the input on D12 (aka port pin PA19), the equivalent on the MKR 1500 NB is MISO pin instead. You might also have to change SerialUSB to Serial as well.

The SAMD21's 48MHz generic clock 0 (GCLK0) is routed to timers TC4 and TC5. The two 16-bit timers are chained to create a 32-bit counter accessed through TC4's registers. The incoming pulses are asynchronously routed from the PA19 pin through the External Interrupt Controller (EIC) then on to the Event System (a 12-channel peripheral-to-peripheral highway), to the timer itself. The timer is set-up to count the incoming pulses.

Here's the code:

// Setup TC4 in 32-bit mode to count incoming pulses on port pin PA19 using the Event System
void setup()
{
  // Serial Communication /////////////////////////////////////////////////////////////////
  
  SerialUSB.begin(115200);                        // Send data back on the Zero's native port
  while(!SerialUSB);                              // Wait for the SerialUSB port to be ready
  
 // Generic Clock /////////////////////////////////////////////////////////////////////////
 
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |        // Enable the generic clock...
                      GCLK_CLKCTRL_GEN_GCLK0 |    // On GCLK at 48MHz
                      GCLK_CLKCTRL_ID_TC4_TC5;    // Route GCLK0 to TC4 and TC5

  // Port Configuration ///////////////////////////////////////////////////////////////////

  PORT->Group[PORTA].PINCFG[19].bit.PMUXEN = 1;                 // Enable the port multiplexer on port pin PA19  
  PORT->Group[PORTA].PMUX[19 >> 1].reg |= PORT_PMUX_PMUXO_A;    // Set-up the pin as an EIC (interrupt) peripheral on D12

  // External Interrupt Controller (EIC) ///////////////////////////////////////////////////

  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO3;                                // Enable event output on external interrupt 3 (D12)
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE3_HIGH;                           // Set event detecting a HIGH level on interrupt 3
  EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT3;                               // Disable interrupts on interrupt 3
  EIC->CTRL.bit.ENABLE = 1;                                               // Enable the EIC peripheral
  while (EIC->STATUS.bit.SYNCBUSY);                                       // Wait for synchronization

  // Event System //////////////////////////////////////////////////////////////////////////

  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;                                  // Switch on the event system peripheral

  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                               // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU);               // Set the event user (receiver) as timer TC4
  
  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |               // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                  // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_3) |   // Set event generator (sender) as external interrupt 3
                       EVSYS_CHANNEL_CHANNEL(0);                          // Attach the generator (sender) to channel 0                                 
  
  // Timer Counter TC4 /////////////////////////////////////////////////////////////////////

  TC4->COUNT32.EVCTRL.reg |= TC_EVCTRL_TCEI |              // Enable asynchronous events on the TC timer
                             TC_EVCTRL_EVACT_COUNT;        // Increment the TC timer each time an event is received

  TC4->COUNT32.CTRLA.reg = TC_CTRLA_MODE_COUNT32;          // Configure TC4 together with TC5 to operate in 32-bit mode
                      
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;                       // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  TC4->COUNT32.READREQ.reg = TC_READREQ_RCONT |            // Enable a continuous read request
                             TC_READREQ_ADDR(0x10);        // Offset of the 32-bit COUNT register
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);                // Wait for synchronization
}

void loop()
{
  SerialUSB.println(TC4->COUNT32.COUNT.reg);               // Output the results
  delay(1000);                                             // Wait for 1 second
}

This is mostly a we help you with your code forum, not a we write code for you to order forum.

A simple pulsing sketch would be within your grasp after getting out and playing with a few of the basic examples in the IDE.

Just a matter of adjusting some constants and otherwise making the easy logical jump from "how to blink an LED at some rate" to creating a digital signal that goes at your desired frequency and duty cycle.

See

https://docs.arduino.cc/built-in-examples/basics/Blink

and try bending it to your will.

The document you linked states there is availabel

• Pulse output (optional, by open collector NPN)

When you get your physical copy(s) of the meter will be time enough to worry about the very simple matter of connecting that output to an input pin on you Arduino.

The meter also seems to be able to communicate more i formation over standard serial connections. If you want to have a slightly different kind of fun, you might read about what the meter can already do for you along the lines of wherever you are heading with this.

I remain unanimous in my feeling that timers and interrupts needn't enter into your solution.

a7

Hi alto777 -

I thought something like this might work.
Thought if I connect pin 2 to gnd it would count up

const byte inputPin = 4;  // <-- switch <-- gnd
volatile unsigned long counts;
unsigned long lastCount;

void setup() {
  pinMode(inputPin, INPUT_PULLUP);

  Serial.begin(9600);

  attachInterrupt(digitalPinToInterrupt(inputPin), count, FALLING);
  counts = 0;
}

void count() {
  ++counts;
}

void loop() {
  if (counts != lastCount) 
  {
    Serial.println(counts);
  }
  lastCount = counts;
}

What happens when you try it?

A click sound, but nothing in serial monitor
I have a 2 switch shield mounted, mabye it is the relay clicking?

Simplify your experiments by using the least hardware you can at first.

Access to the volatile unsigned long outside the context of the interrupt service routine must be done with care you aren't taking:

void loop() {

// Grab a copy of the variable
  cli();
  int myCopy = counts;
  sei()

  if (myCopy != lastCount) 
  {
    Serial.println(myCopy);
  }
  lastCount = myCopy;
}

Name it better. The code I added turns off interrupts making access to the variable safe from being incremented in the ISR whilst your loop code is getting it.

volatile is only half the solution.

You could also set counts to zero in the loop and keep your own track of a running total. Although an unsigned long should last quite a while, there is no reason to depend on it. Regularly sweeping your winnings off the table makes it very more unlikely.

Did you make your pulsing circuit? If you are counting simple switch closures you have a chance to see contact bounce in action...

HTH

a7

I changed to pin 4, MKR seems to not support pin 2 but 4 for Interrupt
Now I get results in Serial monitor, but not consistance.
I this due to the fact that i can't give a short enougth signal with my wire ?

No. It is either or both of the issues I pointed out. Fix you code and repeat your experiments.

I would predict an increase in the number of between 1 and something potentially higher and seemingly random as you count the contact bounces.

You could… handle the pushbutton in regular code using standard techniques (denouncing and edge detection), feed that to an output, and then hook that output to the input that the ISR is watching.

Hey, just like you started off by wanting…

Both debouncing and edge detection have a crap ton of solutions, I think the IDE may have examples if not google

arduino contact debouncing 

and

arduino state change detection

a7

Is this a little bit better?

#include <Arduino.h>

const byte inputPin = 4;  // <-- switch <-- gnd
volatile unsigned long counts;
unsigned long lastCount;

void setup() {
  pinMode(inputPin, INPUT_PULLUP);

  Serial.begin(9600);

  attachInterrupt(digitalPinToInterrupt(inputPin), interrupt_handler, FALLING);
  counts = 0;
}

void interrupt_handler() {
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 100ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 100) {
    ++counts;
    last_interrupt_time = interrupt_time;
  }
}

void loop() {

  // Turn off interrupts.
  noInterrupts();

  //Copy the volatile variable.
  int countsCopy = counts;

  // Turn interrupts back on.
  interrupts();

  if (countsCopy != lastCount) {
    Serial.println(countsCopy);
  }

  lastCount = countsCopy;
}

No. You aren't reading the code I posted and either understand it or slavishly copying it…

// Grab a copy of the variable

  cli();

  int myCopy = counts;

  sei();

I spread it out so you can see.


Turn off interrupts.

Copy the volatile variable.

Turn interrupts back on.

I see that I left off a semicolon in the code in #11, sry. I hope it is the kind of error you can spot (or be made aware of by the compiler) and fix. Part of meeting us more than halfway - we try to be careful, but things like that will leak through.

Now your ISR looks like you have done some research. Looks good, but is fatally flawed… read the code below carefully, see the difference and understand why yours was close but no cigar.

Hint: srsly, you gotta put your finger on the code and read and "execute" it step by tiny step, every line. If you do that with your attempt, you will see why it just doesn't work.

Instead:

void interrupt_handler()
{
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 200) 
  {
    ++counts;
    last_interrupt_time = interrupt_time;
  }
}

Only move your time stamp forward if you actually did anything based on the passage of that 200 ms.

BTW, 200 ms is an eternity. Most switches and even using a paper clip as a switch will settle down much faster.

You could do some experiments with your switches and see how large that lockout period has to be so you get one count per button press.

So now you are into interrupts, when you may have been able to totally avoid them.

Don't say I didn't warn you!

a7

HI

Thanks for all your help!
Didnt understand the cli(); and sei(); , sorry!
Just updated the code once again

I went for interrupts path – because I thought that if my program is busy contacting the webserver or any delay(), it would mis counting some pulses, maybe I am wrong ?

It may appear to be easier to rely on interrupts, and in this case I might even agree.

But the key to getting huge mileage out of these tiny machines we play with is to write programs that are never too "busy".

Never too busy to check the pushbuttons. Never to busty to go and see how that other thing is coming along. Never too busy to move the ball one or several pixels closer to the goal.

Sooner later you will want to learn how to do that. It's an Aha! moment and the path there is different for everyone.

If you want something for reading next to the fireplace, google

 Arduino two things at once 

and

Arduino finite state machine

and

Arduino finite state machine traffic lights

and spend a few minutes on a few sites and see if any match your learning style and current level of knowledge.

Like I said, you may not need it now, but if you go further you will, and if you want to be able to read and understand code you might want to steal borrow.

a7

Hi

Do you know if either A1, A2, A3, A4, SCL or SDA pins can be used as interrupt counter pins?

MKR 1500 NB

Arsking because my shield has already screw terminals for those pins :slight_smile:

Hi @itgain

Yes, all pins apart from SDA can be used as normal interrupt pins.

On the MKR 1500 NB, the SDA happens to be on port pin PA08, which is configured as the Non-Maskable Interrupt or NMI. This can still be used as an interrupt, but unfortunately not using the standard Arduino attachInterrupt() function.

Hi MartinL

Thanks :slight_smile:

Could you help me changing thiese code lines to use pin A1 instead af 4?

const byte inputPin = 4;
pinMode(inputPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(inputPin), interrupt_handler, FALLING);