vitormhenrique / OctoPrint-Enclosure

OctoPrint Enclosure Plugin
GNU General Public License v3.0
399 stars 203 forks source link

PWM frequency for Noctua fans #349

Open wmonzel opened 4 years ago

wmonzel commented 4 years ago

Describe the bug Noctua PWM fans typically require a target frequency of 25kHz. Regardless of the PWM frequency specified, the maximum frequency is 5.367 kHz as measured with an oscilloscope. Testing done using a Raspberry Pi 4 (4GB), PWM0 (GPIO12) and PWM1 (GPIO13) octoprint (3).log

To Reproduce Steps to reproduce the behavior:

  1. Octoprint settings -> Enclosure Plugin
  2. Click on "Add Outputs"
  3. Output type: PWM
  4. I/O number: 13
  5. Start with Server
  6. PWM frequency: 25000
  7. Default duty cycle: 50
  8. Save settings and reboot system
  9. Attach scope probe to header pins 33 (PWM1) and 34 (ground) and observe results.

Expected behavior A stable 25kHz square wave with a 50% duty cycle should be observable on the scope.

Additional context See attached log. Note: hardwarePWM does provide expected results.

haplm commented 4 years ago

Interesting. I have just connected Noctua fan (via 74AHCT125 - QUAD LEVEL-SHIFTER) to GPIO19 (it is supposed to be one of the hardware PWM pins), and it seems to work well. I haven't attached the scope, but there's clear difference between say 20% and 100% duty cycle. I can turn off the fan with 0% duty cycle, so everything seems to be working fine.

What does it do in your case?

wmonzel commented 4 years ago

Interesting. I have just connected Noctua fan (via 74AHCT125 - QUAD LEVEL-SHIFTER) to GPIO19 (it is supposed to be one of the hardware PWM pins), and it seems to work well. I haven't attached the scope, but there's clear difference between say 20% and 100% duty cycle. I can turn off the fan with 0% duty cycle, so everything seems to be working fine.

What does it do in your case?

In my case when set to anything greater than 0% the fan does not maintain a constant speed but by the sound seems to run close to 100%, it "revs" - runs high than slows down. I didn't use a 74AHCT125 since the Noctua PWM specifications white paper indicates that one isn't needed. I did not connect a scope probe to the fan's tach output. I stuck with the default pwm hardware pins to hopefully maximize my success.....

haplm commented 4 years ago

Well, this is what most people struggle with. The documentation says: External pull-up is not necessary as the signal is pulled up to 3,3V/5V inside the fan.

People discuss this a lot on various forums as it is unclear which number it is, but if it is 5V, it could damage your Pi. Generally they recommend to use level shifter and convert it to 5V PWM signal. So I did. The circuit costs close to nothing, and can be easily ordered from sites like DigiKey.

With that, my NOCTUA NF-F12 industrialPPC-2000 PWM runs fairly smooth and can be regulated without any issue.

Unfortunately I don't have a scope, so I can't check what's going on on the pin when it runs.

This is how I wired it (it also shows relay controlling LED strips and DHT sensor to measure temperature)

Screenshot 2020-10-18 at 19 41 42
wmonzel commented 4 years ago

I'm convinced that the quad level shifter is not necessary. I did some more readings this morning. These tests were performed using HardwarePWM with a Noctua NF-A14 iPPC-3000 PWM in circuit: Voltage measured with my scope: 3.3v at multiple duty cycles. Current measured with a multimeter in circuit and hardwarePWM set to 100% duty cycle: 20 µA. The Pi GPIO should be able to handle this without issue.
As I mentioned before, with Octoprint-Enclosure the upper frequency limit appears to be just above 5 kHz. Using the same circuit and the same GPIO pins, hardwarePWM sets the frequency correctly. This is either a configuration issue unique to Octoprint-Enclosure or a compatibility issue between Octoprint-Enclosure and the Pi 4 B.
I'm using a Raspberry Pi 4 B. Are you using the same?

vitormhenrique commented 4 years ago

