# Working PWM control and read of 4 fans and 4 TMP36

Just came to a finish of a code that reads 4 temps, controls 4 fans and reads the 4 fans RPM. Thought I'd put it up before I add other features. It comes in parts cause the code is long.

``````/*
* This code does 3 basic things.
*
* 1. It reads the temperature from four TMP36 analogue sensors. This reading
* compares to the internal 2,56 volt reference and the sensors are fed by an
* LP2950 3,3 volt regulator connected to the Arduino Mega 2560's internal 5
* volt. If the TMP36 sensors would measure 125 degrees Celsius they would produce
* 1,75 volt on their vout pin but that it theory as I am not planning to
* measure higher temperatures then around 60 C which would translate to 1,1 volt.
* The analogue pins reads this voltage by getting a value between 0 and 1024. The code
* converts this value to degrees Celsius by dividing the reference voltage which I
* measuered to 2577 mV with 1024 and then subtracting 500 mV and then divide the
* lot with 10. The degrees Celsius I will be able to display on an LCD or whatever.
* However I also do a copy of the ADC-pin reading and constrain this value to
* inbetween 300 and 450 which represents about 25 to 63 degrees Celsius. This value
* I put in a map algorithm that do a linear conversion of the temp value to a value
* that I use for setting the fan contol pins duty cycle between 20 and 320.
*
* 2. It produces a 25 kHz 5 volt p-p squarewave signal on four pins that can be
* modulated in duty cycle between 0 and 100%. Those pins controls one 12 volt
* PWM fan each. The duty cycle value is set by the map algorithm described above.
* I use pin 11 and 12 controlled by timer 1 and 5 and 2 controlled by timer 3
* for this.
*
* 2. It reads the length of the DC fans built in hall sensor(s) activity.
* The readings are in microseconds and with a formula this can be converted
* and presented as Revolutions Per Minute (RPM) and/or percentage spinning
* of the fans max revolutions per time unit. To avoid to much flickering when
* displaying this value it runs to a smoothing algorithm as well that adds the
* last four measurements and displays that total value divided by 4.
* I tested to use the internal built in 20k pullup resistors but the microamp
* current is not enough for stable readings so instead I use external 2,4 k
* resistors that gives around 2 mA of current per pin. This current gets shorted
* to ground by the fans internal mosfets that takes the reading from high to low
* two times per revolution. Noctua recommends up to 5 mA in their specs.
*
* PS. In my first versions of the code I tried working with arrays as long as
* possible but I ran in to problem when trying to attatch the OCR duty cycle
* values to the PWM values in an array so I rewrote it all to straight code,
* hence the length of it all since I do everything four times in the code.
*/

const int tmpPin0 = A12;          // Temp pin assignments
const int tmpPin1 = A13;
const int tmpPin2 = A14;
const int tmpPin3 = A15;

const int pwmPin0 = 11;           // PWM pin assignments
const int pwmPin1 = 12;
const int pwmPin2 = 5;
const int pwmPin3 = 2;

const int hallSensorPin0 = 13;    // Hall sensor pin assignments
const int hallSensorPin1 = 9;
const int hallSensorPin2 = 22;
const int hallSensorPin3 = 23;

int tmpReading0;                  // Variables for the temp

int tmpConstr0;                   // Variables for the temp
int tmpConstr1;                   // sensor Voltages
int tmpConstr2;
int tmpConstr3;

int tmpDegree0;                   // Variables for temp
int tmpDegree1;                   // sensor Degrees Celsius
int tmpDegree2;
int tmpDegree3;

int pwm1A = 120;                  // Variables for the duty
int pwm1B = 120;                  // cycles (0-320 = 0-100%)
int pwm3A = 120;                  // Boot up values 120
int pwm3B = 120;

unsigned long highPulseTime0;     // Variables for the durations
unsigned long highPulseTime1;     // of the high pulses
unsigned long highPulseTime2;
unsigned long highPulseTime3;

unsigned long lowPulseTime0;      // Variables for the durations
unsigned long lowPulseTime1;      // of the low pulses
unsigned long lowPulseTime2;
unsigned long lowPulseTime3;

unsigned long newPulseTime0;      // Variables for the durations
unsigned long newPulseTime1;      // of the new pulses, ie high
unsigned long newPulseTime2;      // plus low pulse
unsigned long newPulseTime3;

int revsPerMinute0;               // Variables for RPM values
int revsPerMinute1;               // calculated from the pulse times
int revsPerMinute2;
int revsPerMinute3;

int smoothedRevsPerMinute0;       // Variables for the
int smoothedRevsPerMinute1;       // smoothed RPM values
int smoothedRevsPerMinute2;
int smoothedRevsPerMinute3;

int percentage0;                  // Variables for percentage value
int percentage1;                  // calculated from their RPM values
int percentage2;                  // and their max RPM values
int percentage3;

const int samples = 4;            // No of samples used for smoothing

const int maxRevFan0 = 1140;      // Constants for each fans maximum
const int maxRevFan1 = 1570;      // revs per minute
const int maxRevFan2 = 2180;
const int maxRevFan3 = 1550;
``````

Void setup part

``````void setup() {

// Use the internal 2,56 volt reference as Vref
analogReference(INTERNAL2V56);

pinMode(tmpPin0, INPUT);        // Tell the Temp pins
pinMode(tmpPin1, INPUT);        // to be inputs
pinMode(tmpPin2, INPUT);
pinMode(tmpPin3, INPUT);

pinMode(pwmPin0, OUTPUT);       // Tell the PWM pins to
pinMode(pwmPin1, OUTPUT);       // be outputs
pinMode(pwmPin2, OUTPUT);
pinMode(pwmPin3, OUTPUT);

pinMode(hallSensorPin0, INPUT); // Tell the Hall Sensor
pinMode(hallSensorPin1, INPUT); // pins to be inputs
pinMode(hallSensorPin2, INPUT);
pinMode(hallSensorPin3, INPUT);

// Timer 1 PWM settings

TCCR1A = 0;                     // Clear timer registers
TCCR1B = 0;
TCNT1 = 0;

TCCR1B |= _BV(CS10);            // No prescaler
ICR1 = 320;                     // PWM mode counts up 320 then down
// 320 counts (25kHz)

OCR1A = pwm1A;                  // 0-320 = 0-100% duty cycle
TCCR1A |= _BV(COM1A1);          // output A clear rising/set falling

OCR1B = pwm1B;                  // 0-320 = 0-100% duty cycle
TCCR1A |= _BV(COM1B1);          // output B clear rising/set falling

TCCR1B |= _BV(WGM13);           // PWM mode with ICR1 Mode 10
TCCR1A |= _BV(WGM11);           // WGM13:WGM10 set 1010

// Timer 3 PWM settings

TCCR3A = 0;                     // Clear timer registers
TCCR3B = 0;
TCNT3 = 0;

TCCR3B |= _BV(CS10);            // No prescaler
ICR3 = 320;                     // PWM mode counts up 320 then down
// 320 counts (25kHz)

OCR3A = pwm3A;                  // 0-320 = 0-100% duty cycle
TCCR3A |= _BV(COM1A1);          // Output A clear rising/set falling

OCR3B = pwm3B;                  // 0-320 = 0-100% duty cycle
TCCR3A |= _BV(COM1B1);          // Output B clear rising/set falling

TCCR3B |= _BV(WGM13);           // PWM mode with ICR1 Mode 10
TCCR3A |= _BV(WGM11);           // WGM13:WGM10 set 1010

Serial.begin(9600);             // Start a serial for debugging readouts

delay(5000);                      // System start delay

}
``````

Void loop part

