Go Down

Topic: Problem measuring mains frequency. (Read 4598 times) previous topic - next topic



I've built a mains frequency meter based on:


The circuit I've built simply takes the mains and outputs half-wave rectified AC (top half of the sine wave) at 5v peak. For what the Arduino does with this see the above link. The schematic is at:


And my code, which is the pretty much the Jeelabs code with the addition of a PWM output to drive a moving coil meter:


Now, what I don't understand is why periodically I get a totally erroneous reading. The meter needle bounces across the scale and then back to near the subsequent reading. Sometimes these events are some time apart and sometimes the thing seems to get caught in a brief frenzy of them.

I *think* there is always at least one correct reading after every bad one, so I could maybe say if delta
greater than N then use last reading, or something. But this would be a bit of a fudge.

I've pasted two sequences of measurements each containing an erroneous reading
as examples below.

Any thoughts much appreciated!



-- Example 1

50.072 Hz +0.14 %

50.070 Hz +0.14 %

50.067 Hz +0.13 %

50.063 Hz +0.13 %

50.061 Hz +0.12 %

50.057 Hz +0.11 %

50.053 Hz +0.11 %

50.049 Hz +0.10 %

50.640 Hz +1.28 %

50.039 Hz +0.08 %

50.034 Hz +0.07 %

50.029 Hz +0.06 %

50.024 Hz +0.05 %

50.021 Hz +0.04 %

50.018 Hz +0.04 %

-- Example 2

50.136 Hz +0.27 %

50.135 Hz +0.27 %

50.134 Hz +0.27 %

50.132 Hz +0.26 %

50.131 Hz +0.26 %

50.129 Hz +0.26 %

50.128 Hz +0.26 %

50.127 Hz +0.25 %

50.127 Hz +0.25 %

50.724 Hz +1.45 %

50.129 Hz +0.26 %

50.127 Hz +0.25 %

50.125 Hz +0.25 %


I believe you are seeing the value of micros() wrapping.

Code: [Select]
long now = micros();

The value of long climbs normally until it reaches 2^31 at which time it becomes a huge negative number, which gradually increments back to zero.
You should probably use unsigned longs for these values, and have some code to deal with when now < lastTime which will happen each time micros() wraps from 2^32 to 0.


Too add a little more explanation, micros() overflows every 70 minutes.  If you are using a signed variable to store it, that means you're going to see the "overflow" every 45 minutes.

This is an excellent tutorial on how to handle (and avoid) rollover issues:  http://www.arduino.cc/playground/Code/TimingRollover
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com


Both, many thanks for the advice! I'm a complete newbie to software development, so I'll take some time to look into your suggestion and to try to implement this change.

Whilst it sounds like the issue you outlined would certainly cause problems, I'm not seeing this issue after, say, 45 minutes of being powered up. It can start after a much shorter period of time has elapsed, and then the interval between the errors appears "random" and, again, much shorter.




I've seen this problem in my work also.  When I borrowed an oscilloscope and watched it the problem (naturally) didn't happen.  I finally just decided it was some transient that was running down the wire and caused my problem.  I then measured the frequency more often and kept a rolling average for a short period.  That solved my problem.  How many times you measure and how long you hold the average for is a function of what you need to do so some experimentation may be in order.  I currently go for a full second to get my numbers. 


Jan 29, 2011, 03:47 pm Last Edit: Jan 29, 2011, 04:42 pm by Andrew Back Reason: 1

I believe you are seeing the value of micros() wrapping.

Errors can occur almost immediately after the program has started running. I added another Serial.print to show the current value of 'long now = micros()' at the time of an error.


An example is below. So, I don't doubt that long rollover could cause a problem, but this is not my main concern right now.



69068824 microseconds 49.929 Hz -0.14 %

70070248 microseconds 49.928 Hz -0.14 %

71071688 microseconds 49.928 Hz -0.14 %

72073184 microseconds 49.925 Hz -0.15 %

73074736 microseconds 49.922 Hz -0.16 %

74064584 microseconds 50.512 Hz +1.02 %

75057988 microseconds 50.331 Hz +0.66 %

76059680 microseconds 49.915 Hz -0.17 %

77061400 microseconds 49.914 Hz -0.17 %

78063144 microseconds 49.912 Hz -0.18 %

79064904 microseconds 49.912 Hz -0.18 %

80066660 microseconds 49.912 Hz -0.18 %


The larger errors appear to be an additional 1.2% (relative to the background error) or off by nearly one count in a hundred.
And the code counts to 100:).

Perhaps there are occasional glitches ( higher frequency noise ) in the shape of power line, so there are three crossings of 1.1v on one side of the sine curve, and the comparator noticed two of them - so your count reached 100, half a cycle too soon.

Could you put a low-pass filter on the input?

This has a negligible chance of changing the result - you are doing a little more than reqd with interrupts disabled..
you could move the now = millis(); to before the cli();

Coding Badly

Jan 29, 2011, 08:40 pm Last Edit: Jan 29, 2011, 08:44 pm by Coding Badly Reason: 1
So, I don't doubt that long rollover could cause a problem, but this is not my main concern right now

It is about to become a problem.  Fix the data-type.

No matter have careful you are, your application is always going to have some "jitter".  Some things that will cause jitter: millis sometimes skips a value; Serial.print is blocking; your code has many expensive math operations.

Any given reading may be a bit high or a bit low.  If your code is correct, the long term average should be dead-on.  You need to let your application run for several minutes and calculate an average frequency.  If that is 50 Hz, you know your basic strategy is sound.  Then you can work on reducing the jitter.

On a side-note, I suggest a change at the top of loop...

Code: [Select]
void loop() {
       if (count >= 100) {
       count -= 100;
       [glow=lime,2,300]long now = micros();  // Move this after interrupts have been restored[/glow]


Can You please post the schematic diagram?
Its missing



The fact that all the frequencies are above 50Hz makes me wonder whether there is a flaw in your measurement technique. Either the measurement interval is slightly too long, or you're somehow getting double-accounting of samples at the start/end of a sample interval, or the input event is being triggered more than once per AC wave. How many AC cycles do each of those frequency calculations include? Have you confirmed that the long term average frequency of detected events is actually 50Hz?

PS the link to your sketch does not work for me.

Go Up