https://toitlang.org/ logo
How to accurately capture fan speed (TAC Pin) on E...
# help
a
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...
f
Do you just need to count pulses or do you need the shape of each pulse (pwm). What's the frequency?
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
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.
a
Yes, it really is the pulse count πŸ™‚ Thanks, I hadn't seen that, I'll get back to you once I've tested it.
f
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.
a
Sleep automatically yields, I guess? Because everything is working fine with my tasks.
f
Correct. And everything that waits for hardware. Like
wait-for
a
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):
Copy code
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
f
Maybe try to play with the
glitch-filter-ns
of the
Unit
constructor.
Could be that the transition from one level to another isn't clean and oscillates a bit before it stabilizes.
You can use the glitch-filter to tell the pulse-counter to ignore pulses/spikes that are shorter than a certain duration.
Btw: you can use string-interpolation to make printing easier:
print "pulse-total: $pulse-total"
.
And once an operand of a math-operation is floating-point the others are automatically converted. So none of the `to-float`s in your code are necessary.
a
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?
f
In what respect? (memory space)
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.
a
It was mainly about the concatenated or interpolated string.
f
In that case they are equivalent.
a
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!