Measuring AC line frequency

Hi all,

I would like to ask for suggestions on how to measure the frequency from my main supply which is 240V using Arduino Mega 2560? As i know the frequency varies very little, hence i would like to measure it as accurate as possible.The reason for this is because I'm doing a smart meter project with Arduino Mega 2560.

The problem that I am facing is that I don't know what is the best way to measure the frequency from the AC mains. I was thinking of converting the sine wave into square, then capture the rising edge, from there calculate the frequency. Is this way accurate? The simulation i made is attached.

Other ways that I've researched is using zero crossings, Schmitt Trigger, and also TC9400. However, I am not so familiar with these methods. Can anyone help me to further explain how to measure the frequency using these methods?

Suggestion are much appreciated.
Thanks!

You might adapt this a bit - http://ruggedcircuits.com/html/circuit_-26.html -
Think R2 should be doubled to get the same current when the voltage doubles

if you connect it to an interrupt pin (see attachInterrupt) it should be fairly easy to count the pulse/second.
or the timing delta - micros() - between to pulses.

To accurately measure the AC line frequency, you need to have a very accurate time base and count cycles (e.g. rising edge of a zero-crossing detector) for a long time. Variations in the frequency are typically a few parts in 10,000, so counting pulses for something like 1000 seconds is required. See this web page for some examples 60 Hz AC Mains Frequency Accuracy Measurement

Rather than measuring the time between cycles, I suggest count the cycles per second ( 50 or 60), or even over 2 seconds.
The waveform over one or two cycles could be in error, if loads are applied, and especially if inductive loads are applied. Counting the cycles over one or two seconds should pretty much eliminate these errors.

This seems to work well, as Rob said, the code just counts interrupts.

Does it mean that by using this circuit, the AC will convert into a square wave in the Arduino itself? Then interrupt is used to count the pulses. Hence, as said by 'jack wp', to get a more accurate reading, i will count how many pulses occur in 1 or 2 second and then get the frequency?

Thanks.

Here are three application notes from Atmel that might help:

kriscpm:

[quote author=Jack Christensen link=topic=192063.msg1419646#msg1419646 date=1381171476]
This seems to work well, as Rob said, the code just counts interrupts.

Does it mean that by using this circuit, the AC will convert into a square wave in the Arduino itself? Then interrupt is used to count the pulses. Hence, as said by 'jack wp', to get a more accurate reading, i will count how many pulses occur in 1 or 2 second and then get the frequency?

Thanks.
[/quote]
Note you will get only the positive part of the SInus _/_/_/_ and those pulses will trigger the IRQ pin.
50/60 pulses per second on average.

Two points:

  1. Over the long term, the average frequency of the mains is very stable; but over the short term it can vary quite a bit. See http://www.nationalgrid.com/uk/Electricity/Data/Realtime/Frequency/Freq60.htm for an example. I remember many years ago when were debugging a problem with read errors on 8 inch floppy disk drives, most of the problems we saw were around 5pm and were caused by the mains frequency varying most at that time (the spindle motors of those drives were mains-driven induction motors).

  2. Whatever mechanism you use to count cycles, you must ensure that you count only genuine cycles and not transients. One possibility is to use a low pass filter followed by a Schmitt trigger. Alternatively, use the attached circuit (which incorporates a simple low pass filter) and in the software, don't count any pulses you receive that are too close to the last pulse.

PS - the capacitor in the schematic should be a metallized film one, not ceramic.

One further point: the Mega 2560 uses a ceramic resonator, not a crystal, so it is not a very accurate frequency reference.

Hey all,

For your need, the simple setup described here work as well: Power Grid Monitor 2 | Blinkenlight
Basically it does count the interrupts triggered to infer the mains frequency.

I did it myself, and it works pretty flawlessly, I just did a little twist to the code (not pretty, but works well) so it prints the frequency to the serial console.
I can post my code if needed.

However, I run into a little glitch when the arduino is not connected to the computer. (i.e powered by a wall adapter)
I added a Xbee radio so I can retrieve the frequency value wirelessly, however, when I don't share a GND with the computer (either via the USB connection, or via a simple USB-RS232 cable where I only use the GND), the frequency measurement is far off. I don't understand why, and the understanding of this problem might be a bit out of my league.

I will try some of the suggestions posted above to see if it's better. I reckon the optocoupler circuit might be a good idea as it ensures the arduino is a bit more protected. And it should work with the code of the above link.

Cheers,

Gromain

EDIT: Oh well, I might as well add my code, it's not secret after all!

//
//  www.blinkenlight.net
//
//  Copyright 2012 Udo Klein
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see http://www.gnu.org/licenses/
// 
//  input capture = pin8 --> now how to exploit the display as good as possible
//
// Pins
// 01 00 08 --> used of serial connection and input capture IO

const uint8_t input_capture_pin = 8;


// helper type for efficient access of the counter structure
typedef union {
    uint32_t clock_ticks;
    struct {
        uint8_t  byte_0;
        uint8_t  byte_1;
        uint16_t word_1;
    }; 
} converter;



// Timing variables for sending
unsigned long now;
unsigned long next_send;
const unsigned long delay_send = 5000;

const String STATE = "STATE";
const char eom = ';'; //end of message
const char eol = '\n'; //end of line
const char sep = '~'; //separator for measures
const char initOrd = '>'; //first order character
const int waitingDelay = 50;

const String sensorName = "FREQ";    //Simple name to recognise the arduino on the Xbee Network

// Serial receive variables
char inputC;
String inputS;


// The timer values will be initialized nowhere.
// This works because we are intested in the differences only.
// The first differences will be meaningless due to lack
// of initialization. However they would be meaningless anyway
// because the very first capture has (by definition) no predecessor.
// So the lack of initialization semantically reflects this.
volatile converter input_capture_time;
volatile uint16_t timer1_overflow_count;

// 0 indicates "invalid"
volatile uint32_t period_length = 0;
volatile bool next_sample_ready = false;

ISR(TIMER1_OVF_vect) {
    ++timer1_overflow_count;
}

ISR(TIMER1_CAPT_vect) {
    static uint32_t previous_capture_time = 0;
    
    // according to the datasheet the low byte must be read first
    input_capture_time.byte_0 = ICR1L;
    input_capture_time.byte_1 = ICR1H;  
                             
    if ((TIFR1 & (1<<TOV1) && input_capture_time.byte_1 < 128)) {
        // we have a timer1 overflow AND
        // input capture time is so low that we must assume that it
        // was wrapped around
       
        ++timer1_overflow_count;    
         
        // we clear the overflow bit in order to not trigger the
        // overflow ISR, otherwise this overflow would be
        // counted twice
        TIFR1 = (1<<TOV1);
    }  
    input_capture_time.word_1 = timer1_overflow_count;
     
    period_length = input_capture_time.clock_ticks - previous_capture_time;
    previous_capture_time = input_capture_time.clock_ticks;
     
    next_sample_ready = true;
}

void initialize_timer1() {
    // Timer1: "normal mode", no automatic toggle of output pins
    //                        wave form generation mode  with Top = 0xffff
    TCCR1A = 0;

    // Timer 1: input capture noise canceler active
    //          input capture trigger on rising edge
    //          clock select: no prescaler, use system clock
    TCCR1B = (1<<ICNC1) | (1<< ICES1) | (1<<CS10);
    
    // Timer1: enable input capture and overflow interrupts
    TIMSK1 = (1<<ICIE1) | (1<<TOIE1);
    
    // Timer1: clear input capture and overflow flags by writing a 1 into them   
    TIFR1 = (1<<ICF1) | (1<<TOV1) ;
}


void visualize_frequency_deviation(const uint8_t target_frequency, const uint32_t period_length) {
    
    const int64_t deviation_1000 = 1000*(int64_t)F_CPU / period_length - 1000*(int64_t)target_frequency;
     
    Serial.print(F("period length  "));
    Serial.println(period_length);
     
    static int8_t sign = 1;
    if (deviation_1000 != 0) {
        // only compute new sign for frequency deviation != 0
        sign = deviation_1000 >= 0? 1: -1;
    }
    
    Serial.print(F("deviation: "));
    Serial.println((int32_t)deviation_1000);
    
    const uint64_t value = abs(deviation_1000);    
    
    Serial.print(F("value: "));
    Serial.println((uint32_t)value);
}


void setup() {

    Serial.begin(57600);

    // just to indicate which pins will be the output pins
    // this "output test" makes it easier to wire the circuit
    delay(1000);

    pinMode(input_capture_pin, INPUT);
    digitalWrite(input_capture_pin, HIGH);
    now = millis();
    next_send = now + delay_send;
    initialize_timer1();
}


const uint8_t target_frequency = 50;
const uint8_t sample_buffer_size = 50;
int64_t value_1000 = 0;

uint32_t sample_buffer[sample_buffer_size];


void loop() {
    static uint8_t sample_index = 0;
    now = millis();
    if (next_sample_ready) {
        next_sample_ready = false;
        
        cli();	// Disable global interrupts
        sample_buffer[sample_index] = period_length;   
        sei();	// Enable global interrupts
        sample_index = sample_index > 0? sample_index - 1: sample_buffer_size - 1;
        
        uint32_t average_period_length = 0;
        for (uint8_t index = 0; index < sample_buffer_size; ++index) {
            average_period_length += sample_buffer[index];
        }
        average_period_length /= sample_buffer_size;       
        
        value_1000 = 1000*(int64_t)F_CPU / average_period_length;
    }
    // boucle de comptage et tous les X tours (qui correspondent à 5s) envoie de la valeur en série
    if (now >= next_send) {
        Serial.print(sensorName);
        Serial.print(sep);
        Serial.print((int32_t)value_1000, DEC);
        Serial.println(eom);
        next_send = now + delay_send;
    }
}

kriscpm:

[quote author=Jack Christensen link=topic=192063.msg1419646#msg1419646 date=1381171476]
This seems to work well, as Rob said, the code just counts interrupts.

Does it mean that by using this circuit, the AC will convert into a square wave in the Arduino itself? Then interrupt is used to count the pulses. Hence, as said by 'jack wp', to get a more accurate reading, i will count how many pulses occur in 1 or 2 second and then get the frequency?
[/quote]

It's really the opto-isolator that does the conversion. Its LED blinks at the line frequency. As @jremington said in reply #2, more than 1 or 2 seconds may be required. The link he gave is pretty interesting so be sure to check that out for some techniques.

You may find this project useful:
http://coolarduino.wordpress.com/2013/01/04/power-quality-meter/

Hi, I found this discussion really helpful for the same purpose, but in my case when I am going to use a Precision RTC chip DS3231, I have a different idea about how to make AC frequency measurement more accurate. I am going to use two interrupt pins one to increment a counter on each zero crossing of AC line voltage and other to count 1Hz Square wave generated pulses. after 5 sec. the counted pulses from zero crossing detector would be divided by 5 and give average AC line frequency since 1Hz signal would be precise the calculated AC frequency would also be relatively accurate when RTC is not used.

Here is online frequency meter showing real-time activity of UK power grid.

http://www.dynamicdemand.co.uk/grid.htm

kriscpm:
Hi all,

I would like to ask for suggestions on how to measure the frequency from my main supply which is 240V using Arduino Mega 2560? As i know the frequency varies very little, hence i would like to measure it as accurate as possible.The reason for this is because I'm doing a smart meter project with Arduino Mega 2560.

The problem that I am facing is that I don't know what is the best way to measure the frequency from the AC mains. I was thinking of converting the sine wave into square, then capture the rising edge, from there calculate the frequency. Is this way accurate? The simulation i made is attached.

Other ways that I've researched is using zero crossings, Schmitt Trigger, and also TC9400. However, I am not so familiar with these methods. Can anyone help me to further explain how to measure the frequency using these methods?

Suggestion are much appreciated.
Thanks!

In North and South America, the commercial power frequency is tied to a 10 mHz clock that is synced to the GPS time signal. That 10mHz signal is divided down to 60 Hz to sync the power frequency.

If your country does something similar, you need to use the same scheme to determine if the frequency at your location is correct or not.

Paul

kriscpm:
I would like to ask for suggestions on how to measure the frequency from my main supply which is 240V using Arduino Mega 2560? As i know the frequency varies very little, hence i would like to measure it as accurate as possible.The reason for this is because I'm doing a smart meter project with Arduino Mega 2560.

Think about using an optical tacho arrangement with a phototransistor as input, then simply point at any fluro light.