Giuseppe Parrello

 

Embedded PWM Fan


Introduction

This project is based on a 4-pin PWM fan, for testing I have used a Noctua NF-A14 PWM fan, powered at 12 volts - the power must be supplied via an external power supply. We are going to manage this fan in such a way as to be able to turn it on/off or to change its speed. For ease we are going to use the FTDI FT232H development board (of which there is a dedicated page on this site) and the NXP PCA9685 development board (of which there is a dedicated page on this site).
This project is divided into two parts, the second part is simply a variation of the first part and includes two switches that are used to change the fan speed.

 

Project 1 - Connection

This fan must be partially connected to the NXP PCA9685 development board. The connection connectors are listed below:

Image Board FT232H Board PCA9685 Fan

FT232H - PCA9685 - PWM FAN

AD0 SCL ------
AD1 + AD2 SDA ------
+5V VCC ------
GND GND ------
------ Channel 0 - PWM PWM signal
------ ------ RPM signal (not used)
------ ------ VCC (power supply)
------ ------ GND (power supply)

 

Project 1 - Python code

To use this project, the presence of the "PCA9685_FTDI" library is required. The Python code is the following:

import time
import pca9685_ftdi

"""
  Blue   = PWM signal
  Green  = RPM speed signal
  Yellow = +VCC
  Black  = GND
"""

# Create library object using our FTDI I2C port
pwm = pca9685_ftdi.PCA9685_FTDI()

MIN       = 300
STEP      = 5
SLEEP     = 0.025
CHANNEL   = 0

try:
    while True:
        print("\nFAN off")
        pwm.setPWM(CHANNEL, 0, 0)
        time.sleep(10)
 
        print("\nFAN full speed")
        pwm.setPWM(CHANNEL, 0, 4095)
        time.sleep(10)

        print("\nIncreasing FAN speed")
        for fadeValue in range(MIN, 4096, STEP):
            pwm.setPWM(CHANNEL, 0, fadeValue)
            time.sleep(SLEEP)

        print("\nDecreasing FAN speed")
        for fadeValue in range(4095, MIN - STEP, -STEP):
            pwm.setPWM(CHANNEL, 0, fadeValue)
            time.sleep(SLEEP)

        time.sleep(2)

except KeyboardInterrupt:
    # Capture keyboard ^C to exit the program
    print('\nYou terminated the program. The program ends!')
    #pwm.clearAllPins()
    pwm.set_all_pwm(0, 0)
    pwm.close()

 

Project 2 - Connection

This fan must be partially connected to the NXP PCA9685 development board, while the two switches must be connected to FTDI FT232H development board. The connection connectors are listed below:

Image Board FT232H Board PCA9685 Fan Switch 1 Switch 2

FT232H - PCA9685 - PWM FAN - SWITCHES

AD0 SCL ------ ------ ------
AD1 + AD2 SDA ------ ------ ------
+3.3V VCC ------ ------ ------
GND GND ------ ------ ------
------ Channel 0 - PWM PWM signal ------ ------
------ ------ RPM signal (not used) ------ ------
------ ------ VCC (power supply) ------ ------
------ ------ GND (power supply) ------ ------
+5V ------ ------ Pin 1 Pin 1
AD3 ------ ------ Pin 2 ------
AD4 ------ ------ ------ Pin 2

 

Project 2 - Python code

To use this project, the presence of the "PCA9685_FTDI" library is required. The Python code is the following:

import time
import pca9685_ftdi

"""
  Blue   = PWM signal
  Green  = RPM speed signal
  Yellow = +VCC
  Black  = GND
"""

GPIO_IN_OFFSET = 3  # FT232H GPIO inputs are AD3-AD4 (AD0-AD2 are reserved to I2C)

MIN          = 0
MAX          = 4096
STEP         = 256
SLEEP        = 0.25
CURRENTVALUE = MIN
CHANNEL      = 0

# Create library object using our FTDI I2C port
pwm = pca9685_ftdi.PCA9685_FTDI()
pwm._i2c.set_retry_count(16)
gpio = pwm._i2c.get_gpio()
# We set the two GPIO pins to write mode as we need to set them to 0 before reading the values
# (otherwise, if we set them to read mode, the values will be always 1).
gpio.set_direction(0b11000, 0b11000)
gpio.write(0b000000)
pwm.setPWM(CHANNEL, 0, 0)
print(CURRENTVALUE)

try:
    while True:
        din = (gpio.read(True) >> GPIO_IN_OFFSET) & 0b11
        if (din == 1 or din == 2):
           if (din == 1):
              CURRENTVALUE = CURRENTVALUE + STEP
           if (din == 2):
              CURRENTVALUE = CURRENTVALUE - STEP
           if (CURRENTVALUE < MIN):
              CURRENTVALUE = MAX
           if (CURRENTVALUE > MAX):
              CURRENTVALUE = MIN
           print(CURRENTVALUE)
           pwm.setPWM(CHANNEL, 0, CURRENTVALUE)
           time.sleep(SLEEP)

except KeyboardInterrupt:
    # Capture keyboard ^C to exit the program
    print('\nYou terminated the program. The program ends!')
    #pwm.clearAllPins()
    pwm.set_all_pwm(0, 0)
    pwm.close()