this seems to be accurate to ~0.5Hz but depends on the rest of the code in the programme. If there are interrupts, the millis() function will be inaccurate.
Please could someone suggest how I can measure 50Hz mains frequency to within 0.1% or less?
On an Arduino like the Uno, it is inaccurate in any case, due to the low quality resonator or crystal. Tell us what MCU board you actually have.
For accurate time or frequency measurements, you need an accurate reference clock.
A different approach would be to count the number of zero crossings in a decently long interval, using for example, the 1PPS signal of a GPS module as a time base. Nick Gammon's web pages feature a nice tutorial on input capture using Timer1 for such a purpose.
Why do you want to measure mains frequency.
Mains frequency is a zillion times more accurate than the clock of a Mega.
It only makes some sense (to me) if you run your own petrol/diesel generator.
Leo..
Right! If you live in a modern country the line frequency is probably more accurate than the crystal in your watch. And if it runs a tiny bit fast for a little while they slow it down for awhile so the number of cycles in a year is probably "perfect" and clocks are correct. Electric clocks with synchronous motors or digital clocks synchronized to the line frequency are some of the most accurate as long as you don't have a power failure.
GPS, cell network time, and internet time come from atomic clocks so they are also "perfect". And if you have a power failure or your cell phone battery dies, they reset to the correct time when power is restored.
There are groups of "timing enthusiasts" who closely monitor the frequency and voltage of their household AC, with the idea that this gives them some insight into the health of the system. And some historical records are available, at least in the U.K.
The frequency does vary by some fraction of 1% during the day, but it seems that the number of AC cycles is held to a fixed number over some long term.
The original idea behind Time Error Correction (TEC) is that over a 24-hour period the total number of cycles is held constant. This is done by speeding up or slowing down the generators in that timeframe, to compensate for slow downs due to load or speed ups from decreasing load. Try this link: The Dirty Little Secret about Mains Power Line Frequency
Still don't see why you need to know if the mains frequency is 49 , 50 or 51 Hz.
Almost all modern gear doesn't rely on exact mains frequency anymore.
Leo..
The aaplication is for an off grid electricity system. The solar inverters get a frequency signal to ramp down generation when excess power builds up. When the frequency rises above 51Hz the inverters ramp down. I monitor the island grid frequency and utilise excess power before the inverters ramp down. If my monitors can respond to a change from 50-51Hz this will pre-empt the ramping down of the solar inverters.
0.1% of 50Hz is 0.25Hz. Looking at your linked code, my first thought is to use a longer sampling time as they suggest. Maybe double the 50 crossings to go from 0.2% to 0.1%.
Also, millis's hardware counter keeps running during interrupts. While interrupts are suspended, millis()'s overflow updating is only paused until the interrupts are un-suspended, and then the millis() interrupt takes place. If you code has ISRs that inhibit interrupt for longer than 1024 cycles, you could miss a cycle during the pause, but those would be poor ISRs.
What is your circuit? Detecting zero crossing with while() {AnalogRead()...} seems fraught with danger. A resistor and optocoupler should be able to condition a half-wave digitalRead() and you could measure a cycle with 20000 micros() instead of 50 millis() in loop().
Even computer grade crystals are typically spec'ed to something like 0.1% (100 ppm). I'd expect they're "good enough" for this application over a narrow room temperature range, but something to consider as part of the error budget.
Watch grade crystals are typically around 0.001% (1 ppm).
Yes, however the 0.1% refers to the accuracy, not deviation. So with a typical quartz crystal can, the centre frequency may not be exactly as marked on the can, but whatever it is, will remain pretty close. That means, you can provide a correction factor in software and calibrate it.
A resonator is not like that, it is highly temperature sensitive regards resonant frequency. Quartz are also temperature sensitive, but not nearly to the same degree.