Hi all.
I need to read fan round/minuts using Nactua fan.
So I need to convert normal function to detect event on gpio GPIO.add_event_detect(TACH, GPIO.FALLING, fell)
where
TACH = 24 # Fan’s tachometer output pin
PULSE = 2 # Noctua fans puts out two pluses per revolution
AND fell is function to detect rounds
def fell(n):
global t
global rpm
dt = time.time() - t
if dt < 0.005: return # Reject spuriously short pulses
freq = 1 / dt
rpm = (freq / PULSE) * 60
t = time.time()
Last for detect rounds
try:
while True:
print “%.f RPM” % rpm
The “Preformatted Text” option </> will do code tags for you when posting code.
Just a FYI post, it retains indents etc and makes reading posted code a lot easier.
I have that breakout, haven’t tried what your attempting though.
I will have a good look at your code once you repost it.
Hi,
many thanks for your indication, so I repost the code.
The principal function that I need to convert in python for use ioexpander is
GPIO.add_event_detect(TACH, GPIO.FALLING, fell)
You can find the call in the code below, I send you all code of my file.
The code is necessary to read RPM of the fan Nactua. All code is possibile to convert using ioexpander but I don’t know how to convert the event detect.
Thanks
Herbert
import RPi.GPIO as GPIO
import time
# Pin configuration
TACH = 24 # Fan's tachometer output pin
PULSE = 2 # Noctua fans puts out two pluses per revolution
WAIT_TIME = 1 # [s] Time to wait between each refresh
# Setup GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(TACH, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull up to 3.3V
# Setup variables
t = time.time()
rpm = 0
# Caculate pulse frequency and RPM
def fell(n):
global t
global rpm
dt = time.time() - t
if dt < 0.005: return # Reject spuriously short pulses
freq = 1 / dt
rpm = (freq / PULSE) * 60
t = time.time()
# Add event to detect
GPIO.add_event_detect(TACH, GPIO.FALLING, fell)
try:
while True:
print "%.f RPM" % rpm
rpm = 0
time.sleep(1) # Detect every second
except KeyboardInterrupt: # trap a CTRL+C keyboard interrupt
GPIO.cleanup() # resets all GPIO ports used by this function
Hi to all,
thanks for your help.
I use Pimoroni Ioexpander breakout, for new users only two link is possible to post so nothing link for Pimoroni.
I need to expand pwm pin and gpio of my raspberry. I use a fan to cool down the CPU in particular Noctua 5v.
I use this guide to create service for check temperature and regulate the fan speed. In secondary I add the service that check the RPM fan. I convert and adapt the python code for use ioexpander pin with this fan Use PWM to Control Fan Speed. For this service is all ok.
The second service check and echo the RPM of fan code on github
In this service/code for convert to use ioexpander the problem is
# Add event to detect
GPIO.add_event_detect(TACH, GPIO.FALLING, fell)
This istruction normal using with GPIO, I not understand how to convert using IOexpander python library.
So now I hope everything is clearer.
Regards.
Herbert
Thanks,
for your clarification I post the first code that set speed of fan based on cpu temperature convertet with ioexpander:
#!/usr/bin/env python3
import RPi.GPIO as GPIO
import time
import signal
import sys
import os
import ioexpander as io
import MySQLdb
# Configuration
FAN_PIN = 1 # BCM pin used to drive PWM fan
WAIT_TIME = 10 # [s] Time to wait between each refresh
PWM_FREQ = 25 # [kHz] 25kHz for Noctua PWM control
# Configurable temperature and fan speed
MIN_TEMP = 50
MAX_TEMP = 60
ISTERESI_DOWN = 1
ISTERESI_UP = 1
FAN_LOW = 100
FAN_HIGH = 255
FAN_OFF = 0
FAN_MAX = 255
FAN_MID = FAN_OFF / 2
speed= 0
step=0
# Get CPU's temperature
def getCpuTemperature():
res = os.popen('vcgencmd measure_temp').readline()
temp =(res.replace("temp=","").replace("'C\n",""))
print("temp is {0}".format(temp)) # Uncomment for testing
return temp
# Set fan speed
def setFanSpeed(vel):
global speed
#fan.start(speed)
ioe.output(FAN_PIN, int(vel))
speed=vel
return()
# Handle fan speed
def handleFanSpeed():
temp = float(getCpuTemperature())
# Turn off the fan if temperature is below MIN_TEMP
if temp < MIN_TEMP - ISTERESI_DOWN:
setFanSpeed(FAN_OFF)
print("Fan OFF") # Uncomment for testing
# Set fan speed to MAXIMUM if the temperature is above MAX_TEMP
elif temp > MAX_TEMP:
setFanSpeed(FAN_MAX)
print("Fan MAX") # Uncomment for testing
# Caculate dynamic fan speed
else:
if temp > MIN_TEMP + ISTERESI_UP:
step = (FAN_HIGH - FAN_LOW)/(MAX_TEMP - MIN_TEMP)
temp -= MIN_TEMP
setFanSpeed(FAN_LOW + ( round(temp) * step ))
print("Fan Load : {} Fan is {}%".format(FAN_LOW + ( round(temp) * step ),round((speed / int(FAN_MAX) * 100),1) )) # Uncomment for testing
else:
#temp -= MIN_TEMP
print("Speed mantain : {} fan is {}%".format(speed,round((speed / int(FAN_MAX) * 100),1)))
return ()
try:
# Setup GPIO pin
BRIGHTNESS = 1 # Effectively the maximum fraction of the period that the LED will be on
PERIOD = int(255 / BRIGHTNESS) # Add a period large enough to get 0-255 steps at the desired brightness
ioe = io.IOE(i2c_addr=0x18)
ioe.set_pwm_period(PERIOD)
ioe.set_pwm_control(divider=1) # PWM as fast as we can to avoid LED flicker
ioe.set_mode(FAN_PIN, io.PWM,invert=False)
setFanSpeed(FAN_OFF)
# Handle fan speed every WAIT_TIME sec
while True:
handleFanSpeed()
time.sleep(WAIT_TIME)
except KeyboardInterrupt: # trap a CTRL+C keyboard interrupt
setFanSpeed(FAN_MID)
#GPIO.cleanup() # resets all GPIO ports used by this function
The second code, in link to another post, is dedicated for check RPM.
The istruction GPIO.add_event_detect need to convert using IOExpander.
Herbert
Hello,
I’m have the same issue.
The script to manage fan speed works correctly but I can’t retrieve the tach info with the script.
I try to found the equivalent of the function GPIO.add_event_detect() but without success still have 0 RPM.
When I look in the function code ioe.on_interrupt, it call _gpio.add_event_detect() so I think it’s the right function.
I look at the exemple weather.py which use interrupt and I write this script but without success it doesn’t enter the callback function.
#!/usr/bin/env python3
import time
import ioexpander as io
import RPi.GPIO as GPIO
# Configuration
TACH_PIN = 2 # Tach pin used to control speed fan
WAIT_TIME = 1.0 # [s] Time to wait between each refresh
PULSE = 2 # Noctua fans puts out two pluses per revolution
# Setup variables
t = time.time()
rpm = 0
ioe = io.IOE(i2c_addr=0x18, interrupt_pin=TACH_PIN)
ioe._gpio.setup(ioe._interrupt_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
ioe.set_pin_interrupt(TACH_PIN, True)
def fell():
global t
global rpm
print('FELL ON')
dt = time.time() - t
if dt < 0.005:
return # Reject spuriously short pulses
freq = 1 / dt
rpm = (freq / PULSE) * 60
t = time.time()
ioe.clear_interrupt()
ioe.on_interrupt(fell)
ioe.clear_interrupt()
try:
while True:
print("%.f RPM" % rpm)
time.sleep(WAIT_TIME)
except KeyboardInterrupt: # trap a CTRL+C keyboard interrupt
print('end script')
I’m a developper but not in python maybe a step by step debug will be usefull ?
I don’t know if it’s an issue with the code or with connections. I plug the INT pin of the IO Expander to the INT pin of the HyperPixel 4.0 pin. Maybe there is a things to do, special configuration to passtrough interruptions to the raspberry ?
And it’s work !
I have a little issue about this part of the callback :
if dt < 0.005:
return # Reject spuriously short pulses
With it the script stop working if the fan is too quick, without it works but I’m not sure of the fiability of the calculate value. Maybe it can be improve ?
Now, I will try to found the right init section to work with the HyperPixel 4.0
Still looking for the right configuration.
I found on the web that normally the INT pin of the IO Expander is link to GPIO27 of the raspberryPi but according to this page : Hyperpixel4 at Raspberry Pi GPIO Pinout, GPIO27 is for touch interrupt.
So, I don’t know how to figure it out. Anyone can help me ?
I already sent an email directly to Pimoroni support but no answer yet.
To retrieve the speed of a fan with the IO expander, you need to use a function wich can count number of interruptions it received.
All is in the weather example script → https://github.com/pimoroni/ioe-python/blob/master/examples/weather.py
In this script, there is a part on a anemometer which is the solution.
The new script :
import time
from threading import Lock
import RPi.GPIO as GPIO
import ioexpander as io
# Configuration
TACH_PIN = 2 # Tach pin on the IO Expander used to control tach
WAIT_TIME = 1.0 # [s] Time to wait between each refresh
PULSE = 2 # Noctua fans puts out two pluses per revolution
# Setup variables
rpm = 0
wind = 0
wind_overflows = 0
last_wind = 0
lock = Lock()
def handle_interrupt(stuff):
global wind, last_wind, wind_overflows
lock.acquire(blocking=True)
ioe.clear_interrupt()
new_wind, _ = ioe.read_switch_counter(TACH_PIN)
if new_wind < last_wind:
wind_overflows += 1
last_wind = new_wind
wind = (wind_overflows * 128) + new_wind
lock.release()
ioe = io.IOE(i2c_addr=0x18, interrupt_pin=4)
ioe._gpio.setwarnings(False)
ioe._gpio.setup(ioe._interrupt_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
ioe.set_pin_interrupt(TACH_PIN, True)
ioe.on_interrupt(handle_interrupt)
ioe.clear_interrupt()
last_wind_counts = 0
while True:
lock.acquire(blocking=True)
wind_hz = (wind - last_wind_counts) / PULSE
print(f"""
Fan Speed: {wind} ({int(wind_hz * 60)} RPM)
""")
last_wind_counts = wind
lock.release()
time.sleep(WAIT_TIME)
For my need, this solution doesn’t work due the fact I use an HyperPixel screen. As the screen used every pins of the RPi. I found another solution, count for 1 second the number of status change on the pin of the IO Expander which is connect to the FAN with a pull up resistor.