3 wire fan reading RPM when speed controlled by PWM

hi there,,
i am really new in using arduino and i am trying to build a small project that read the RPM from PC fan and control the speed by using PWM.

for the PWM reading am referring to this articular example:

which is works great with me and i can read the RPM value

for the PWM control am using the fading example in the analog .. "using the comment analogWrite(Pin number, fade value);"

the problem start when i am trying to join these two codes together. The RPM reading goes wrong

i connected the tachometer output to oscilloscope and i get a freak output

any suggestion?

wazeer91:
any suggestion?

I suggest you read the 'sticky' at the top of this forum and show us the code you're having difficulty with.

int NbTopsFan;
int Calc;
int hallsensor = 2;
int Pin = 9;
int temp = 0;
float tempC;


typedef struct{                  //Defines the structure for multiple fans and their dividers
  char fantype;
  unsigned int fandiv;
}fanspec;

//Definitions of the fans
fanspec fanspace[3]={{0,1},{1,2},{2,8}};

char fan = 1;   //This is the varible used to select the fan and it's divider, set 1 for unipole hall effect sensor
               //and 2 for bipole hall effect sensor

void rpm ()      //This is the function that the interupt calls
{
NbTopsFan++;
}

void setup() {
pinMode(hallsensor, INPUT);
pinMode(Pin, OUTPUT);
Serial.begin(9600);
attachInterrupt(0, rpm, RISING);
}


void lp ()
{
  
   NbTopsFan = 0; //Set NbTops to 0 ready for calculations
   sei(); //Enables interrupts
   delay (1000); //Wait 1 second
   cli(); //Disable interrupts
   Calc = ((NbTopsFan * 60)/fanspace[fan].fandiv); //Times NbTopsFan (which is apprioxiamately the fequency the fan is spinning at) by 60 seconds before dividing by the fan's divider
   Serial.print (Calc, DEC); //Prints the number calculated above
   Serial.print (" rpm\r\n"); //Prints " rpm" and a new line
}


void loop ()
{
  
tempC = analogRead(temp);          
tempC = (5.0 * tempC * 100.0)/1024.0;  
Serial.println(tempC);
  
if (tempC < 30)
{
  analogWrite(Pin, 127);
  lp();
}

else
{
  analogWrite(Pin , 255);
  lp();
}
}

here is my code, which i want to control the fan speed depend on the temperature reading from LM35 sensor

the code just work great without the rpm.

i make the rpm reading in another function and i call it when i need it.

if i turn on my hardware with temperature higher than 30C it works fine, but when the temperature falls under 30C here start the wrong reading for RPM

i read about it and i find some guy has solved this problem using pulse stretching method .. he is using PIC microcontroller, here is the article

wazeer91:
hi there,,
i am really new in using arduino and i am trying to build a small project that read the RPM from PC fan and control the speed by using PWM.

for the PWM reading am referring to this articular example:
http://www.themakersworkbench.com/content/tutorial/reading-pc-fan-rpm-arduino

which is works great with me and i can read the RPM value

for the PWM control am using the fading example in the analog .. "using the comment analogWrite(Pin number, fade value);"

the problem start when i am trying to join these two codes together. The RPM reading goes wrong

i connected the tachometer output to oscilloscope and i get a freak output

any suggestion?

I need to do a similar thing.

Did you not have any problems getting the RPM reading? I can only see 0 rpm. Everything is connected correctly.

How far are you with yours?

wazeer91:
The problem start when i am trying to join these two codes together. The RPM reading goes wrong

Of course it does. PWM switches the motor on and off at a high frequency. Well the "tacho" wire from the fan is powered from the motor (winding) so every time the PWM switches the motor off, it switches off the tacho wire as well.

The best way to do this is to start with a 4-wire PC fan. These fans have a separate PWM input, which you can drive from an Arduno PWM output using just a resistor or diode, or even directly. No need to use a transistor to switch the power to the fan, and no problems reading the RPM.

I was gonna say, would I need two arduino boards to read and PWM the fan :grin:

So I've got one of these: ARCTIC - Cooling, Mounts, Equipment and I've managed to read the RPM using this code only: http://www.darrinhodges.com/reading-fan-tacho-with-arduino/ all other examples gave me 0 rpm readings.

The code:

volatile int counter = 0;
volatile int result = 0;
volatile int led = 13;
void setup() {
 cli();
 TCCR1A = 0;
 TCCR1B = 0;
 TCNT1 = 0;
 OCR1A = 15624; //(16*10^6) / (1024 * 1) -1
 TCCR1B |= (1 << WGM12);
 TCCR1B |= (1 << CS12) | (1 >> CS10);
 TIMSK1 |= (1 << OCIE1A);
 sei();
 pinMode(led, OUTPUT);
 attachInterrupt(0,rpm,RISING);
 Serial.begin(9600);
 }
ISR(TIMER1_COMPA_vect) {
 result = (counter / 2) * 60;
 counter = 0;
 }|
void rpm() {
 counter++;
 }
void loop() {
 Serial.println(result);
 }

What I'd like now is to display it on a LCD 20x4.

Paul__B:
Of course it does. PWM switches the motor on and off at a high frequency. Well the "tacho" wire from the fan is powered from the motor (winding) so every time the PWM switches the motor off, it switches off the tacho wire as well.

Correct. Two strategies can cope with this problem:

  1. Low-pass filter to smooth the PWM into a steady voltage. May require a substantial filter for fan that draws a lot of current.
  2. PWM at a low frequency and only count tacho pulses when the output is high. You'll need to set the frequency low enough and the RPM high enough that you can count at least two pulses per high interval, but this may put the fan into the range of audio frequencies so you may hear a "whine".

dc42 has the best solution though.

dc42:
The best way to do this is to start with a 4-wire PC fan. These fans have a separate PWM input, which you can drive from an Arduno PWM output using just a resistor or diode, or even directly. No need to use a transistor to switch the power to the fan, and no problems reading the RPM.

dc42, please could you take a look at my code? I'm trying to control my Arctic Cooling PWM F12 Fan.

// RPM reader that works with Arctic Cooling F12 PWM fan


 #include <LiquidCrystal.h>
 
 LiquidCrystal lcd(12, 11, 7, 6, 5, 4);


  int RPM, Light, lux2PWM;
  
 
 #define rpmPin 3
 #define lightPin 1



volatile int counter = 0;
volatile int result = 0;
volatile int led = 13;
volatile int PWMpin = 9;
void setup() {
 cli();
 TCCR1A = 0;
 TCCR1B = 0;
 TCNT1 = 0;
 OCR1A = 15624; //(16*10^6) / (1024 * 1) -1
 TCCR1B |= (1 << WGM12);
 TCCR1B |= (1 << CS12) | (1 >> CS10);
 TIMSK1 |= (1 << OCIE1A);
 sei();
 pinMode(led, OUTPUT);
 pinMode(PWMpin, OUTPUT);
 attachInterrupt(0,rpm,RISING);
 Serial.begin(9600);
 lcd.begin(20, 4);
 }
ISR(TIMER1_COMPA_vect) {
 result = (counter / 2) * 60;
 counter = 0;
 }
void rpm() {
 counter++;
 }
 

 void loop() {
//  Serial.println(result);
// lcd.setCursor(0,1);
// lcd.print(result);
// lcd.clear();
  Light = toLux(analogRead(lightPin));
  lux2PWM = (Light/3.1372549019607843137254901960784);
  showData(RPM, Light);

  delay(1000);
 }
 
void PWM() {
  analogWrite(PWMpin,lux2PWM);
}

int toLux(int adc)
{
//  return (map(adc, 0, 1023, 900, 0)); simple linear model
  if (adc > 975)
    return 1;
  else if (adc > 768)
    return 1 + 0.04 * (adc - 768);
  else if (adc > 341)
    return 10 + 0.16 * (adc - 341);
  else if (adc > 293)
    return 80 + 0.82 * (adc - 293);
  else if (adc > 236)
    return 120 + 0.53 * (adc - 236);
  else if (adc > 171)
    return 150 + 1.52 * (adc - 171);
  else if (adc > 156)
    return 250 + 3.46 * (adc - 156);
  else
    return 300 + 5.6 * (adc - 67);
}




 void showData (int RPM, int light) {
  String s1, s2, s3;
  String spaces = "              ";            
  s1 = String(result) + " RPM "; 
  s2 = String(lux2PWM) + " PWM  ";
  s3 = s1 + spaces.substring(0, 20 - s1.length() - s2.length()) + s2;
  lcd.setCursor(0,0);
  lcd.print(s3);
}

I can see a few issues:

 TCCR1B |= (1 << CS12) | (1 >> CS10);

You have 1 >> CS10 instead of 1 << CS10.

  1. You are using timer 1 to define a gate in which to count RPM pulses. But timer 1 is used to control PWM on pin 9, which is the one you are using to control the fan speed. Either use a different PWM pin (but not pin 10 either), or see here Frequency Counter Library - #36 by dc42 - Science and Measurement - Arduino Forum for a way to measure frequency (and hence RPM) without taking over one of the timers.

  2. rpmPin should be declared const but not volatile.

  3. You are PWMing the fan at the default frequency of 490Hz, whereas the Intel PWM fan specification calls for 25kHz. But 490Hz may work OK.

Great, but the problem I'm facing now is that I have no more pins left. I have the LCD connected. is there any way to use less pins for LCD?

http://code.google.com/p/shiftreglcd123/
https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home

What do you have connected to all the other pins? A standard character LCD need to be connected to 6 pins, but 5 of those pins (all except the Enable input to the LCD) can often be shared with other devices.

Just before reading the fan speed put he fan to the max, then read the tacho, then put it back to your speed, you need only to read the tacho for maybe 1/10 of a sec or lower, and every N seconds, so this trick doesn't change your fan speed, nor you'll notice it in any way (sound)

I have successfully done that in my project.

dc42:
What do you have connected to all the other pins? A standard character LCD need to be connected to 6 pins, but 5 of those pins (all except the Enable input to the LCD) can often be shared with other devices.

At the moment I'm using pins 12, 11, 7, 6, 5, 4 for LCD.

tylernt:
Google Code Archive - Long-term storage for Google Code Project Hosting.
https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
GitHub - marcmerlin/NewLiquidCrystal: LiquidCrystal library with object support to allow for multiple kinds of connections (3 wires through shift registers, or even a 2 wire SR hack) I2C support is unmaintained and probably broken

Thanks for this. I'm gonna take a look.

I don't want to hijack anybodies thread, because my goal is to use PWM to control TEC/Peltier devices.

Guy's if you have 5 minutes, I would appreciate some advice on this:

http://forum.arduino.cc/index.php?topic=196207.msg1447928#msg1447928

zYxMa:
At the moment I'm using pins 12, 11, 7, 6, 5, 4 for LCD.

Yes, but what are you using all the remaining pins for? i.e. 0-3, 8, 9, 10, 13, A0-A6.

dc42:

zYxMa:
At the moment I'm using pins 12, 11, 7, 6, 5, 4 for LCD.

Yes, but what are you using all the remaining pins for? i.e. 0-3, 8, 9, 10, 13, A0-A6.

Not much really. I use 1 analog pin and pwm pin 13 and that's it. :roll_eyes:

zYxMa:
Great, but the problem I'm facing now is that I have no more pins left.

zYxMa:
At the moment I'm using pins 12, 11, 7, 6, 5, 4 for LCD.
...
I use 1 analog pin and pwm pin 13 and that's it.

I'm confused. You say you're out of pins, but you have 0, 1, 2, 3, 8, 9, 10, and 5 analog pins available?

tylernt:

zYxMa:
Great, but the problem I'm facing now is that I have no more pins left.

zYxMa:
At the moment I'm using pins 12, 11, 7, 6, 5, 4 for LCD.
...
I use 1 analog pin and pwm pin 13 and that's it.

I'm confused. You say you're out of pins, but you have 0, 1, 2, 3, 8, 9, 10, and 5 analog pins available?

Out of PWM pins, that's what I mean, but in future I'd like to control more PWM devices and yet have the LCD connected. Also dc42 suggested that I don't use pin 9 and 10 for what I wanted. Now I'm confused.

Ah, I see. See:

http://code.google.com/p/rogue-code/wiki/SoftPWMLibraryDocumentation