guild icon
Toit
#How to accurately capture fan speed (TAC Pin) on ESP32?
Thread channel in help
Arubinu
Arubinu 04/25/2025 07:29 PM
Hello, I would like to measure the speed of my fan (12V) using the TAC pin.
On ATmega microcontrollers, if I'm not mistaken, there's ISR support (interrupt service routine) that allows you to catch each pulse.

What would be the equivalent approach on Toit.io for ESP32 devices?

When I simply create a task with (gpio.Pin MY_PIN).do:, I don't get reliable results.
But I suspect the issue comes from how .do: behaves inside a task, since it appears to be blocking...
ArubinuOPArubinu
Hello, I would like to measure the speed of my fan (12V) using the TAC pin. On ATmega microcontrollers, if I'm not mistaken, there's ISR support (interrupt service routine) that al...
floitsch
floitsch 04/26/2025 08:10 AM
Do you just need to count pulses or do you need the shape of each pulse (pwm). What's the frequency?
floitsch
floitsch 04/26/2025 08:12 AM
If you just need to count, then use the pulse-counter. It counts in hardware for you, which makes it independent of the speed of your program and uses less CPU:
https://libs.toit.io/pulse-counter/library-summary
floitsch
floitsch 04/26/2025 08:13 AM
If you need the shape then maybe the RMT could be a choice. It's an amazing peripheral, but might not be applicable here. I would need to have more information.
Arubinu
Arubinu 04/26/2025 08:16 AM
Yes, it really is the pulse count :πŸ™‚: Thanks, I hadn't seen that, I'll get back to you once I've tested it.
floitsch
floitsch 04/26/2025 08:18 AM
If you really need a software based counter, then the following information might be helpful:
- do internally uses wait-for which isn't really high performance. It uses an event queue and signals to wake up your task. That's efficient when nothing happens but adds a bit of overhead when a change is finally detected.
- a tight loop around pin.get will be faster, but you need to make sure that other tasks can run (assuming you have them). So adding a yield inside the loop might be necessary.
- do is blocking. If you want to run out code concurrently you either have to start a different process (a new container or spawn), but then the communication with it gets difficult. Alternatively you usually use tasks (using task). These are cooperative threads. You need to yield to allow other tasks to run. Some of the library functions do that automatically.
Arubinu
Arubinu 04/26/2025 09:01 AM
Sleep automatically yields, I guess? Because everything is working fine with my tasks.
ArubinuOPArubinu
Sleep automatically yields, I guess? Because everything is working fine with my tasks.
floitsch
floitsch 04/26/2025 09:22 AM
Correct. And everything that waits for hardware. Like wait-for
Arubinu
Arubinu 04/28/2025 03:33 PM
I'm going back without having fully investigated my problem (I'm not an electronics engineer, so it's difficult for me).

There's some good stuff; I get high values ​​with this method.
The problem is that these values ​​are way too high (I easily get 30,000 over 3 seconds).

I know people who work in electronics; I'll ask their opinion. And I'll get back to you on that :πŸ™‚:

Code summary (just in case):
import gpio import pulse-counter main: pin/gpio.Pin := gpio.Pin in-pin --input --pull-up pulse-count/pulse-counter.Unit := pulse-counter.Unit pin task::get-task get-task: while true: pulse-count.clear start := Time.now.ms-since-epoch sleep --ms=3000 pulse-total := pulse-count.value now := Time.now.ms-since-epoch print "pulse-total: " + pulse-total.stringify // <-- 30.000 last-speed = ((pulse-total.to-float / 2.0) * 60000.0 / (now - start).to-float).round print "last-speed: " + last-speed.stringify // <-- 300.000
ArubinuOPArubinu
I'm going back without having fully investigated my problem (I'm not an electronics engineer, so it's difficult for me). There's some good stuff; I get high values ​​with this met...
floitsch
floitsch 04/28/2025 03:35 PM
Maybe try to play with the glitch-filter-ns of the Unit constructor.
floitsch
floitsch 04/28/2025 03:36 PM
Could be that the transition from one level to another isn't clean and oscillates a bit before it stabilizes.
floitsch
floitsch 04/28/2025 03:36 PM
You can use the glitch-filter to tell the pulse-counter to ignore pulses/spikes that are shorter than a certain duration.
floitsch
floitsch 04/28/2025 03:37 PM
Btw: you can use string-interpolation to make printing easier: print "pulse-total: $pulse-total".
floitsch
floitsch 04/28/2025 03:37 PM
And once an operand of a math-operation is floating-point the others are automatically converted. So none of the to-floats in your code are necessary.
Arubinu
Arubinu 04/28/2025 03:40 PM
I saw this principle on a video, I see the problem. I have to try this, thanks.
Regarding the floating point, I hadn't taken the time to do any tests. Not sure if it was the left or right element of the calculation... I'll make a note of it ^^
For debugging, which one takes up the least memory space?
floitsch
floitsch 04/28/2025 03:41 PM
In what respect? (memory space)
floitsch
floitsch 04/28/2025 03:43 PM
If you want to optimize the floating-point allocations, then try to combine operations that can be done statically.
For example, you start by dividing by 2 and then multiply by 60_000. This means that you will do 2 operations instead of one, if you just multiplied by 30_000.
I wouldn't worry too much about memory of floating-point numbers. If you really allocate a lot of them in a loop you will notice the garbage-collector, but in your case it won't matter.
Arubinu
Arubinu 04/28/2025 03:47 PM
It was mainly about the concatenated or interpolated string.
floitsch
floitsch 04/28/2025 03:49 PM
In that case they are equivalent.
πŸ‘Œ1
Arubinu
Arubinu 04/28/2025 04:40 PM
I just tested it again with a value of 5000 for the glitch-filter-ns.
At 50%, it gives 88, or 879 RPM according to the calculations, and that's pretty close to the specifications.

Thank you so much!
πŸ‘1
21 messages in total