Frequency measurement using ESP32

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
choies
Posts: 8
Joined: Sat Jan 30, 2016 11:06 am

Frequency measurement using ESP32

Post by choies » Wed Dec 26, 2018 2:30 pm

Hi, all.

I would like to measure the frequency of an external input signal using ESP32.

In the case of Arduino, pulseIn() is used to measure frequency. Therefore I tried to use similar function (machine.time_pulse_us())to measure frequency. However, the accuracy of this function is not good.

Is there a library or method to measure the frequency of an external input signal (square wave, duty is 50%, frequency range: 10Hz ~ 10kHz)? Please let me know it.

HermannSW
Posts: 197
Joined: Wed Nov 01, 2017 7:46 am
Contact:

Re: Frequency measurement using ESP32

Post by HermannSW » Wed Dec 26, 2018 9:16 pm

According documentation the first call is likely to be not accurate:
https://docs.micropython.org/en/latest/ ... chine.html
... If the pin is already equal to pulse_level then timing starts straight away. ...
I did a 50% duty 500Hz PWM sigbnal on pin 12 as in example:
https://docs.micropython.org/en/latest/ ... l/pwm.html

And I connected pin 12 and pin 13 with a cable.
As you stated the measurement is not accurate sometimes:

Code: Select all

>>> machine.time_pulse_us(p13,0)
1001
>>> machine.time_pulse_us(p13,0)
999
>>> machine.time_pulse_us(p13,0)
626
>>> machine.time_pulse_us(p13,0)
1001
>>> 
Only at the end of measuring 0 length you can be sure that signal is 1.
So just calling the same command twice gives always precise values for 2nd call:

Code: Select all

>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(608, 1000)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(999, 1001)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(596, 1001)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(724, 999)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(51, 1000)
>>> 

Works for 10KHz as well:

Code: Select all

>>> pwm12.freq(10000)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(50, 49)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(50, 50)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(37, 50)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(11, 50)
>>> 

Works even for maximal frequency 78125Hz for PWM (1,000,000/78125/2=6.4µs):

Code: Select all

>>> pwm12.freq(78126)
E (755361) ledc: requested frequency and duty resolution can not be achieved, try reducing freq_hz or duty_resolution. div_param=255
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Bad frequency 78126
>>> pwm12.freq(78125)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(3, 6)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(3, 6)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(4, 6)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(6, 6)
>>> machine.time_pulse_us(p13,0),machine.time_pulse_us(p13,0)
(6, 6)
>>> 
Last edited by HermannSW on Wed Dec 26, 2018 9:23 pm, edited 1 time in total.
Pico-W Access Point static file webserver:
https://github.com/Hermann-SW/pico-w

Tiny MicroPython robots (the PCB IS the robot platform)
viewtopic.php?f=5&t=11454

webrepl_client.py
https://github.com/Hermann-SW/webrepl#webrepl-shell

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Frequency measurement using ESP32

Post by Roberthh » Wed Dec 26, 2018 9:21 pm

Unlike @HermannSW, I used an external pulse generator, and then figures look worse, because the function may be called when the input is already at the requested level, and then a lower-than-expected value is returned. However, with the code below I got reasonable values. It calls time_pulse_us() twice in a row and takes the second one. Still, the precision is restricted by the 1 us resolution. And the irq had to be disabled to exclude excessive results.

Code: Select all

from machine import Pin, time_pulse_us, disable_irq, enable_irq
import time

p = Pin(18, Pin.IN)

while True:

    sum = 0
    loops = 100
    for _ in range(loops):
        state= disable_irq()
        time_pulse_us(p, 0, 1000000)
        sum += time_pulse_us(p, 0, 1000000)
        enable_irq(state)
    print(500000*loops/sum)
    time.sleep(0.97)

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Frequency measurement using ESP32

Post by Roberthh » Wed Dec 26, 2018 9:36 pm

Interestingly, taking both levels improves the result.

Code: Select all

from machine import Pin, time_pulse_us, disable_irq, enable_irq
import time

p = Pin(18, Pin.IN)

while True:

    sum = 0
    loops = 100
    for _ in range(loops//2):
        state= disable_irq()
        time_pulse_us(p, 1, 1000000)
        sum += time_pulse_us(p, 1, 1000000)
        enable_irq(state)
        state= disable_irq()
        time_pulse_us(p, 0, 1000000)
        sum += time_pulse_us(p, 0, 1000000)
        enable_irq(state)
    print(500000*loops/sum)
    time.sleep(0.97)
Edit: Added another enable/disable_irq in the middle, otherwise the watchdog timer would step in. That is surely to be considered with a no-pulses scenario.
Edit2: Works OK up to 30 kHz. Above that it degrades heavily. (~41khz instead 40khz, ~60kHz instead 50kHz)

OutoftheBOTS_
Posts: 847
Joined: Mon Nov 20, 2017 10:18 am

Re: Frequency measurement using ESP32

Post by OutoftheBOTS_ » Thu Dec 27, 2018 3:30 am

although not yet implemented in MP does the RTM https://docs.espressif.com/projects/esp ... s/rmt.html hardware peripheral is especially designed for both transmitting and receiving pulse signals with high accuracy.

I believe this is how the ESP32 neopixel library generates the accurate signals needed to drive the neopixels LEDs

I know both Mattyt and Laboris have talked about implementing this in MP but not sure hwere the development is at

User avatar
mattyt
Posts: 410
Joined: Mon Jan 23, 2017 6:39 am

Re: Frequency measurement using ESP32

Post by mattyt » Thu Dec 27, 2018 3:52 am

RMT transmit is coming along (I've started some very rough testing locally but am not happy with the API yet) but I haven't started on the receive side. I ought to have something good enough to share in the next couple of days for transmit.

It should be possible to do some quite accurate pulse measurements using RMT - it does use the 80MHz clock - but receive is not my primary focus right now...

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Frequency measurement using ESP32

Post by Roberthh » Thu Dec 27, 2018 11:26 am

RMT is somewhat limited in that the counters are 15 bit only. So you have to use different channels for lower and higher frequencies.
I made a variant of my sample code which does not disable irq, and uses a low pass filter for the results, getting good figures between 10Hz or lower and 25 kHz.

Code: Select all

from machine import Pin, time_pulse_us, disable_irq, enable_irq
import time

p = Pin(18, Pin.IN)

def median_of_n(p, n, timeout):
    set = []
    for _ in range(n):
        time_pulse_us(p, 1, timeout)
        v = time_pulse_us(p, 1, timeout)
        time_pulse_us(p, 0, timeout)
        v += time_pulse_us(p, 0, timeout)
        set.append(v)
    set.sort()
    return set[n//2]

tau = 0.25
res = 0

while True:
    res += tau * (median_of_n(p, 7, 55000) - res)
    print(1000000/res, "        \r")
    time.sleep(0.1)

choies
Posts: 8
Joined: Sat Jan 30, 2016 11:06 am

Re: Frequency measurement using ESP32

Post by choies » Thu Dec 27, 2018 3:10 pm

Thank you for your valuable response. It works well for my application.

roanokeroboticsclub
Posts: 1
Joined: Mon Nov 15, 2021 3:00 am

Re: Frequency measurement using ESP32

Post by roanokeroboticsclub » Mon Nov 15, 2021 3:18 am

I'm using a Raspberry Pi Pico to read the pulse width of an input from a RC Hobby King RC receiver, and then drive an H Bridge.
Its a easy thing in Arduino IDE.
I'm trying in micro python, having no luck.
The complete program is below.
the line from throttlePWM=machine.time_pulse_us(Throttle, 1, 21000,/) throws a Syntax Error, I've tried a bunch of different ways and dont understand why it doesnt work. I notice the other program examples do not attempt to assign the pulse width time to a variable.
How to configure so the pulse width is able to read and have some decisions made based on its read value ?


from machine import Pin, PWM, time_pulse_us
import utime

PWM1A=PWM(Pin(2)) # GP2 physical pin 4
PWM1B=PWM(Pin(3)) # GP3 physical pin 5
PWM1A.freq(1000)
PWM1B.freq(1000)

Throttle=Pin(14,Pin.IN)

while True:
# machine.time_pulse_us(pin, pulse_level, timeout_us=1000000, /) GPIO14 is physical pin19
throttlePWM=machine.time_pulse_us(Throttle, 1, 21000,/) #
print(ThrottlePWM)
utime.sleep(1)

Post Reply