Logging Mains Grid Frequency

Here in South Africa the Eskom mains grid is under pressure, with periodic planned load shedding to alleviate the strain. I thought it would be a good idea to monitor and log the mains frequency using an ESP32 module and ESPHome, in order to gauge the state of the grid and possibly predict outages. However, it turns out that accurately measuring low frequencies to 2 or 3 decimals is not as easy as one would think.

I used a 9VAC wall-wart transformer for the supply and signal, with the 9VAC secondary winding driving an opto-coupler through a 1k current limiting resistor and reverse-blocking diode, and a bridge rectifier/capacitor/LM7805 to power the ESP32.

I tried the frequency_counter component, which was not consistent in output, even with an averaging filter. Then I tried the pulse width sensor but calibration and consistency was not acceptable. Finally I settled on @stevebaxter’s pulse_meter custom sensor, which is so far gives the most consistent reading.

Here is the code, warts and all:

sensor:
- platform: pulse_meter
  pin: GPIO34
  name: "Eskom Grid PPM"
  id:  eskom_ppm
  unit_of_measurement: 'ppm'
  
  
- platform:  template
  id: frequency
  name:  "Eskom Grid Frequency"
  unit_of_measurement:  "Hz"
  lambda:  |-
    return ( id(eskom_ppm).state  /  60);
  accuracy_decimals: 3
  update_interval: 5s 
  filters:
     - offset: -2.63158

The offset was needed because the frequency was consistently exactly over by that amount. It may vary between ESP32 devices.

Update: After boot, the log output shows the correct ppm and Hz readings, but after about half an hour, (I will still confirm exact interval) it spontaneously changes so that the offset value is no longer needed. Maybe an artifact of Steve’s code?
Log output:

[21:48:50][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:48:55][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:00][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:05][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:10][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:15][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:20][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:25][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:30][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy
[21:49:35][D][sensor:092]: 'Eskom Grid Frequency': Sending state 50.00000 Hz with 3 decimals of accuracy

and:

21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3000.00000 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3157.89478 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3000.00000 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3157.89478 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3000.00000 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3157.89478 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3000.00000 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3157.89478 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3000.00000 ppm with 2 decimals of accuracy
[21:51:00][D][sensor:092]: 'Eskom Grid PPM': Sending state 3157.89478 ppm with 2 decimals of accuracy

Please feel free to use the code if you wish, and to give comments, improvements or suggestions for alternative ways to implement a mains frequency counter.

1 Like

So I’ve taken out the offset and filtered out the artifacts in the ppm reading:

- platform: pulse_meter
  pin: GPIO34
  name: "Eskom Grid PPM"
  id:  eskom_ppm
  unit_of_measurement: 'ppm'
  filters:
  - filter_out: 3157.89478
- platform:  template
  id: frequency
  name:  "Eskom Grid Frequency"
  unit_of_measurement:  "Hz"
  lambda:  |-
    return ( id(eskom_ppm).state  /  60);
  accuracy_decimals: 3
  update_interval: 5s 

The Hz output seems to be stable now.

1 Like

Hi. Are you able to predict outages?

I hope to, but there have not been any so far since I started the project. :slightly_smiling_face: :wink:

Here’s mine:
https://snapshots.raintank.io/dashboard/snapshot/L4tDMaMStOyxTyM44OXGEFF3UO4tFAwh?orgId=2&var-avg_window=5m

I’ve got data from January, but never saw anything really interesting. When we start shedding due to coal supply issues it’s very preventative so we don’t usually see much correlation.

The sensing is pretty sensitive, a bad multiplug seems to be sensitive to both movement and other devices on it resulting in noise that one should really be filtering out. A wall wart transformer might have some filtering, I want to try that some time.

Mine has some occasional glitches, which is where the Shelly EM helps. It reads slower and has a slightly offset value compared to the Arduino, but the trend it the same and that’s what matters.

I had to chuck 4 months of data because Home Assistant just wasn’t coping. I’ve switched it to the LTSS plugin and reduced the HA DB to 3 days retention, but HA still seems fairly slow (proper VM, lots of RAM and CPU). I want to remove the frequency data out completely and see how HA behaves then, but that’s effort.

This is what the input and output to the 555 trigger looks like. A proper 74HC14 did not work as nicely, it triggers far too sensitively on the low side both up and down:

The lowest point I’ve recorded so far and as far as I remember there was nothing interesting going on then:

I found that the rise time on the opto-coupler output was fast enough not to need a schmitt trigger.
The trafo AC output is quite clean, especially loaded with the rect/cap/reg and esp board. Not sure if any suppression was included in the wall-wart itself.

I’m still experiencing occasional anomalies in the live Hz readings. Sometimes a series of 0Hz or 90Hz counts, and intervals where it reads normal.

I wonder whether the sensor component filter_out accepts < and > because then an allowable band can be specified.

I’ve not gotten into those nice fancy graphs yet.