Counting pulse of Energy Meter

Can somebody help me the arduino code please

Start here.

Got the outline of a water meter project... pulse counting and doing useful things with it... maybe, depending on your requirements, it can be adapted to your project.

This is the code I used as the basis of a radiation counter. It’s for a Uno with a pulse input on pin 2, and should get you started. While it’s written for the Radiation Watch detector, you can work it by putting a switch between pin 2 and ground. Toggle it really fast to simulate a few hundred counts per minute.

// countsPerMinute102618.ino

const byte inputPin = 2;  // <-- (https://www.sparkfun.com/products/14209)

unsigned long counts = 0UL;
unsigned long seconds = 0L;

unsigned long lastDisplayMillis;
const long displayMillis = 1000L;

volatile boolean inputState = false;
boolean lastInputState = false;

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

  Serial.begin(115200);
  lastDisplayMillis = millis();

  counts = 0;
  seconds = 0;

  attachInterrupt(digitalPinToInterrupt(inputPin), getInputState, CHANGE);
}

void getInputState() {
  inputState = !digitalRead(inputPin);
}

void loop() {

  // add to count when the pulse is done
  if (inputState != lastInputState) {
    if (lastInputState) counts++;
    lastInputState = inputState;
  }

  // add to seconds every displayMillis (= 1 second)
  if (millis() - lastDisplayMillis >= displayMillis) {
    lastDisplayMillis += displayMillis;
    seconds++;

    // display counts per minute
    int countsPerMinute = ((60 * counts) / seconds);
    Serial.print(F("counts/min: "));
    Serial.print(countsPerMinute);
    Serial.print('\n');
  }
}

Why using an interrupt to read the digital pin, and instead of counting pulses in the ISR you have loop() poll for the pin state through that variable?

Hi wvmarle,

I actually did a lot of experimenting with a function generator while writing this code. My goal was to optimize the rate of pulse detection, while skipping as few pulses as possible.

Straight polling is considerably faster than isr code, because volatile variables have overhead. (And the interrupt itself also has some overhead.) If I were to count pulses in the ISR, I would need 3 volatile variables instead of 1, slowing the detection code down by (in my 8MHz device) more than a microsecond.

Then why not skip the interrupt entirely and just poll, you may ask. An accurate radiation counter needs to deal with random data, and events can happen anytime. Using an interrupt means that I can capture every count, with a time resolution of about 250ns. Even the tightest polling loop is many times longer than that, so interrupt is the way to go to capture the most events (events closer together than the max resolution are read as one event. My hardware beeps instead of clicks if counts overlap.)

My experiments said that doing all the work in the interrupt gives a resolution of 14kHz max.

  • Straight polling yields 62kHz, with every optimization I could muster.
  • Splitting between a short interrupt, and doing the analysis in loop() got to 43kHz. At that rate, I sussed that more than 99.9 percent of the events are counted.

In a nutshell then, that is why I split the counting algorithm between the ISR and loop(): To count as fast as possible, and catch nearly every event.

Bear in mind: this program is just a generic shell. In reality, every hardware situation calls for unique code.

I see the need for only one volatile variable: the actual count, or maybe a second one if you want to set a flag that a pulse has arrived. If you set the interrupt to RISING (or FALLING) you get only one interrupt per pulse (no double counting - which in itself is easy enough to deal with, just divide the count by two), and there's no need for state checking.

For really fast counting of pulses (up to f/2 so 4 MHz for an 8 MHz system) you can use a counter, and have no overhead to speak of.

But in either case you don't get to do an action in response to a pulse coming in, as what you seem originally to wanted to do (but the code you posted doesn't, hence my question).

Of course there's still one major avenue for optimisation left: write the thing in assembler code :slight_smile:

You are correct - it can be made much shorter by not checking the switch state, and just acting on a single sided interrupt (falling in this case):

volatile unsigned long counts;
unsigned long lastCount;

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

  Serial.begin(115200);

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

count() {
  ++counts;
}

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

or expanded to show the pulse length instead of the count, such as in this code:

const byte inputPin = 2;  // <-- switch <-- gnd

volatile boolean inputState;
boolean lastInputState;

unsigned long pulseLength;
unsigned long startMicros;

void setup() {
  pinMode(inputPin, INPUT_PULLUP);
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(inputPin), setInputState, CHANGE);
}

void setInputState() {
  inputState = digitalRead(inputPin);
}

void loop() {
  if (inputState != lastInputState) {
    if (isPulseDone()) Serial.println(pulseLength);
    lastInputState = inputState;
  }
}

boolean isPulseDone() {  // true means the pulse is done
  if (inputState) {  // rising edge of the pulse.
    startMicros = micros();  // start the microseconds clock
    return false;
  } else {  // falling edge - stop clock
    pulseLength = (micros() - startMicros);
    return true;
  }
}

I had no idea what the minimalistic OP was doing, so my example was quite general. I’ve made many incarnations of this code, the one I originally posted left it’s use and optimization to the OP.


wvmarle:

Of course there’s still one major avenue for optimisation left: write the thing in assembler code :slight_smile:

I’m not up to speed on avr assembly, but there is a machine specific modification that can significantly speed up an interrupt. Instead of using attachInterrupt() you can set the interrupt registers directly, like this:

volatile unsigned long counts;
unsigned long lastCount;

void setup() {
  pinMode(2, INPUT_PULLUP);
  counts = 0;

  Serial.begin(115200);

  EICRA &= ~(bit(ISC00) | bit (ISC01));  // clear both flags
  EICRA |= bit (ISC01);  // set falling level
  EIMSK |= bit (INT0);  // INT0 on
  sei();
}

ISR(INT0_vect) {
  ++counts;
}

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

This does exactly the same thing as the first sketch in this post, but the ISR takes a few microseconds less time.

Wonder why that function has so much overhead! I figured that attachInterrupt() is merely a convenience function to set the correct registers for you, after which it can get out of the way.

Similarly for digitalRead(), but that one at least still has to translate pin numbers to port numbers and so, so a bit of overhead is expected upon each call.

wvmarle:
Wonder why that function has so much overhead! I figured that attachInterrupt() is merely a convenience function to set the correct registers for you, after which it can get out of the way.

Similarly for digitalRead(), but that one at least still has to translate pin numbers to port numbers and so, so a bit of overhead is expected upon each call.

On Nick Gammon's Interrupts page he's got the assembly listing of the ISR() method and attachInterrupt() (scroll about a quarter of the way down the page, it's titled "How long does it take to execute an ISR?") It looks like a lot more registers are saved with attachInterrupt() than ISR(). Interesting that they are not all necessary, yet "Arduino language" puts them into the code anyway. I wonder if there are circumstances where that is useful?

Lately I've been using PIN and PORT instead of digitalRead() and digitalWrite() in time sensitive code, but only when it's for me. Registerese makes code intimidating to beginners. I'm not a fan of Atmel's unpronounceable, acronyms.