Arduino nano frequncy measurement of the mains improvement

Hello,
I am trying to improve the way of frequency measurement I am using in my case. In respect to this, I am asking for some recommendations or tips on how to really improve it.

Here is the thing:
My sensor is based on the emonlib voltage sensor as shown on the picture below:

By using the zerocrossing method and a digital low pass filter, i am trying to get some realistic reading of the mains voltage frequency that is 50Hz for 40 zerocrossings. Despite of getting some "good results" around 50Hz, for example like :

**50.00; **
50.00;
50.13;
50.25;
50.00
50.13
50.13
50.13
50.00
50.25
.
.

the measurements does not look stable. On the other hand for the test I am using the AC output of a high level Inverter. Of course if I increase the number of crossings, i will get some smooth frequency results, I do not have so much time budged.

Originally I was testing the code with and without the low pass filter and there is no much differences. That makes me thing that the problem was not because of not having any filter on the input of the adc.

here is the code I am using:

// This file shows an example implementation of a
// second order low-pass filter on an Arduino.

int startF;
boolean stf = false;
unsigned long first, second;
int xn = 0;
float yn1 = 0;
float yn = 0;
int xn1 = 0;
unsigned long delta = 0;
int inPinV = A2;
int crossings = 40;
double frequency = 0;

void setup () {
  Serial.begin(115200);
  for (int i = 0 ; i < 5 ; i++)
  {
    xn = analogRead(inPinV);
    filter();
  }
}

void loop () {

  stf = false;
  delta = 0;

  while (stf == false)                                  //wait for first crossing
  {
    xn = analogRead(inPinV);
    filter();

    if ((yn > 512) && (yn < 520))
    {
      stf = true;  //check its within range
      first = millis();
      delay(2); //Make sure no second read is in this range
    }
  }
  stf = false;

  for (int i = 0 ; i < crossings ; i++)
  {
    while (stf == false)                                  //wait for next crossing
    {
      xn = analogRead(inPinV);
      filter();

      //Serial.println(yn);
      if ((yn > 512) && (yn < 520))
      {
        stf = true;
        delay(2); //Make sure no second read is in this range
      }
    }
    stf = false;
  }
  second = millis();
  delta = (second - first) - 1; //Add time between crossings
  frequency = (crossings / 2) / ((double)delta / 1000);

  Serial.println(frequency);
}

void filter() {

  yn = yn1 * 0.65365856 + xn * 0.17317072 + xn1 * 0.17317072;
  xn1 = xn;
  yn1 = yn;
}

I have played a lot with the threshold levels 512-520 and it seems that with these settings I got the best results (the ones pasted above).

Any suggestions why for my configuration i cannot get stable readings for 40 zero or in my case threshold crossings?

Thanks!

Have you tested with another piece of equipment or known source to verify the equipment is or isn't measuring improperly?

Are you the guy that was measuring a power inverter or generator? Those measurements might be true... It might be off-frequency and variable. Or the wave may be noisy.

But in any case, it's probably a good idea to average over more than one cycle. I assume most laboratory frequency counters measure for more than one cycle.

Also, if you are not triggering an interrupt on a zero-crossing, you are only reading when your program loops-around and reads and that adds timing errors/variations.

The power line in a developed country will be more accurate than the clock in your microcontroller, but it can also be noisy.

When I made "zero-crossing" detector a million years ago I was reading just-after the zero crossing. Somewhere around 45 degrees is probably best. If you are look for the exact zero crossing, 1 volt of noise on the AC line can throw it off. And if you look for the peak, the actual peak-voltage can very (depending on the load, etc.) and there is no slope at the peaks so it makes the exact peak hard to find.

That looks like normal measurement error to me. The analogRead method you are using to detect "zero crossing" adds about 110 us jitter (default sample period on the Uno), or 0.5% error in one cycle of 50 Hz, to all the other sources of error.

Yes I did. I am measuring the same frequency with a professional tool, that probably integrates for a longer period but is quite stable.

The analogRead timing is not synchronized to the input waveform, so you will naturally expect +/- 1 ADC sample period jitter in the voltage measurement.

Use a zero crossing detector circuit to reduce this.

Hey,
Do you think that an interrupt that triggers when the event "zerocross" happens will improve what you stated?

What interrupt, connected to what circuit?

With some research and thought, you can very definitely improve the accuracy of the present measurements. The EEVBlog forum is a good resource.

attachInterrupt() I meant! Thanks for the hint!

Interrupts introduce variable latency.

The most accurate means of timing external events is to use Input Capture mode with a hardware timer. See https://gammon.com.au/timers

5000 crossings are needed to display 50Hz with two decimal places.

Time (micros) between two crossings is a faster method, but that needs accurate detection.
Averaging many of these can improve readout.

Note that many Arduinos don't have an accurate time reference (crystal).
Leo..

Your power line is not a solid 50 Hz, I would to get something like you are. In the US it is dead on every 24 hour period but changes as loads are added and removed, that is the nature of the generators as they load up they slow down dropping the frequency, the governor catches that and speeds it back up. The errors get corrected over a 24 hour period.

It sounds like you did a good job. You can always integrate the readings and have it more stable. Also realize your Nano is not crystal controlled so it will drift some as well and you have interrupts running in the background.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.