Skip to content

Instantly share code, notes, and snippets.

@rxseger
Created October 9, 2016 04:53
Show Gist options
  • Save rxseger/2b27e661221b6f350b859d13f256cf29 to your computer and use it in GitHub Desktop.
Save rxseger/2b27e661221b6f350b859d13f256cf29 to your computer and use it in GitHub Desktop.
read RPM from a PC fan tachometer wired to GPIO
#!/usr/bin/python -u
# tachfan.py - read RPM from a PC fan tachometer wired to GPIO
#
# references:
# http://electronics.stackexchange.com/questions/8295/how-to-interpret-the-output-of-a-3-pin-computer-fan-speed-sensor
# http://www.formfactors.org/developer/specs/REV1_2_Public.pdf
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
TACH = 36 # BCM 16
GPIO.setwarnings(False)
GPIO.setup(TACH, GPIO.IN, pull_up_down=GPIO.PUD_UP)
t = time.time()
def fell(n):
global t
dt = time.time() - t
if dt < 0.01: return # reject spuriously short pulses
freq = 1 / dt
rpm = (freq / 2) * 60
print "%.f" % (rpm,)
t = time.time()
GPIO.add_event_detect(TACH, GPIO.FALLING, fell)
while True: time.sleep(1e9)
Copy link

ghost commented Aug 4, 2019

is there any way to just get the speed one time?

@Nodi-Rubrum
Copy link

Sure... just change the script to not loop endlessly.

@Nodi-Rubrum
Copy link

Nodi-Rubrum commented Feb 3, 2021

Script does not work as presented. Error in formatting output...

# python3 Tachometer.py File "Tachometer.py", line 19 print "%.f" % (rpm,)

@Nodi-Rubrum
Copy link

Nodi-Rubrum commented Feb 3, 2021

Here is an example of the script that just captures a few readings. You could average the results or just accept a single reading. Changing the time delay determines the number of total readings. It is ALSO a best practice to UNHOOK EVENTS!

`
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BOARD)

TACH = 16 # BOARD 16, GPIO 36

GPIO.setwarnings(False)
GPIO.setup(TACH, GPIO.IN, pull_up_down=GPIO.PUD_UP)

t = time.time()
s = True

def fell(n):
global t
global s

    dt = time.time() - t
    if dt < 0.01: return # reject spuriously short pulses

    freq = 1 / dt
    rpm = (freq / 2) * 60
    #print "%.f" % (rpm,)
    print (rpm)
    t = time.time()
    s = False

GPIO.add_event_detect(TACH, GPIO.FALLING, fell)
#while True: time.sleep(1e9)

while s:
time.sleep(0.25)

GPIO.remove_event_detect(TACH)
`

@BrantleyHobbs
Copy link

BrantleyHobbs commented Oct 6, 2022

Just an outsider observation (I'm browsing code for a project still on the drawing board), but shouldn't t = time.time() be farther up in the function just for accuracy's sake? I understand that on faster CPUs and with things running a few thousand RPM at most (like fans) the above should be fine, but were someone to engineer with a lower powered device and something moving faster than a couple thousand RPM, the errors in having the time recalculated farther down in the function might be significant.

My suggestion would be to put it right after calculating the delta, since the delta (dt) is essentially measuring the pulse frequency of whatever it is you're measuring.

Just my $0.02, and I thank you for providing your code!

@Jibun-no-Kage
Copy link

If the 'overhead' for each measure is consistent, then duration between measurements is consistent. So you will always be at the mercy of the total time to calculate the measure or the duration of overhead. If you forgo the calculation, and report just the raw frequency that is not as informational, but would be more accurate. Total time for the routine to run, will always be a given overhead, regardless of the complexity. That said, if you find that a given implementation of python does 'some' calculation faster, thus reducing the total time in fell() function that would be the most reasonable approach. For example python 3.9 is slow then python 3.10 for example, for the sake of argument. For even the pcode resulting is faster than some other, sure.

Changing the code, to avoid the print in the fell() routine is likely the best approach, IMHO, say write to global and have a completely different process read the global and report it, say only every 5 or 10 seconds. The frequency of printing, can be an issue, is really an issue, because print is slow. On a device like the Pi Pico that has two cores... have 1 core measure and 1 core report? Using say C or C#. That might be the most accurate. That is a bit tricky with python, because you can't force the issue or core assignment.

Here is another interesting issue related to the overhead of reporting/calculating, BIOS/ACPI on PC only in a very gross sense reports FAN RPM, I have seen the variance really great, for example, when I know a given fan at a given voltage/current should be X to have the BIOS report + or - X by a factor of 100s to 1000s, so is the actual RPM that critical? Or that the RPM proves the Fan is working (when compared to temperature delta achieved?). This seems odd, given any PC even 5 or more years old is going to have multiple cores, but the BIOS code monitoring the cores often only runs on CPU 0. Has you scratching your head on that one! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment