Infrared Fan Speed Control
This project demonstrates using an infrared light beam to measure the speed of a fan, and PWM to adjust the speed to a preset value. I used a small 12 Vdc fan from a PC CPU heat sink. It will run on 5V, somewhat slow, and drawing only about 40 mA, perfect for this project. Although an Arduino digital output could safely drive the fan, I decided to play it safe and use a transistor. Here’s my schematic, created with TinyCAD:
The 10K Ohm pot sets the target speed. The PWM output on pin D5 drives the transistor to regulate the motor speed. The fan blades interrupt the infrared beam, and that generates interrupts via pin D3. Here’s the sketch:
/*
IR_MotorSpeed.pde miketranch comcast.net V0.4 Feb 8, 2013
10K pot sets desired speed
IR detector measures fan speed
adjusts PWM voltage to regulate speed
Arduino UNO pins used:
D3 IR detector, interrupt 1
D5 PWM output drives motor
A0 10K pot
*/
const int potPin = 0; // 10K pot 5V to ground, wiper to pin A0
const int PWMLED = 5; // PWM output to transistor to motor
const int IR_Pin = 3; // Hall Sensor Output on Pin 3
const int IR_Int = 1; // Int #1 on Pin 3
volatile int count = 0; // count interrupts from IR Sensor
void setup()
{
pinMode(IR_Pin, INPUT_PULLUP); // open collector IR detector
Serial.begin(9600);
Serial.println(“\n\nInfrared Fan Motor Speed Control”);
}
void loop() {
// static variables remember their values
static int PWMval; // latest attempt to set target speed
// regular variables are initialized each time loop() executes
int potVal = analogRead(potPin); // read the voltage on the pot
// map to desired speed range
int SetSpeed = map(potVal, 0, 1023, 50, 400);
if (count == 0) { // motor stopped, kick start it
PWMval = 254; // full voltage and power
Serial.println(“Full Power Start”);
} else { // adjust speed
float delta = (SetSpeed – count); // speed error
if (delta > 0 ) // too slow, increase voltage
PWMval = PWMval + sqrt(delta); // by square root of error
else // too fast, decrease voltage
PWMval = PWMval – sqrt(-delta); // by square root of error
}
PWMval = constrain(PWMval, 0, 255); // PWM must be 0 to 255
Serial.print(“Set speed: “); Serial.print(SetSpeed);
Serial.print(” PWM: “); Serial.print(PWMval);
analogWrite(PWMLED, PWMval); // set the motor voltage level
delay(1000); // give motor time to settle to new speed
count = 0;
attachInterrupt(IR_Int, IR_ISR, CHANGE);
delay(1000); // measure for 1 second
detachInterrupt(IR_Int); // disable interrupt
Serial.print(” Speed: “); Serial.println(count);
}
void IR_ISR() {
count++;
}
It was fun working out the math to get this sketch working well with the hardware. The interrupt is enabled for 1 second, and counts both edges of each blade, because of the CHANGE keyword. I empirically determined the motor could be slowed to about 40 interrupts per second, or sped up to about 420 interrupts per second. The map function converts the 10K pot setting to a SetSpeed value in the range of 50 to 400.
IR_MotorSpeed.pde miketranch comcast.net V0.4 Feb 8, 2013
10K pot sets desired speed
IR detector measures fan speed
adjusts PWM voltage to regulate speed
Arduino UNO pins used:
D3 IR detector, interrupt 1
D5 PWM output drives motor
A0 10K pot
*/
const int potPin = 0; // 10K pot 5V to ground, wiper to pin A0
const int PWMLED = 5; // PWM output to transistor to motor
const int IR_Pin = 3; // Hall Sensor Output on Pin 3
const int IR_Int = 1; // Int #1 on Pin 3
volatile int count = 0; // count interrupts from IR Sensor
void setup()
{
pinMode(IR_Pin, INPUT_PULLUP); // open collector IR detector
Serial.begin(9600);
Serial.println(“\n\nInfrared Fan Motor Speed Control”);
}
void loop() {
// static variables remember their values
static int PWMval; // latest attempt to set target speed
// regular variables are initialized each time loop() executes
int potVal = analogRead(potPin); // read the voltage on the pot
// map to desired speed range
int SetSpeed = map(potVal, 0, 1023, 50, 400);
if (count == 0) { // motor stopped, kick start it
PWMval = 254; // full voltage and power
Serial.println(“Full Power Start”);
} else { // adjust speed
float delta = (SetSpeed – count); // speed error
if (delta > 0 ) // too slow, increase voltage
PWMval = PWMval + sqrt(delta); // by square root of error
else // too fast, decrease voltage
PWMval = PWMval – sqrt(-delta); // by square root of error
}
PWMval = constrain(PWMval, 0, 255); // PWM must be 0 to 255
Serial.print(“Set speed: “); Serial.print(SetSpeed);
Serial.print(” PWM: “); Serial.print(PWMval);
analogWrite(PWMLED, PWMval); // set the motor voltage level
delay(1000); // give motor time to settle to new speed
count = 0;
attachInterrupt(IR_Int, IR_ISR, CHANGE);
delay(1000); // measure for 1 second
detachInterrupt(IR_Int); // disable interrupt
Serial.print(” Speed: “); Serial.println(count);
}
void IR_ISR() {
count++;
}
It was fun working out the math to get this sketch working well with the hardware. The interrupt is enabled for 1 second, and counts both edges of each blade, because of the CHANGE keyword. I empirically determined the motor could be slowed to about 40 interrupts per second, or sped up to about 420 interrupts per second. The map function converts the 10K pot setting to a SetSpeed value in the range of 50 to 400.
For more detail: Hooked on Arduino & Raspberry Pi