AC Freq Measurement

I have been reading through a lot of posts about measuring the frequency of the mains supply lines, and there seems to be no real consensus on how to do it with an Arduino. I was wondering, could I just step 120 V down to 6 or 12 V, filter it with a diode, drop it across a voltage divider and count the pulses on an analog input, then do a little math in code to get a relatively accurate value for my supply frequency?

What I want to accomplish ultimately is to have a readout of an engine shaft RPM and attached alternator frequency. It seems simple, but the massive amounts of other similar debates makes me think otherwise.

How accurately do you want to measure the frequency?
Thats what determines the complexity needed.

Well, I will be measuring around 60 Hz, if I'm at 59 or 61, I won't be too concerned. This will be used to govern the speed of an engine powering an alternator for small power generation. The closer I can keep the frequency to 60 Hz the better, but a Hz one way or the other won't matter too much.

The accuracy of the Arduino's clock, whether crystal or ceramic resonator, is so good compared to what you need that it is not even a consideration.

Great, thanks for the replies. I rigged up a quick test circuit to monitor the half rectified pulses output by a voltage divider and diode filter on the serial monitor. It works well to show the pulse train with the appropriate delay timing. Now I'm at a loss on how to translate that train into a meaningful frequency.

What function do I use to count the pulses so I can do a conversion operation on them to output hertz?

http://interface.khm.de/index.php/lab/experiments/frequency-measurement-library/

Nice!

My favorite approach below. Low parts count/cost, no transformer, no analog input, no math to speak of, just counting.

#include <util/Atomic.h>

//pin assignements
const byte OPTO = 2;
const byte LED = 13;

volatile int cycleCount;    //ISR count

void setup(void)
{
    Serial.begin(115200);
    pinMode(OPTO, INPUT_PULLUP);
    pinMode(LED, OUTPUT);
    attachInterrupt(0, mainsFreq, FALLING);
}

void mainsFreq(void)
{
    ++cycleCount;
}

void loop(void)
{
    static unsigned long msLast;
    static bool ledState;
    const unsigned long oneSecond = 1000;
    int count;    //our copy of the ISR count

    unsigned long ms = millis();    
    if (ms - msLast >= oneSecond) {
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
            count = cycleCount;
            cycleCount = 0;
        }
        msLast += oneSecond;
        digitalWrite(LED, ledState = !ledState);
        Serial.print(ms);
        Serial.print(' ');
        Serial.println(count);
    }
}

Output:

1000 60
2000 60
3000 60
4000 60
5000 60
6000 60
7000 60
8000 60
9000 60
10000 60
11000 60
12000 60
13000 60
14000 60
15000 61
16000 60
17000 60
18000 60
19000 60
20000 60

Cool, I'm going to have to study this for a bit because I don't get all that's going on there. Will using an interrupt cause any problems with other functions the controller will be running? For example, once I start sampling the frequency, I will output it to a display. Attaching and detaching interrupts won't affect those processes will it?

You could also use micros(). Just measure it 10 times in a row, then update the display. Throw out any results that are wildly off.

InPhase277:
Cool, I'm going to have to study this for a bit because I don't get all that's going on there. Will using an interrupt cause any problems with other functions the controller will be running? For example, once I start sampling the frequency, I will output it to a display. Attaching and detaching interrupts won't affect those processes will it?

Should be fine. No need to detach the interrupt, just let it run. It's about as fast as an interrupt can be, just incrementing a variable and at a very low rate besides. Shouldn't interfere with anything.

It could be fancied up some, a 1-second timer interrupt could pick up the count, and put it in another variable for the main code. So the whole thing would run under the covers in the ISRs.

Where can I find this atomic.h header? Safely, I mean.

InPhase277:
Where can I find this atomic.h header? Safely, I mean.

:smiley:

It's part of AVR-Libc, which is part of the Arduino IDE. That sketch will run as-is on your Arduino.

When sharing variables between an ISR and interruptible code like in main(), it is important that the variables are accessed "atomically" meaning the main code has to complete a read or write of the variable in a non-interrupted fashion. All the ATOMIC_BLOCK macro does is to ensure that the two lines of code in the block are executed without being interrupted.

One problem that can arise if this is not done is that data can get corrupted. The variable cycleCount is a two-byte int, therefore it may take two operations to read or write it. If an interrupt occurred when only one byte was read or written, and the code in the interrupt also updated the variable, its value would be corrupted.

The other thing that should always be done with variables shared between an ISR and other code is that they need to be declared volatile. This ensures the compiler places the variable in memory instead of in a register, which will sometimes happen for efficiency.

HTH ... jc

OK, I tried the code with a circuit fed from a 12 V transformer, through a diode bridge rectifier and stepped down to 3 V through a voltage divider. I get the expected 120 Hz ripple to display on the serial monitor, but the problem is it isn't very stable, even if I run it through some averaging code. It may jump to 125 or 138, or any random number above 120 Hz. It especially happens when something on that same circuit comes on or off (e.g. fridge). At first I thought it was the lagging phasing of the transformer as voltage varied on the supply side, but my multimeter shows a rock-solid 120 Hz no matter what.

Is there something inherently wrong with my circuit idea, or is the Arduino interrupt flaky, or what?

OK, I tried the code with a circuit fed from a 12 V transformer, through a diode bridge rectifier and stepped down to 3 V through a voltage divider. I get the expected 120 Hz ripple to display on the serial monitor, but the problem is it isn't very stable, even if I run it through some averaging code. It may jump to 125 or 138, or any random number above 120 Hz. It especially happens when something on that same circuit comes on or off (e.g. fridge). At first I thought it was the lagging phasing of the transformer as voltage varied on the supply side, but my multimeter shows a rock-solid 120 Hz no matter what.
Is there something inherently wrong with my circuit idea, or is the Arduino interrupt flaky, or what?

The 3V full wave signal would have a slow rise time and since it's full wave rectified sine, the Arduino would mostly be detecting a high level.

Ideally, it would be best to have a 1/2 wave signal (60Hz) connected to the Arduino. If you used a 1K resistor in series with the 12V and clamped it to 5V with a zener diode, then the signal would be more like a 60 Hz square wave with 50% duty cycle. This should be easier to detect and count by using a digital input.

The circuit in Reply #7 looks good - it would also produce a 60 Hz square wave. An optional 10K pull-up on pin D2 would provide a stronger signal and sharper edges.

Thanks everyone! That circuit and FreqPeriod library from reply #7 was the best way to go. It is spot on and any fluctuation is in the 1/100s of Hz. And it of course reads much higher frequencies too, so it will be handy on many audio projects as well. 8)

Perhaps you are picking up non-60Hz interference that is shifting the short Off time. Something a capacitor could bypass?