``````void loop() {

// Read the analog pins for temp values

// Recalculate the readings to Degrees Celsius
tmpDegree0 = ((tmpReading0 * (2577 / 1024.0) - 500) / 10);
tmpDegree1 = ((tmpReading1 * (2577 / 1024.0) - 500) / 10);
tmpDegree2 = ((tmpReading2 * (2577 / 1024.0) - 500) / 10);
tmpDegree3 = ((tmpReading3 * (2577 / 1024.0) - 500) / 10);

// Create a constrained reading for fan controll

// Constrains percentage scale to stay within 0 and 100
tmpConstr0 =  constrain(tmpConstr0, 300, 450);
tmpConstr1 =  constrain(tmpConstr1, 300, 450);
tmpConstr2 =  constrain(tmpConstr2, 300, 450);
tmpConstr3 =  constrain(tmpConstr3, 300, 450);

// Create a value between 20 and 320 from the temp readings
pwm1A = map(tmpConstr0, 300, 450, 20, 320);
pwm1B = map(tmpConstr1, 300, 450, 20, 320);
pwm3A = map(tmpConstr2, 300, 450, 20, 320);
pwm3B = map(tmpConstr3, 300, 450, 20, 320);

// Tell the duty cycle parameter to adapt to the created value
OCR1A = pwm1A;
OCR1B = pwm1B;
OCR3A = pwm3A;
OCR3B = pwm3B;

//  Catch the high pulse durations with the pulseIn command
highPulseTime0 = pulseIn(hallSensorPin0, HIGH);
highPulseTime1 = pulseIn(hallSensorPin1, HIGH);
highPulseTime2 = pulseIn(hallSensorPin2, HIGH);
highPulseTime3 = pulseIn(hallSensorPin3, HIGH);

//  Catch the low pulse durations with the pulseIn command
lowPulseTime0 = pulseIn(hallSensorPin0, LOW);
lowPulseTime1 = pulseIn(hallSensorPin1, LOW);
lowPulseTime2 = pulseIn(hallSensorPin2, LOW);
lowPulseTime3 = pulseIn(hallSensorPin3, LOW);

//  Add respective high pulses to respective low pulses
newPulseTime0 = highPulseTime0 + lowPulseTime0;
newPulseTime1 = highPulseTime1 + lowPulseTime1;
newPulseTime2 = highPulseTime2 + lowPulseTime2;
newPulseTime3 = highPulseTime3 + lowPulseTime3;

//  Calculate revs per minute with total pulse times
revsPerMinute0 = (1000000 * 60)/(newPulseTime0 *2);
revsPerMinute1 = (1000000 * 60)/(newPulseTime1 *2);
revsPerMinute2 = (1000000 * 60)/(newPulseTime2 *2);
revsPerMinute3 = (1000000 * 60)/(newPulseTime3 *2);

//  Smooth out the readings by dividing the last four samples
smoothedRevsPerMinute0 = smoothedRevsPerMinute0
+ ((revsPerMinute0 - smoothedRevsPerMinute0)/samples);
smoothedRevsPerMinute1 = smoothedRevsPerMinute1
+ ((revsPerMinute1 - smoothedRevsPerMinute1)/samples);
smoothedRevsPerMinute2 = smoothedRevsPerMinute2
+ ((revsPerMinute2 - smoothedRevsPerMinute2)/samples);
smoothedRevsPerMinute3 = smoothedRevsPerMinute3 +
((revsPerMinute3 - smoothedRevsPerMinute3)/samples);

// Calculate the fans spinning in percentage of its max
percentage0 = (smoothedRevsPerMinute0
/ (maxRevFan0/100));
percentage1 = (smoothedRevsPerMinute1
/ (maxRevFan1/100));
percentage2 = (smoothedRevsPerMinute2
/ (maxRevFan2/100));
percentage3 = (smoothedRevsPerMinute3
/ (maxRevFan3/100));

// Constrains percentage scale to stay within 0 and 100
percentage0 =  constrain(percentage0, 0, 100);
percentage1 =  constrain(percentage1, 0, 100);
percentage2 =  constrain(percentage2, 0, 100);
percentage3 =  constrain(percentage3, 0, 100);

// Print things to monitor for test purpouses

Serial.print("RPM Fan 1");
Serial.print("\t");
Serial.print("\t");
Serial.println(smoothedRevsPerMinute0, DEC);

Serial.print("Percentage Fan 1");
Serial.print("\t");
Serial.println(percentage0, DEC);

Serial.print("PWM Fan 1");
Serial.print("\t");
Serial.print("\t");
Serial.println(pwm1A, DEC);

Serial.print("\t");
Serial.print("\t");

Serial.println();

Serial.print("RPM Fan 2");
Serial.print("\t");
Serial.print("\t");
Serial.println(smoothedRevsPerMinute1, DEC);

Serial.print("Percentage Fan 2");
Serial.print("\t");
Serial.println(percentage1, DEC);

Serial.print("PWM Fan 2");
Serial.print("\t");
Serial.print("\t");
Serial.println(pwm1B, DEC);

Serial.print("\t");
Serial.print("\t");

Serial.println();

Serial.print("RPM Fan 3");
Serial.print("\t");
Serial.print("\t");
Serial.println(smoothedRevsPerMinute2, DEC);

Serial.print("Percentage Fan 3");
Serial.print("\t");
Serial.println(percentage2, DEC);

Serial.print("PWM Fan 3");
Serial.print("\t");
Serial.print("\t");
Serial.println(pwm3A, DEC);

Serial.print("\t");
Serial.print("\t");

Serial.println();

Serial.print("RPM Fan 4");
Serial.print("\t");
Serial.print("\t");
Serial.println(smoothedRevsPerMinute3, DEC);

Serial.print("Percentage Fan 4");
Serial.print("\t");
Serial.println(percentage3, DEC);

Serial.print("PWM Fan 4");
Serial.print("\t");
Serial.print("\t");
Serial.println(pwm3B, DEC);