Today is a rainy day so it was time for some foul programming. I was unhappy with that if I stopped the fan manually the code started to say that the revs are increasing. That is ofc bec the pulseIn-command reads the pulse lengths and they usually get shorter when the revs are increasing, but with one exception: it returns zero pulselength if it doesnt find a pulse. And zero is short.
I just couldnt make things work with If/ElseIf/Else or While commands so I ended up doing Gotos. Is this considered foul programming, could it be done neater? I shall say the code works. It does alarm when I stop the fan and it goes back to normal if I release it.
/*
* This code 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.
*/
int hallSensorPin = 2; // Pin 2 is used to read the Hall sensors activity
unsigned long previousPulseTime; // Variable for storing the duration of a pulse.
word highPulseTime; // It stores the previous value to have something
word lowPulseTime; // compare a new value with
unsigned long newPulseTime; // Since it is in microseconds it needs to be able
// to store a large value.
int revsPerMinute; // Variable for storing RPM value
int smoothedRevsPerMinute; // Variable for storing the smoothed RPM value
int percentage; // Variable for storing percentage value
int samples = 3; // Amount of samples that will be stored and used
// for smoothing
int ledPin = 13; // Pin 13 is used to test alarm
/*
* Set the pin that shall read the Hall Sensor to pull up its input to 5 V
* with the built in 20k resistor. I have seen some lifting to fan 12 V line but that is
* a much dirtyer voltage then the internal regulated 5 V line. Holding it internal also
* makes the electrical loop much smaller and less sensitive to interference.
* The hall sensors activates a sinking transistor that takes the voltage to LOW when
* the hall sensors are active, ie it shorts its input to ground.
* And start serial for test purpouse read outs to serial monitor.
*/
void setup() {
pinMode(ledPin, OUTPUT); // Set LedPin to output
pinMode(hallSensorPin, INPUT_PULLUP); // Pull up the sensor pin to 5V
highPulseTime = pulseIn(hallSensorPin, HIGH); // Read once for how long the pulse is high
lowPulseTime = pulseIn(hallSensorPin, LOW); // Read once for how long the pulse is low
newPulseTime = highPulseTime + lowPulseTime; // Create a previous value by telling it to
previousPulseTime = newPulseTime; // be the same as the new value we just read.
// to get a starter position
Serial.begin(9600); // We start a serial for debugging readouts
}
/*
* The pulseIn function reads a pulse, either the high part or the low part. My fan makes two
* pulses per revolution, ie high-low-high-low so first I read the high pulse, then I read the
* low pulse. Then in the calculation to revs per minute I sum the high and low pulse and after
* that multiplies by factor 2. This shall be a full revlution that we time.
*
* The pulseIn way of measuring a fans revolutions per time unit is not very exact. A more
* exact way would be to count the pulses over a longer time period. This is on the other hand
* taking up a longer processing time of maybe a second to get good readings. I want to avoid
* that so I read the pulses instead which only takes a couple of milliseconds.
*
* 1200 RPM is 20 RPS which meens a full revolution takes 50 ms. I measure half a revolution.
* The downside with this is that a fan is not very exact when you come down to microseconds
* and the hall sensor readings can once in a while bug out and give a strange value.
* I am on the other hand not interested in weather the fan spins with 1207 or or 1211 revs per
* minute, I am interested to see if they do the work they are set to do so I dont care about
* the exact value. On the other hand I do prefer a value that doesnt jump one step every time
* it renews itself so therefore I am smoothing the output values so every new reading adds
* the three readings before it and divides the total four readings with four to present a
* smoothed average. This makes it less jumpy on the last digit.
*
* I also added an outlier corrector which is described below.
*/
void loop() {
run: // goto tag, see below
digitalWrite(13, LOW);
highPulseTime = pulseIn(hallSensorPin, HIGH); // Read for how long the pulse is high
lowPulseTime = pulseIn(hallSensorPin, LOW); // Read for how long the pulse is low
newPulseTime = highPulseTime + lowPulseTime;
/*
* Here I tried with alot of If, Else If and Else commands but it would not work for me.
* If I tried If => Pulse Time = 0 and Else If if it was not 0, somehow the smoothing
* algorithms below bugged out so it just told me that the revs are going up all the way
* to 100% while tha fan was actually stopped and the Led Pin did not want to shine. I
* guess the interpretation of If => Else If and then new If commands does not work.
* I also tried While for the alarm if the fan stoppes and it worked so far but when I
* released the fan it was stuck in the While-loop.
*/
if (newPulseTime == 0) { // A goto command. If statement is true
goto alarm; // we jump down to alarm: in the end
}
if (newPulseTime < (previousPulseTime * 0.9)) { //Kind of a high-pass filter
(newPulseTime = (previousPulseTime * 0.9)); //Takes outlier values down
}
if (newPulseTime > (previousPulseTime * 1.1)) { //Kind of a high-pass filter
(newPulseTime = (previousPulseTime * 1.1)); //Takes outlier values down
}
revsPerMinute = (1000000 * 60)/(newPulseTime * 2); // Convert pulse time to RPM
// Smoothing out the value by adding earlyer values and divide to an average
smoothedRevsPerMinute = smoothedRevsPerMinute + ((revsPerMinute - smoothedRevsPerMinute)/samples);
// Calculate the fans spinning in percentage of its max which is approximately 1560 RPM
percentage = (smoothedRevsPerMinute / 15.55); //The fans max revs are about 1550
percentage = constrain(percentage, 0, 100); //Constrains percentage scale
// Do some serial printing for checking the result for now
Serial.print("High Pulsetime");
Serial.print("\t");
Serial.print("\t");
Serial.println(highPulseTime, DEC);
Serial.print("Low Pulsetime");
Serial.print("\t");
Serial.print("\t");
Serial.println(lowPulseTime, DEC);
Serial.print("Halfrev Microseconds");
Serial.print("\t");
Serial.println(newPulseTime, DEC);
Serial.print("Revolutions per minute");
Serial.print("\t");
Serial.println(smoothedRevsPerMinute, DEC);
Serial.print("Percentage of max revs");
Serial.print("\t");
Serial.println(percentage, DEC);
previousPulseTime = newPulseTime;
alarm:
if (newPulseTime > 0) { //If the pulse is not zero wait a while and go to run
delay(5000);
goto run;
}
else { //otherwise activate alarm, wait a bit and check again
digitalWrite(ledPin, HIGH);
}
delay(5000);
}