Skip to content

Arduino: Read out and control a 4-pin CPU fan

Today I tried to read out the CPU speed information of a 4-wire CPU fan (Intel DTC AAL03) and additionaly I tried to control the fan speed by PWM.

The 4-wire system looks like that:

black – GND
yellow – +12VDC
green – pulse tachometer (for my Intel CPU cooler this is 2 pulses per revision)
blue – PWM value for controlling the fan

I used a external power supply for the +12VDC line. Power consumption was typically 30mA (min RPM) to 60mA (max. RPM) with some short 70mA bursts when changing PWM signal.

I used my signal generator to control the PWM value during my first tests. I tried out some duty cycles and the fan reaches min rpm at 20% duty cycle, that is exactly what the datasheet expects it to be. Max RPM is reached at 97% duty cycle.

If you don’t use PWM input at all the fan still runs at min. RPM speed 2250, max rpm was 4350.

I connected the green wire to my Arduino digital line 0. The fan expects an external pull-up to be present, so I used the internal one. You will need that pull-up resistor enabled, else you won’t get a
stable signal. Via the pulseIn() function you can get the time in microseconds between the pulses. As my fan uses 2 pulses per revision, I have to divide the value by 2.


int fanPulse = 0;
unsigned long pulseDuration;

void setup()
{
Serial.begin(9600);
pinMode(fanPulse, INPUT);
digitalWrite(fanPulse,HIGH);
}

void loop()
{
pulseDuration = pulseIn(fanPulse, LOW);
double frequency = 1000000/pulseDuration;

Serial.print("pulse duration:");
Serial.println(pulseDuration);

Serial.print("time for full rev. (microsec.):");
Serial.println(pulseDuration*2);
Serial.print("freq. (Hz):");
Serial.println(frequency/2);
Serial.print("RPM:");
Serial.println(frequency/2*60);
delay(1000);
}

You can also use your Arduino for controlling the fan speed:

the datasheet of the fan says:
“PWM Frequency: Target frequency 25 kHz, acceptable operational range 21 kHz to 28 kHz”

Well…the arduino has a PWM frequency of: 490,2Hz (measured). So that shouldn’t work at all. But we are lucky and the fan doesn’t bother, it still works well and you can do some nice fan controlling with your arduino. In combination with a temperature sensor you can manage some really nice scenarios.


int fanPulse = 0;
unsigned long pulseDuration;

void setup()
{
Serial.begin(9600);
pinMode(fanPulse, INPUT);
digitalWrite(fanPulse,HIGH);
}

void readPulse() {
pulseDuration = pulseIn(fanPulse, LOW);
double frequency = 1000000/pulseDuration;

Serial.print("pulse duration:");
Serial.println(pulseDuration);

Serial.print("time for full rev. (microsec.):");
Serial.println(pulseDuration*2);
Serial.print("freq. (Hz):");
Serial.println(frequency/2);
Serial.print("RPM:");
Serial.println(frequency/2*60);

}

void loop()
{
analogWrite(3,20);
delay(5000);
readPulse();
analogWrite(3,50);
delay(5000);
readPulse();
analogWrite(3,100);
delay(5000);
readPulse();
analogWrite(3,200);
delay(5000);
readPulse();
analogWrite(3,255);
delay(5000);
readPulse();
}

sample output:

pulse duration:12178
time for full rev. (microsec.):24356
freq. (Hz):41.00
RPM:2460.00
pulse duration:11065
time for full rev. (microsec.):22130
freq. (Hz):45.00
RPM:2700.00
pulse duration:9472
time for full rev. (microsec.):18944
freq. (Hz):52.50
RPM:3150.00
pulse duration:7566
time for full rev. (microsec.):15132
freq. (Hz):66.00
RPM:3960.00
pulse duration:6869
time for full rev. (microsec.):13738
freq. (Hz):72.50
RPM:4350.00

LM35 and the Arduino

Very simple linear temperature sensor.

The sensor outputs 10mV/Deg. C. linear from -55 to +150 C in full range mode.
You can even use the sensor without any supplementary ciruit. Just connect the +5V, GND and one analog input line. In this mode the sensor will work from +2 to +150 C.
As the analogRead() function has a default resolution of 5V/1024 = 4.9mV we multiply the sensor input with that value to get the correct sensor data.


#define vPin 0
void setup() {
Serial.begin(9600);
Serial.println("Setup...");
pinMode(vPin,INPUT);
}

void loop() {
Serial.print(analogRead(vPin)*4.9/10);
Serial.println(" C");
delay(500);
}

simple LCD game…

A little shooting game sketch (it’s exactly only that)…

using the sparkfun LCD, 3 buttons and the Arduino…

Because I was too lazy to get some working random code I used an analog input port on which nothing is connected. Because the obstacles popped out too fast I did the real dirty “if (shuffle == 6)” to pull the hits down…

