PDA

View Full Version : Arduino Tutorial: Reading PC Fan RPM.



Oneslowz28
06-07-2010, 06:11 AM
http://thebestcasescenario.com/oneslowz28/front_page/tutorials/arduinofantut1.pngBy Charles Gantt ( Oneslowz28 )

Part 1: Outputting Fan RPM to the serial console


What you will need:


1x 3 wire PC fan
1x external 12v Power supply (PC PSU)
1x 10k resistor (http://www.protostack.com/index.php?main_page=product_info&cPath=14_31&products_id=37)
1x Seeeduino or Arduino compatible dev board (http://www.seeedstudio.com/depot/seeeduino-v212-fully-assembled-arduino-compatible-p-389.html?cPath=27).
Jumper Wires (http://www.seeedstudio.com/depot/premium-jumper-wires-140-pcsbox-p-394.html?cPath=34)
1x breadboard (http://www.seeedstudio.com/depot/bread-board-clear-8262-cm-p-262.html?cPath=60_5)



http://themakersworkbench.com/sites/default/files/images/Fan_RPM_Arduino_0.jpg

This is pretty simple to hook up. First you need to run the Signal wire (almost always yellow) to the breadboard. Then from it connect a jumper wire to Arduino Digital Pin 2. Also from the sensor wire you need to connect a 10k resistor to the Arduino's 5V pin. This is a simple pull-up resistor. We also need to make sure that we connect the fans ground line to one of the Arduino's ground pins. Next just connect the fans power wire to the PSUs 12v line and the fans ground wire to the PSUs ground.

Now upload the following code to your Arduino, and then open the serial terminal. Again I would like to thank Crenn for his help with the code.


//code by Crenn from http://thebestcasescenario.com
//project by Charles Gantt from http://themakersworkbench.com


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

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


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

//The pin location of the sensor
int hallsensor = 2;


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);
Serial.begin(9600);
attachInterrupt(0, rpm, RISING);
}
void loop ()
{
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
}

You should see an RPM output in the serial terminal. It is accurate within 10-15 RPM of the fans actual RPM which is close enough for me. If your RPM output seems to be double what it should be your fan may have a bipolar Hall efect sensor and its counting each pass of the magnets pole as a single RPM when each should be 1/2 an RPM. No worries though as this is an easy fix. Just simply change the " char fan = 0 code from 0 to 1. Upload the modified code and you should be seeing accurate RPM numbers. Check IGA Catalogue (https://www.catalogueau.com/iga/) and ALDI Catalogue (https://www.catalogueau.com/aldi/).

My fritzing page on this project. (Download all the files there)
Reading PC Fan / Water Pump RPM with an Arduino (http://fritzing.org/projects/reading-pc-fan-rpm-with-an-arduino/)

Oneslowz28
06-07-2010, 06:12 AM
Reserved for part 2

The boy 4rm oz
06-14-2010, 07:48 AM
Nice tutorial. I think I will be giving this a miss though, reminds me too much of java, I hate java.

Oneslowz28
06-14-2010, 05:45 PM
Its pretty easy after an hour or two of playing around with the code.

SXRguyinMA
06-14-2010, 10:23 PM
yes, I've never coded anything except basic html 8-10 years ago, and I got the hang of it rather quickly :D

SXRguyinMA
01-02-2011, 07:34 PM
alright I adapted this code to work with an LCD and will add a second fan in the future. the problem I have is when the RPM drops below 1000. the readout keeps the characters to the left rather than the right. so 600rpm should be displayed "600" or even "0600" but rather is displayed "6000". and idea how to fix this? here's the code:


//code by Crenn from http://thebestcasescenario.com
//project by Charles Gantt from http://themakersworkbench.com

#include <LiquidCrystal.h>

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

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

and to enable them:
sei(); // enable interrupts
*/
//Varibles used for calculations
int NbTopsFan;
int Calc;
//The pin location of the sensor
int hallsensor = 2;

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 LCD is initialised,
//and the interrupt is attached
void setup()
{
pinMode(hallsensor, INPUT); //Sets RPM pin to input
lcd.begin(16, 2); //Set up the LCD's number of rows and columns
lcd.print(" diluzio91's "); //First line opening message
lcd.setCursor(0, 1);
lcd.print("Fan Control Cntr"); //Second line opening message
delay(5000); //Delay for opening message
lcd.setCursor(0, 1); //Clear bottom line
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(" Fan 1 Fan 2 "); //Update top line readout
attachInterrupt(0, rpm, RISING);
}
void loop ()
{
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
lcd.setCursor(0, 1);
lcd.print (Calc, DEC); //Prints the number calculated above
lcd.setCursor(4, 1);
lcd.print ("RPM"); //Prints " RPM"
}Also, how would I go about adding in a second fan to this setup? And the reading seems to fluctuate a lot. at a steady rpm, it bounces between 1500rpm and 1200

and the setup:
http://i92.photobucket.com/albums/l11/sportrider12584/setup.png

diluzio91
01-02-2011, 09:11 PM
i dont know about adding a 2nd fan, but the reading fluctuation dosn't surprise me, my scythe would bounce around a bunch on some fans, almost not at all on others, try a couple different fans and see what you get?

SXRguyinMA
01-02-2011, 09:16 PM
you only need one fan? well then that's easy lol

I changed the connection point for the RPM pickup and it dropped to a ~150rpm fluctuation

nop
01-06-2011, 09:05 PM
Setting the PORTx bit associated with the input line should provide a 20k pull-up to +5v inside the processor (see 13.2.1 of the data sheet) and http://www.arduino.cc/en/Tutorial/DigitalPins talks about this too. So maybe the resistor isn't necessary.

The Intel 4-pin fan spec at http://www.formfactors.org/developer/specs/4_Wire_PWM_Spec.pdf describes the RPM sense line as open-collector/open-drain and pulled up to +12v on the motherboard. I haven't had any problems with your +5v 10kohm pull-up with the couple of 3-pin fans I've tried, so I'm not sure +12v is necessary (there'd be a voltage divider to get down to 5v.)

If I did want to go to higher voltage pullups, one thing I haven't figured out is what to do when running the fan at a voltage below 12v. Somehow it seems wrong to put any voltage above Vmotor (or whatever the red wire is called) My Vmotor outputs aren't very clean and given how they vary, pulling up to Vmotor just seems like it shouldn't work for me either. So I don't know, and I'm sticking with +5v for now. I could always chicken out and go to an optoisolator for the tach.

swarnakar.ani24
07-23-2016, 02:08 AM
//The pin location of the sensor
int hallsensor = 2;

The interrupt pin depends upon the arduino board Using

Uno, Nano, Mini, other 328-based 2, 3
Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21
Micro, Leonardo, other 32u4-based 0, 1, 2, 3, 7
Zero all digital pins, except 4
MKR1000 Rev.1 0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Due all digital pins
101 all digital pins


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

attachInterrupt(0, rpm, RISING); this will not work according to the syntax . Correct one should be attachInterrupt(digitalPinToInterrupt(pin), rpm, RISING); - (pin) is hall sensor pin for example 2

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode); (recommended)
attachInterrupt(interrupt, ISR, mode); (not recommended)
attachInterrupt(pin, ISR, mode) ; (not recommended Arduino Due, Zero, MKR1000, 101 only)