-
-
Notifications
You must be signed in to change notification settings - Fork 484
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: DC FAN RPM Input Sensor #302
Comments
Try out the following code to measure your fan signal. I would definitely use a voltage divider to get the signal voltage at or below 3.3 volts before testing. Change PWM_GPIO to your GPIO (using BCM numbering), then run with:
read_PWM.py: # coding=utf-8
# read_PWM.py
# 2015-12-08
# Public Domain
import time
import pigpio # http://abyz.co.uk/rpi/pigpio/python.html
class reader:
"""
A class to read PWM pulses and calculate their frequency
and duty cycle. The frequency is how often the pulse
happens per second. The duty cycle is the percentage of
pulse high time per cycle.
"""
def __init__(self, pi, gpio, weighting=0.0):
"""
Instantiate with the Pi and gpio of the PWM signal
to monitor.
Optionally a weighting may be specified. This is a number
between 0 and 1 and indicates how much the old reading
affects the new reading. It defaults to 0 which means
the old reading has no effect. This may be used to
smooth the data.
"""
self.pi = pi
self.gpio = gpio
if weighting < 0.0:
weighting = 0.0
elif weighting > 0.99:
weighting = 0.99
self._new = 1.0 - weighting # Weighting for new reading.
self._old = weighting # Weighting for old reading.
self._high_tick = None
self._period = None
self._high = None
pi.set_mode(gpio, pigpio.INPUT)
self._cb = pi.callback(gpio, pigpio.EITHER_EDGE, self._cbf)
def _cbf(self, gpio, level, tick):
if level == 1:
if self._high_tick is not None:
t = pigpio.tickDiff(self._high_tick, tick)
if self._period is not None:
self._period = (self._old * self._period) + (self._new * t)
else:
self._period = t
self._high_tick = tick
elif level == 0:
if self._high_tick is not None:
t = pigpio.tickDiff(self._high_tick, tick)
if self._high is not None:
self._high = (self._old * self._high) + (self._new * t)
else:
self._high = t
def frequency(self):
"""
Returns the PWM frequency.
"""
if self._period is not None:
return 1000000.0 / self._period
else:
return 0.0
def pulse_width(self):
"""
Returns the PWM pulse width in microseconds.
"""
if self._high is not None:
return self._high
else:
return 0.0
def duty_cycle(self):
"""
Returns the PWM duty cycle percentage.
"""
if self._high is not None:
return 100.0 * self._high / self._period
else:
return 0.0
def cancel(self):
"""
Cancels the reader and releases resources.
"""
self._cb.cancel()
if __name__ == "__main__":
import time
import pigpio
import read_PWM
PWM_GPIO = 4
RUN_TIME = 60.0
SAMPLE_TIME = 2.0
pi = pigpio.pi()
p = read_PWM.reader(pi, PWM_GPIO)
start = time.time()
while (time.time() - start) < RUN_TIME:
time.sleep(SAMPLE_TIME)
f = p.frequency()
pw = p.pulse_width()
dc = p.duty_cycle()
print("f={:.1f} pw={} dc={:.2f}".format(f, int(pw+0.5), dc))
p.cancel()
pi.stop() |
Actually, this is probably the script you should test. Change RPM_GPIO to your GPIO (using BCM numbering), then run with: ~/Mycodo/env/bin/python ~/read_RPM.py read_RPM.py: # coding=utf-8
# read_RPM.py
# 2016-01-20
# Public Domain
import time
import pigpio # http://abyz.co.uk/rpi/pigpio/python.html
class reader:
"""
A class to read speedometer pulses and calculate the RPM.
"""
def __init__(self, pi, gpio, pulses_per_rev=1.0, weighting=0.0, min_RPM=5.0):
"""
Instantiate with the Pi and gpio of the RPM signal
to monitor.
Optionally the number of pulses for a complete revolution
may be specified. It defaults to 1.
Optionally a weighting may be specified. This is a number
between 0 and 1 and indicates how much the old reading
affects the new reading. It defaults to 0 which means
the old reading has no effect. This may be used to
smooth the data.
Optionally the minimum RPM may be specified. This is a
number between 1 and 1000. It defaults to 5. An RPM
less than the minimum RPM returns 0.0.
"""
self.pi = pi
self.gpio = gpio
self.pulses_per_rev = pulses_per_rev
if min_RPM > 1000.0:
min_RPM = 1000.0
elif min_RPM < 1.0:
min_RPM = 1.0
self.min_RPM = min_RPM
self._watchdog = 200 # Milliseconds.
if weighting < 0.0:
weighting = 0.0
elif weighting > 0.99:
weighting = 0.99
self._new = 1.0 - weighting # Weighting for new reading.
self._old = weighting # Weighting for old reading.
self._high_tick = None
self._period = None
pi.set_mode(gpio, pigpio.INPUT)
self._cb = pi.callback(gpio, pigpio.RISING_EDGE, self._cbf)
pi.set_watchdog(gpio, self._watchdog)
def _cbf(self, gpio, level, tick):
if level == 1: # Rising edge.
if self._high_tick is not None:
t = pigpio.tickDiff(self._high_tick, tick)
if self._period is not None:
self._period = (self._old * self._period) + (self._new * t)
else:
self._period = t
self._high_tick = tick
elif level == 2: # Watchdog timeout.
if self._period is not None:
if self._period < 2000000000:
self._period += (self._watchdog * 1000)
def RPM(self):
"""
Returns the RPM.
"""
RPM = 0.0
if self._period is not None:
RPM = 60000000.0 / (self._period * self.pulses_per_rev)
if RPM < self.min_RPM:
RPM = 0.0
return RPM
def cancel(self):
"""
Cancels the reader and releases resources.
"""
self.pi.set_watchdog(self.gpio, 0) # cancel watchdog
self._cb.cancel()
if __name__ == "__main__":
import time
import pigpio
import read_RPM
RPM_GPIO = 4
RUN_TIME = 60.0
SAMPLE_TIME = 2.0
pi = pigpio.pi()
p = read_RPM.reader(pi, RPM_GPIO)
start = time.time()
while (time.time() - start) < RUN_TIME:
time.sleep(SAMPLE_TIME)
RPM = p.RPM()
print("RPM={}".format(int(RPM+0.5)))
p.cancel()
pi.stop() |
I have nearly everything working with two new inputs, rpm and pwm sensing. I need sleep, so I'll finish it tomorrow and test it with the scope. I'll push where I'm at tonight. |
You left me speechless when saw you already had it this morning! Should receive the MOS modules tomorrow, will test and report. I assume you pushed to 3.6, should I pull that branch? |
Hah! I got in a good coding mood and thought those were some really nice inputs that I haven't thought of before. Just use the scripts I commented with above (it's the same code in Mycodo). Currently the code has been committed but I haven't made a release that users can upgrade to. In order to get the new code that hasn't been released you would have to clone the repository with git and copy over your database file. I'll release the next version soon. I don't feel like going into all the issues that can arise from cloning the repo if you're not familiar with it, so I'll just say test with the scripts until the release. I'm going to work on the PID using different outputs feature tonight and also get more RPM and PWM Input options added. If I get all that done tonight I'll make a release. Otherwise, expect one sometime this weekend. |
Also, I'd like to have someone test the code before the release, so it's better you work with the bare scripts so it's easy to modify and retest. |
Rereading your comment you appear to be familiar with git. So, if you want to test the code that's been committed, go ahead and see what happens. I pushed to master. |
I setup the PWM output driving a LED until I receive the MOS modules. I put these two scripts on
running Command Line parameter verbatim on CLI returns:
So it is working from the CLI. However mycodo seems to struggle digesting the script output (no values showing on Data > Live Measurements). Relevant snippet of
System:Stock mycodo 5.3.0 over a freshly installed NOOBs 2.4.4, both installed yesterday from scratch. |
The Linux Command Input requires the script to return a single value representing a number (integer or float). This value should be the value of the Measurement and Unit you set in the Input options. |
I'll add a more verbose error that indicates to the user that the return value of the script is not a single numerical value. |
…ipt not included, system on master will be broken with this commit until added) (#302)
I just released v5.3.1 Let me know if everything works for you after you've upgraded. Thanks. |
Wow! :) thanks! |
…RPM Input issues (#302), Release v5.3.2
I made another release with fixes for various PWM and RPM issues. You'll need to delete your current PWM and RPM Inputs and add them again for everything to work properly. |
(related to #301)
I think IRF520 MOS Module 140C07 suits much better the purpose for fan control than HG7881/9110, it supports 3.3-5V for logic and up to 24V for power, up to 1A w/o heatsink, and up to 5A with a proper one. Uses a single GPIO.
Is just a typical mosfet circuit, but nowadays most often is cheaper to buy already assembled than sourcing components...
A nice addition would be integrating the reading of the fan hall sensor into Mycodo, this will mean a second GPIO would be used, but being able to read the RPM reported by the fan would open up possibilities...
AFAIK, according to this post
Could sensor wire be directly connected to RPi GPIO in this application (using the MOS module feeding 12V to the Fan from external PSU)? or should additional circuitry needed to hook up the fan sensor wire (yellow one) to RPi?
The way I envision this functionality: create a new input sensor: fan tachometer, that will take care of the specified GPIO pin setting (pullup etc), reading, etc. Any required parameters could be manually especified if needed (e.g. Fan DC voltage if required for the RPM calculations).
There are zillions of all sorts of DC/PC Fans with really respectable CFMs; for suitable applications they can be a cheaper, safer, and more precise option (control-wise) than using AC Fans I think.
Moreso, alarms or conditionals could be set in the event of the fan RPMs being out of expected ranges (failing or stuck fan, etc).
The text was updated successfully, but these errors were encountered: