Frequency measurement using ESP32
Frequency measurement using ESP32
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.
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.
Re: Frequency measurement using ESP32
According documentation the first call is likely to be not accurate:
https://docs.micropython.org/en/latest/ ... chine.html
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:
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:
Works for 10KHz as well:
Works even for maximal frequency 78125Hz for PWM (1,000,000/78125/2=6.4µs):
https://docs.micropython.org/en/latest/ ... chine.html
I did a 50% duty 500Hz PWM sigbnal on pin 12 as in example:... If the pin is already equal to pulse_level then timing starts straight away. ...
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
>>>
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
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
Re: Frequency measurement using ESP32
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)
Re: Frequency measurement using ESP32
Interestingly, taking both levels improves the result.
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)
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)
Edit2: Works OK up to 30 kHz. Above that it degrades heavily. (~41khz instead 40khz, ~60kHz instead 50kHz)
-
- Posts: 847
- Joined: Mon Nov 20, 2017 10:18 am
Re: Frequency measurement using ESP32
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
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
Re: Frequency measurement using ESP32
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...
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...
Re: Frequency measurement using ESP32
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.
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)
Re: Frequency measurement using ESP32
Thank you for your valuable response. It works well for my application.
-
- Posts: 1
- Joined: Mon Nov 15, 2021 3:00 am
Re: Frequency measurement using ESP32
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)
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)