So, today I'm basically using https://sourceforge.net/p/raspberry-gpio-python/wiki/PWM/, no changes what so ever.... not a custom driver or anything...

Could you please write a small script and test if that can make your pi

p = GPIO.PWM(channel, frequency)
p.start(dc)   # where dc is the duty cycle (0.0 <= dc <= 100.0)
p.ChangeFrequency(freq)   # where freq is the new frequency in Hz
p.ChangeDutyCycle(dc)  # where 0.0 <= dc <= 100.0

It might be a limitation on that library.... I'm adding support on PWM from I2C chips on python 3... so need to find a chip that has 25kHz....

haplm commented 4 years ago

Guys, you are forcing me to buy a scope and check what's going on with my fan that it works :-)

Anyway, people recommend to use https://github.com/joan2937/pigpio, that should be capable of the desired frequency.

Some information here: https://www.raspberrypi.org/forums/viewtopic.php?t=244194&start=25

vitormhenrique commented 4 years ago

I saw that library, never used, will install and run some tests, I'm interested to see if requires sudo or any special permission because that is also a major issue with the enclosure and setup for most users...

if @wmonzel could test hist pi, default library, and measure the frequency would be nice.... I'll work more on the plugin tonight...

wmonzel commented 4 years ago

@haplm : hardwarePWM does use PiGPIO, so I think you're on to something. I didn't realize that octoprint-enclosure did not. BTW it occurred to me that the max current draw would be with a 0% duty cycle. I've checked and the current only goes up to 51 µA at 0%. @vitormhenrique: I'll be glad to run that script, but it will have to be in the morning. I want to make sure I've disabled hardwarePWM and possibly PiGPIO too.

Thank you both for your help.

wmonzel commented 4 years ago

@vitormhenrique I haven't taught myself python yet, so I'm probably doing something wrong. My test script pwmtest.py: p = GPIO.PWM(channel, frequency) p.start(0) # where dc is the duty cycle (0.0 <= dc <= 100.0) p.ChangeFrequency(25000) # where freq is the new frequency in Hz p.ChangeDutyCycle(20) # where 0.0 <= dc <= 100.0

Results: pi@octopi:~ $ pwd /home/pi pi@octopi:~ $ python pwmtest.py Traceback (most recent call last): File "pwmtest.py", line 1, in p = GPIO.PWM(channel, frequency) NameError: name 'GPIO' is not defined

What basic steps have I missed?

I looked into raspberry-gpio-python and there is a ticket: https://sourceforge.net/p/raspberry-gpio-python/tickets/173/ that indicates that gpio-python implements software pwm only. Software pwm doesn't seem like a good idea at higher frequencies.

wmonzel commented 4 years ago

@vitormhenrique OK, I figured out that some initialization was in order: Import RPi.GPIO, GPIO.setmode, GPIO.setup. I can provide the updated test script if you like.
I tired several different frequency settings and came up with the following results. I did not bother changing the duty cycle (all tests run at 20%) since the waveform duty cycle appeared to be fairly stable.

set freq observed frequency range comment
1000 870 approximate, not stable. Fan still
2000 1500-1600 freq.range approximate, not stable. Fan speed very inconsistent
3000 2000-2200 freq. range approximate, not stable. Fan speed very inconsistent
4000 1900-2500 freq. range approximate, not stable. Fan speed very inconsistent
5000 2300-2900 freq. range approximate, not stable. Fan speed very inconsistent
10000 4000 freq. not as variable but still not stable. Fan speed very inconsistent
20000 4900-6000 freq. range approximate, not stable. Fan speed very inconsistent
25000 5300 freq. range still not stable, but fan speed is more consistent

This generally agrees with my earlier observations with Enclosure.

vitormhenrique commented 4 years ago

@wmonzel my bad, my script was more a pseudo code than a copy paste and run, but glad to know that you figured it out how to get it running...

as I thought the issue is more on the library than actually on the enclosure plugin...

We need to test with pigpio and see if that is better pip install pigpio

User guide here: http://abyz.me.uk/rpi/pigpio/python.html#set_PWM_frequency

and the circuit python Adafruit library that I planed to use for python 3 port:

https://github.com/adafruit/Adafruit_CircuitPython_Motor

wmonzel commented 4 years ago

@vitormhenrique no worries regarding pseudo code. I could use to learn python. Since hardwarePWM uses pigpio, the tests I've run with hardwarePWM should be good enough to validate pigpio's effectiveness. The output waveform with hardwarePWM is very stable and accurate. I have not compared CPU load but it's a safe bet that using hardware pwm beats software pwm in this area - another advantage for using the hardware pwm gpio pins. I strongly suspect that the waveform stability of a software implemented pwm will further suffer as the CPU load increases, especially at higher frequencies.

I use hardwarePWM as a work around to control my enclosure fan. But I've yet to figure out a way to use hardwarePWM to control fan speed based on some other variable, like temperature. That's where your plug in shines. Right now I periodically monitor the enclosure temperature and adjust the fan speed accordingly.

I'm not sure what you gain by using the circuit python Adafruit library (hardwarePWM doesn't seem to use it), but I do know that hardware based pwm is only available on GPIO12, 13, 18, and 19.

haplm commented 4 years ago

It is weird. The enclosure plugin suddenly lost the ability to control the fan, and I had to install pigpio + hardwarePWM. That works just fine...

wmonzel commented 3 years ago

Hi @vitormhenrique,

I've modified handle_pwm_linked_temperature to use PiGPIO instead of raspberry-gpio-python and have run a few prints with good results. The change within handle_pwm_linked_temperature is to call a new function write_hwpwm instead of write_pwm. Within init.py I also added the necessary "import pigpio" and two small functions (modules? Sorry, I'm still learning Python so I may not be using the right terms) write_hwpwm and startHWPWM. All other sections of your code are unchanged and continue to use RPi.GPIO. I didn't even have to disable the hardwarePWM plugin, I just left the duty cycle at 0. I plan to do some more testing but wanted your input since you mentioned that you are working on a major rewrite. Is this something your are interested in? At the moment I've made the changes outside of Git, so I would either need to attach the file here or go through the normal Git process.

darkcheater commented 3 years ago

@wmonzel could you please share your changes? I am also interested in that because with my Arctic PWM FAN I have a really high gap between 0 % and 1 % when using this low PWM frequency.

wmonzel commented 3 years ago

@wmonzel could you please share your changes? I am also interested in that because with my Arctic PWM FAN I have a really high gap between 0 % and 1 % when using this low PWM frequency.

Hello @darkcheater, sorry for the delayed response. I just noticed it last night.
One file has changed: __init__.py. I've cloned the repo under my name and created a fork with the change. Hopefully you will be able to see it. This is based on @vitormhenrique 's current push. If your current copy of __init__.py is different let me know.

Notes:

ahmaddxb commented 3 years ago

@wmonzel I am using the enclosure plugin to control many hardware pwm pins which i realised dont work with RPi.GPIO. I too tested OctoPrint-Hardwarepwm plugin for my LED strip light and all the flickering is gone. With RPi.GPIO and 100% duty I still have flickering and anything less is just unusable, brightness control is off the table with RPi.GPIO. I have not tested it with my pwm fan which is linked to temperature like you did but im sure it will preform better than it currently does.

I would like to see all the hardware pwm pin available to use PIGPIO Library with the Enclosure plugin in future updates.

wmonzel commented 3 years ago

Hello @ahmaddxb You might try setting the frequency to something slightly greater than 100Hz if in a 50Hz AC country or 120Hz if in a 60Hz AC country. Something slow enough for software PWM to keep up with but fast enough to avoid detectable flicker. This would allow you to keep using RPi.GPIO. The difficulty with just switching to hardware based PWM is that it is only available on GPIO12, 13, 18, and 19.

I'll have a look at possibly expanding what I've done within my own branch (wmonzel/OctoPrint-Enclosure) and will let you know if I have something worth testing.