Pages: [1]   Go Down
Author Topic: Reading Fan RPM when Fan is PWM Controlled  (Read 1471 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a circuit which controls the fan speed based on the amount of light in the room. The fan I'm using is a 3pin fan, the yellow wire is the reporting one. My code is able to read the RPM of the fan when I set the PWM pin to either 0 or 255. Ex:


analogWrite(transistor,0); //RPM 0
analogWrite(transistor,255); // RPM around 900

however, say I were to write

analogWrite(transistor,123); //roughly half the speed

the RPM would not be accurate, and would be very far off.

Here's the code I'm currently using:

Code:
/*To disable interrupts:
 cli();                // disable global interrupts

and to enable them: 
 sei();                // enable interrupts
*/


                                   //Varibles used for calculations
int NbTopsFan;
int Calc;

int ldr; //Light sensor

//Pin Locations

int OnGreenLed = 4;

int OffRedLed = 7;

int SpeedOrangeLed = 11;

int buttonPin = 8; //For Button

int transistor = 9;

int currentState = LOW;

int hallsensor = 2;



 //int ldr = 0;
 //  ldr = analogRead(0)/4;
                       
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++;
}

//This is the setup function where the serial port is initialised,and the interrupt is attached
void setup()
{
  pinMode (hallsensor, INPUT);
  pinMode(OffRedLed,OUTPUT); 
  pinMode(OnGreenLed,OUTPUT);
 
  pinMode(SpeedOrangeLed,OUTPUT);
 
 pinMode(buttonPin, INPUT); 
 Serial.begin(9600);
 attachInterrupt(0, rpm, RISING);
 setPwmFrequency(9,1); //to remove the high frequency sound when fan is being pwm controlled
 
 
}
void loop ()
{
 
  // read the state of the pushbutton value:
  ldr = analogRead(A1);
 currentState = digitalRead(buttonPin);

 
 if(currentState == HIGH){
   
  digitalWrite(OffRedLed, LOW);
  digitalWrite(OnGreenLed, HIGH);
 
  analogWrite(SpeedOrangeLed,ldr/4);
 // delay(10);
  analogWrite(transistor,ldr/4);
     }
 
  else{
    // turn LED off:
    digitalWrite(OffRedLed, HIGH);
    digitalWrite(OnGreenLed, LOW);
   
  digitalWrite(SpeedOrangeLed,LOW);
    analogWrite(transistor,0);
   
 
  }
   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 Button State: "); //Prints " rpm" and a new line
  Serial.print(currentState);
  Serial.print(" ldr reading = ");
Serial.print(ldr);
Serial.print("\n");

}


void setPwmFrequency(int pin, int divisor) {
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) {
      TCCR0B = TCCR0B & 0b11111000 | mode;
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode;
  }
}

http://www.themakersworkbench.com/content/tutorial/reading-pc-fan-rpm-arduino

is the tutorial I followed on how to read the RPM.

Any suggestions?
Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 223
Posts: 6593
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

In order to read the fan speed via the tacho when you are controlling the fan speed, you need to do one of the following:

1. Control the fan speed by linearly adjusting the voltage you supply to it, instead of using PWM; or

2. Use a 4-pin fan. This has a pin for the +12V supply and a separate pin for the PWM control input. The permanent +12V supply allows the tacho to provide an output regardless of the PWM.
Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

texas
Offline Offline
God Member
*****
Karma: 27
Posts: 862
old, but not dead
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

First off, do you have a pullup resistor installed on the TACH wire?  What color are the rest of your fan wires as yellow is usually the +12V wire?  Assuming all that is right, then the main problem is that the hall sensor that generates the TACH signal can only do so with the power on.  Since you are PWMing the main power, it can't generate pulses when the switch is off.  What is your PWM frequency?

dc42 just told you how to fix it, but I already typed all this.  smiley
Logged

Experience, it's what you get when you were expecting something else.

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the responses
In order to read the fan speed via the tacho when you are controlling the fan speed, you need to do one of the following:

1. Control the fan speed by linearly adjusting the voltage you supply to it, instead of using PWM; or

2. Use a 4-pin fan. This has a pin for the +12V supply and a separate pin for the PWM control input. The permanent +12V supply allows the tacho to provide an output regardless of the PWM.

I would imagine both those options require me to buy something, so I'll probably take the loss on this one.