I didn’t clean up the code…just for someone starting to prog and needs some input… 🙂


#include <i2cmaster.h>
#include <SoftwareSerial.h>

#define txPin 3
#define seedPin 4
#define btnUp 8
#define btnDown 9
#define btnShoot 10
#define rxPin -1
#define DEFAULT_SERIAL_SPEED 9600

#define CLEAR 0x01
#define CURSOR_MOVE_RIGHT 0x14
#define CURSOR_MOVE_LEFT 0x10
#define CURSOR_BOX_BLINK_ON 0x0D
#define CURSOR_BOX_BLINK_OFF 0x0C
#define CURSOR_POS_LINE1 128
#define CURSOR_POS_LINE2 192
#define LINE_LENGTH 16
#define LINE1_END 144
#define LINE2_END 208
#define MAX_MISSING_OBSTACLES = 10;

SoftwareSerial lcd = SoftwareSerial(rxPin, txPin);
int line;
int bullets[LINE_LENGTH] = {0};
int obstacles[LINE_LENGTH] = {0};
int ship = 0;
int points = 0;
boolean gameOverFlag = false;
int missingObstaclesCount = 0;

void setup(){
Serial.begin(DEFAULT_SERIAL_SPEED);
Serial.println("Setup...");

// LC-Display part
// set controller pins
pinMode(txPin, OUTPUT);
// sparkfun default port speed
lcd.begin(DEFAULT_SERIAL_SPEED);
// initialize the game variables
initGame();
// use internal pull-ups for the control lines
digitalWrite(btnUp,HIGH);
digitalWrite(btnDown,HIGH);
digitalWrite(btnShoot,HIGH);
}

void initGame() {
// clear the display first
sendCmd(CLEAR);
// set position to first character
sendCmd(CURSOR_POS_LINE1);
line = CURSOR_POS_LINE1;
ship = line;
points = 0;
for(int i = 0; i < LINE_LENGTH; i++) { bullets[i] = 0; obstacles[i] = 0; } missingObstaclesCount = 0; } void sendCmd(byte cmd) { lcd.write(0xFE); lcd.write(cmd); } void moveBulletsArray() { for (int i = LINE_LENGTH-1; i > 0; i--) {
bullets[i] = bullets[i-1];
}
bullets[0] = 0;
}
void moveObstaclesArray() {
for (int i = LINE_LENGTH-1; i > 0; i--) {
obstacles[i] = obstacles[i-1];
}
obstacles[0] = 0;
}

void moveBullets() {
for (int i = 0; i < LINE_LENGTH; i++) { if (bullets[i] > 0) {
bullets[i]++;
sendCmd(bullets[i]);
lcd.print("-");
}
}
}

void moveObstacles() {
for (int i = 0; i < LINE_LENGTH; i++) { if (obstacles[i] > 0) {
obstacles[i]--;
sendCmd(obstacles[i]);
lcd.print("o");
}
}
}

void collisionDetect() {
for (int i = 0; i < LINE_LENGTH ; i++) { for (int y = 0; y < LINE_LENGTH; y++) { if (obstacles[i] == bullets[y] && obstacles[i] > 0) {
sendCmd(obstacles[i]);
lcd.print("*");
points+=10;
obstacles[i] = 0;
bullets[y] = 0;
}
}
if (obstacles[i] == ship) {
sendCmd(CLEAR);
lcd.print("GAME OVER!");
gameOverFlag = true;
delay(1000);
return;
}
if(obstacles[i] == CURSOR_POS_LINE1 || obstacles[i] == CURSOR_POS_LINE2) missingObstaclesCount++;

if (missingObstaclesCount > 10) {
sendCmd(CLEAR);
lcd.print("GAME OVER!");
gameOverFlag = true;
delay(1000);
return;
}

}
}

void loop(){
int iDown = digitalRead(btnDown);
int iUp = digitalRead(btnUp);
int iShoot= digitalRead(btnShoot);

if(gameOverFlag) {
sendCmd(CLEAR);
lcd.print("GAME OVER!");
sendCmd(CURSOR_POS_LINE2);
lcd.print("Points: ");
lcd.print(points);
delay(1000);
sendCmd(CURSOR_POS_LINE2);
lcd.print("Press Fire...");
delay(1000);
sendCmd(CURSOR_POS_LINE2);
lcd.print("to restart...");
delay(1000);
if(iShoot == LOW) {
gameOverFlag = false;
initGame();
}
return;
}

srand (analogRead(seedPin));
int shuffle = rand() % 10 + 1;

// print the ship
if(iDown==LOW) line = CURSOR_POS_LINE2;
else if(iUp==LOW) line = CURSOR_POS_LINE1;

moveBulletsArray();
moveObstaclesArray();

sendCmd(line);
lcd.print(">");
ship = line;

if (iShoot == LOW) {
bullets[0] = line+1;
}
if (shuffle == 6) {
int sline = CURSOR_POS_LINE1;
srand (analogRead(4));
int shuffleLine = rand() % 10+1;
if (shuffleLine % 2) sline = CURSOR_POS_LINE2;
obstacles[0] = sline + 16;
}

moveBullets();
collisionDetect();
moveObstacles();
collisionDetect();

if (points > 200) {
delay(50);
} else if (points > 100) {
delay(100);
} else if (points > 30) {
delay(150);
} else {
delay(200);
}
sendCmd(CLEAR);
}

Infrared Temperature Sensor MLX90614 and Arduino

The MLX90614 is an infrared temperature sensor. Therefore the cool thing about this is that you can measure the temperature of different surfaces.

My first idea was to measure the brake disc temperature of my bike.  In the first steps I need to accomplish two things,

1) Getting my LCD to work with my Arduino
2) Read out some Data of the MLX90614

I blogged about the first part yesterday, so todoay I decided to give the sensor a try. The sensor can be controlled via I2C bus. I used the I2CMaster library from here: http://bildr.org/2011/02/mlx90614-arduino/
In fact that page gives a pretty good overview about the wiring of the sensor.

The sensor itself needs 2 pull-up resistors on the data line and clock line as stated in the datasheet: http://www.sparkfun.com/datasheets/Sensors/Temperature/SEN-09570-datasheet-3901090614M005.pdf

The datasheet uses a bypass capacitor for stabilizing the current, but I did not use that, it seems my current is stable enough and the sensor in my test circuit works well without it.

 

Now I am electronically ready to measure my brake disc. Now I have to do some physical stuff as the breadboard will look too nerdy on my bike 🙂

 

Arduino 1.0 and Sparkfun Serial LCD

As I was interested in getting data out of my Arduino UNO to a LCD, I just bought a Sparkfun Serial LCD (V2.5) for fun.

The display is almost plug-and-play. As it uses its own PIC 16F88 controller it accepts serial ASCII characters input and some special commands for controlling the display. In a few minutes you are ready with your setup, you just need to wire one digital line from the Arduino board to the display RX pin. You can provide GND and PWR externally or also from your Arduino board (+5V).

The Sparkfun serial library, provided by the manufacturer for interfacing the lCD, is using the external “NewSoftwareSerial”-library, which is now included in the official Arduino 1.0 build, known as “SoftwareSerial” library. In fact I did not modify the Sparkfun Library (it does not work with Arduino 1.0), instead just used the new SoftwareSerial library directly. That worked pretty good for me.

Here is my sketch for a very basic COM-port repeater (I call it repeater because just does that: it sends the data it gets on the serialport to the LC-display).

 

#include <SoftwareSerial.h>

#define txPin 3
#define rxPin -1

#define CLEAR 0x01
#define CURSOR_MOVE_RIGHT 0x14
#define CURSOR_MOVE_LEFT 0x10
#define CURSOR_BOX_BLINK_ON 0x0D
#define CURSOR_BOX_BLINK_OFF 0x0C
#define CURSOR_POS_LINE1 128
#define CURSOR_POS_LINE2 192

SoftwareSerial lcd = SoftwareSerial(rxPin, txPin);

void setup()
{
// set controller pins
pinMode(txPin, OUTPUT);
// set serial mode
Serial.begin(9600);
// sparkfun default port speed
lcd.begin(9600);
// clear the display first
sendCmd(CLEAR);
// set position to first character
sendCmd(CURSOR_POS_LINE1);
}

void loop()
{
// read data if incoming...
if (Serial.available() > 0) {
lcd.write(Serial.read());
}
}

void sendCmd(byte cmd) {
lcd.write(0xFE);
lcd.write(cmd);
}

You can use your command-line to send text to the display:
echo "Hello my friend." > com3:
will output the text on your LCD (if you are using com3 for your arduino).

you can also send the content of a file
type test.txt > com3:
So you can do pretty funny things with your display :)

I just experimented with some scripts which displayed some MUD-Parameters of my character (like skills etc.), the computers IP-Address, current directory of the shell etc…really funny :)

Here is a example PowerShell-Script which reads a file and filters out lines containing a search-pattern.
In fact I’m using this for outputting my MUD AP/PE health information, so I don’t have to switch tasks… :)

function getZeile(){
return (Get-content "C:\temp\mudlog.txt").count-1
}

$port= new-Object System.IO.Ports.SerialPort COM3,9600,None,8,one
$port.Open()

$myvar = (Get-content "C:\temp\mudlog.txt" |find "AP und")[-1]

$myvar = $myvar.replace("und","")

$port.Write([byte]0xFE,0,1)
$port.Write([byte]0x01,0,1)
$port.Write($myvar)

$port.Close()

That only took some minutes from the start. Arduino is great for beginners…