Precise 220V AC Frequency Measurement with Arduino

Here in Germany, the mains alternating current (AC) frequency is meant to be 50 Hz, which tends to fluctuate based on supply and demand. The Bundesnetzagentur tries its best to keep the frequency constant using a feedback control system that uses the frequency to know if the demand drops (the frequency rises) or the demand increases (the frequency drops).

Calculation of the Frequency

The main idea behind the calculation is counting a fixed number of mains freuqency periods and measure how long that lasted using a fast and precise counter.

We only need a precise time source. Arduinos internal timer had quite good results in a test setup but for a reliable monitoring system the time source should not drift over time. One idea is to use the 1 Hz PPS source of a GPS receiver Another idea is using a realtime clock (RTC) such as the DS3231 with a +/- 2ppm 32.768kHz output.

For our setup we use the RTC and count the number \(n\) of 32.768 kHz cycles to complete \(m\) mains cycles. The frequency is thus

\[\begin{array}{rl} f =& \frac{1}{\frac{\frac{n}{32768 Hz}}{m}}\\ =& \frac{32768 m}{n} Hz \end{array}\]

Hardware parts

The VOH1016AB optocoupler is used as it has a Schmitt Trigger on its output. Arduinos already have Schmitt Triggers on their digital inputs, which allows using cheaper optocouplers like PC815 but when using faster microcontrollers like ESP32, you must somehow compensate for hysteresis, which the VOH1016AB already does.

0.1uPin 2Vcc10kVOH1016AB120k1N40412456220V

The emitter of the VOH1016AB optocoupler is connected to GND and the collector delivers our signal to interrupt pin 2, which we pull up with a 10k resistor.

On the mains side of the optocoupler, the 1N4007 diode takes out the negative half of the cycle because the VOH1016AB optocouplers maximum reverse voltage is only 6 V and a \(120 k\Omega\) resistor with 2 W limits the current that passes through the optocoupler. With a source voltage of 220V AC, the peak forward current is equal to (neglecting diode voltages):

\[\frac{220V \cdot \sqrt{2}}{120 k\Omega} = 2.59 mA\]

and the RMS current (half wave) is

\[\frac{2.59 mA}{2} = 1.3 mA\]

Attention, I assume no liability for damages! Working with electricity from the socket is life-threatening.

Source Code

volatile uint32_t cnt50Hz = 0;
volatile uint32_t cnt32kHz = 0;
uint32_t cnt = 0;

void counterFunc1() {

  if (cnt50Hz == 50) {
    cnt = cnt32kHz;
    cnt32kHz = 0;
    cnt50Hz = 0;

void counterFunc2() {

void setup(void) {

  attachInterrupt(0, counterFunc1, FALLING);
  attachInterrupt(1, counterFunc2, FALLING);

void loop() {
  Serial.println(32768. * 50. / cnt, 4);

You might also be interested in the following


Sorry, comments are closed for this article. Contact me if you want to leave a note.