However, I did just search up some info about your first suggestion... it lead me here

http://www.analog.com/library/analogDialogue/archives/38-02/fan_speed.html

I read something about Pulse Stretching...
Is this something you can implement with code?

First off, do you have a pullup resistor installed on the TACH wire?  What color are the rest of your fan wires as yellow is usually the +12V wire?  Assuming all that is right, then the main problem is that the hall sensor that generates the TACH signal can only do so with the power on.  Since you are PWMing the main power, it can't generate pulses when the switch is off.  What is your PWM frequency?

dc42 just told you how to fix it, but I already typed all this.  smiley

Yes I have the pull up resistor, the other wires are red and black (power and ground). PWM frequency I believe is 31250hz.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Haha, I figured it out. I kinda cheated though but it's working for my purposes.

Here's the finished product;

Code:
/*To disable interrupts:
 cli();                // disable global interrupts

and to enable them: 
 sei();                // enable interrupts
*/


                                   //Varibles used for calculations
int NbTopsFan;
int Calc;
long interval1 = 5000;
long time1 = millis();
int ldr; //Light sensor

//Pin Locations

int OnGreenLed = 4;

int OffRedLed = 7;

int SpeedOrangeLed = 11;

int buttonPin = 8; //For Button

int transistor = 9;

int currentState = LOW;

int hallsensor = 2;

int dutycycle = 0;

int previousdc=0;



 //int ldr = 0;
 //  ldr = analogRead(0)/4;
                       
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++;
}

//This is the setup function where the serial port is initialised,and the interrupt is attached
void setup()
{
  pinMode (hallsensor, INPUT);
  pinMode(OffRedLed,OUTPUT); 
  pinMode(OnGreenLed,OUTPUT);
  pinMode(SpeedOrangeLed,OUTPUT);
   pinMode(buttonPin, INPUT); 
  analogWrite(transistor,dutycycle);
 Serial.begin(9600);
 attachInterrupt(0, rpm, RISING);
 setPwmFrequency(9,1);
 
 
}
void loop ()
{
 
  // read the state of the pushbutton value:
  ldr = analogRead(A1);
 currentState = digitalRead(buttonPin);
 unsigned long m = millis();
 
 if(currentState == HIGH){
   if(ldr >= 800)
   {
     dutycycle = 255;
    analogWrite(transistor,dutycycle); //max power
   
  digitalWrite(OffRedLed, LOW);
  digitalWrite(OnGreenLed, HIGH);
  analogWrite(SpeedOrangeLed,dutycycle);
  previousdc=dutycycle;
   }
   else{
   dutycycle = ldr/4;
  digitalWrite(OffRedLed, LOW);
  digitalWrite(OnGreenLed, HIGH);   
  analogWrite(transistor,dutycycle);
  analogWrite(SpeedOrangeLed,dutycycle);
previousdc=dutycycle;
     }
 }
  else{
    // turn LED off:
    dutycycle = 0;
    digitalWrite(OffRedLed, HIGH);
    digitalWrite(OnGreenLed, LOW);
   
  digitalWrite(SpeedOrangeLed,dutycycle);
    analogWrite(transistor,dutycycle);
 previousdc=dutycycle;
   
 
  }
 

  if (m - time1 > interval1){
    time1 = m;

    if (dutycycle == 255)
      dutycycle = 255;
    else
      dutycycle = previousdc;

    digitalWrite(transistor, dutycycle);  //********************************
  }
 
   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
   
if (Calc < 1200)
{
   Serial.print (Calc, DEC); //Prints the number calculated above
  Serial.print (" rpm\r Button State: "); //Prints " rpm" and a new line
  Serial.print(currentState);
  Serial.print(" ldr reading = ");
Serial.print(ldr);
Serial.print("\n");
}
}


void setPwmFrequency(int pin, int divisor) {
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) {
      TCCR0B = TCCR0B & 0b11111000 | mode;
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode;
  }
}

As I'm posting this, I realise I have no idea how it works as I've been using analogWrites(transistor, dutycycle) the whole time, and I BY MISTAKE, have a digitalWrite(transistor,dutycycle), where I tried to create an interval to get the RPM. If I were to replace the digitalWrite with an analogWrite, this code wouldn't work properly. I indicated the point I'm talking about in the code with a //*****************
Logged

Pages: [1]   Go Up
Jump to: