Hi, this is my first complicated Arduino project and I'm struggling to see why my 2 IR sensors are reading 2 different RPMs. I'm looking to use 2 IR sensors to read 2 different objects and light the Green led when they are close to the same speed or light the red LED if it is far off. During my test phase I noticed that RPM gives me an accurate and smooth result in real time while RPM02 is much larger and oscillating. The strange thing is I literally copied the same code yet all my troubleshooting (replacing the IR sensors and switching the inputs) shows that there's something wrong with the code.
My code is 95% based on this Youtube video/code: Arduino Tutorial: Tachometer (RPM Counter) - YouTube
Any help would be greatly appreciated!
const byte PulsesPerRevolution = 2; // Set how many pulses there are on each revolution. Default: 2.
const byte PulsesPerRevolution02 = 2; // Set how many pulses there are on each revolution. Default: 2.
const unsigned long ZeroTimeout = 300000; // For high response time, a good value would be 100000.
// For reading very low RPM, a good value would be 300000.
const unsigned long ZeroTimeout02 = 300000;
// Calibration for smoothing RPM:
const byte numReadings = 5; // Number of samples for smoothing. The higher, the more smoothing, but it's going to
// react slower to changes. 1 = no smoothing. Default: 2.
const byte numReadings02 = 5;
/////////////
// Variables:
/////////////
volatile unsigned long LastTimeWeMeasured; // Stores the last time we measured a pulse so we can calculate the period.
volatile unsigned long LastTimeWeMeasured02; // Stores the last time we measured a pulse so we can calculate the period.
volatile unsigned long PeriodBetweenPulses = ZeroTimeout + 1000; // Stores the period between pulses in microseconds.
// It has a big number so it doesn't start with 0 which would be interpreted as a high frequency.
volatile unsigned long PeriodBetweenPulses02 = ZeroTimeout02 + 1000;
volatile unsigned long PeriodAverage = ZeroTimeout + 1000; // Stores the period between pulses in microseconds in total, if we are taking multiple pulses.
// It has a big number so it doesn't start with 0 which would be interpreted as a high frequency.
volatile unsigned long PeriodAverage02 = ZeroTimeout02 + 1000;
unsigned long FrequencyRaw; // Calculated frequency, based on the period. This has a lot of extra decimals without the decimal point.
unsigned long FrequencyRaw02;
unsigned long FrequencyReal; // Frequency without decimals.
unsigned long FrequencyReal02; // Frequency without decimals.
unsigned long RPM; // Raw RPM without any processing.
unsigned long RPM02; // Raw RPM without any processing.
unsigned int PulseCounter = 1; // Counts the amount of pulse readings we took so we can average multiple pulses before calculating the period.
unsigned int PulseCounter02 = 1;
unsigned long PeriodSum; // Stores the summation of all the periods to do the average.
unsigned long PeriodSum02;
unsigned long LastTimeCycleMeasure = LastTimeWeMeasured; // Stores the last time we measure a pulse in that cycle.
unsigned long LastTimeCycleMeasure02 = LastTimeWeMeasured02;
unsigned long CurrentMicros = micros(); // Stores the micros in that cycle.
unsigned long CurrentMicros02 = micros();
unsigned int AmountOfReadings = 1;
unsigned int AmountOfReadings02 = 1;
unsigned int ZeroDebouncingExtra;
unsigned int ZeroDebouncingExtra02;
// Variables for smoothing tachometer:
unsigned long readings[numReadings]; // The input.
unsigned long readings02[numReadings02]; // The input.
unsigned long readIndex; // The index of the current reading.
unsigned long readIndex02; // The index of the current reading.
unsigned long total; // The running total.
unsigned long total02; // The running total.
unsigned long average; // The RPM value after applying the smoothing.
unsigned long average02; // The RPM value after applying the smoothing.
// LEDs for Red and Green
#define RLED 9 // Red LED connected to 9
#define GLED 10 //Green LED connected to 10
void setup() // Start of setup:
{
Serial.begin(9600); // Begin serial communication.
attachInterrupt(digitalPinToInterrupt(2), Pulse_Event, RISING); // Enable interruption pin 2 when going from LOW to HIGH.
delay(1000);
Serial.begin(9600); // Begin serial communication.
attachInterrupt(digitalPinToInterrupt(3), Pulse_Event02, RISING); // Enable interruption pin 3 when going from LOW to HIGH.
delay(1000);
pinMode(RLED, OUTPUT);
pinMode(GLED, OUTPUT);
} // End of setup.
void loop() // Start of loop:
{
LastTimeCycleMeasure = LastTimeWeMeasured; // Store the LastTimeWeMeasured in a variable.
CurrentMicros = micros(); // Store the micros() in a variable.
if (CurrentMicros < LastTimeCycleMeasure) {
LastTimeCycleMeasure = CurrentMicros;
}
FrequencyRaw = 10000000000 / PeriodAverage; // Calculate the frequency using the period between pulses.
// Detect if pulses stopped or frequency is too low, so we can show 0 Frequency:
if (PeriodBetweenPulses > ZeroTimeout - ZeroDebouncingExtra || CurrentMicros - LastTimeCycleMeasure > ZeroTimeout - ZeroDebouncingExtra) { // If the pulses are too far apart that we reached the timeout for zero:
FrequencyRaw = 0; // Set frequency as 0.
ZeroDebouncingExtra = 2000; // Change the threshold a little so it doesn't bounce.
} else {
ZeroDebouncingExtra = 0; // Reset the threshold to the normal value so it doesn't bounce.
}
FrequencyReal = FrequencyRaw / 10000; // Get frequency without decimals.
// This is not used to calculate RPM but we remove the decimals just in case
// you want to print it.
// Calculate the RPM:
RPM = FrequencyRaw / PulsesPerRevolution * 60; // Frequency divided by amount of pulses per revolution multiply by
// 60 seconds to get minutes.
RPM = RPM / 10000; // Remove the decimals.
// Smoothing RPM:
total = total - readings[readIndex]; // Advance to the next position in the array.
readings[readIndex] = RPM; // Takes the value that we are going to smooth.
total = total + readings[readIndex]; // Add the reading to the total.
readIndex = readIndex + 1; // Advance to the next position in the array.
if (readIndex >= numReadings) // If we're at the end of the array:
{
readIndex = 0; // Reset array index.
}
// Calculate the average:
average = total / numReadings; // The average value it's the smoothed result.
//Serial.print("Period: ");
//Serial.print(PeriodBetweenPulses);
//Serial.print("\tReadings: ");
//Serial.print(AmountOfReadings);
//Serial.print("\tFrequency: ");
//Serial.print(FrequencyReal);
Serial.print("\tRPM: ");
Serial.print(RPM);
Serial.print("\tTachometer: ");
Serial.println(average);
LastTimeCycleMeasure02 = LastTimeWeMeasured02; // Store the LastTimeWeMeasured in a variable.
CurrentMicros02 = micros(); // Store the micros() in a variable.
if (CurrentMicros02 < LastTimeCycleMeasure02) {
LastTimeCycleMeasure02 = CurrentMicros02;
}
FrequencyRaw02 = 10000000000 / PeriodAverage02; // Calculate the frequency using the period between pulses.
// Detect if pulses stopped or frequency is too low, so we can show 0 Frequency:
if (PeriodBetweenPulses02 > ZeroTimeout02 - ZeroDebouncingExtra02 || CurrentMicros02 - LastTimeCycleMeasure02 > ZeroTimeout02 - ZeroDebouncingExtra02) { // If the pulses are too far apart that we reached the timeout for zero:
FrequencyRaw02 = 0; // Set frequency as 0.
ZeroDebouncingExtra02 = 2000; // Change the threshold a little so it doesn't bounce.
} else {
ZeroDebouncingExtra02 = 0; // Reset the threshold to the normal value so it doesn't bounce.
}
FrequencyReal02 = FrequencyRaw02 / 10000; // Get frequency without decimals.
// This is not used to calculate RPM but we remove the decimals just in case
// you want to print it.
// Calculate the RPM:
RPM02 = FrequencyRaw02 / PulsesPerRevolution02 * 60; // Frequency divided by amount of pulses per revolution multiply by
// 60 seconds to get minutes.
RPM02 = RPM02 / 10000; // Remove the decimals.
//Smoothing RPM: Something up with this area makes the first tachometer not reset back to zero
total02 = total02 - readings02[readIndex02]; // Advance to the next position in the array.
readings02[readIndex02] = RPM02; // Takes the value that we are going to smooth.
total02 = total02 + readings02[readIndex02]; // Add the reading to the total.
readIndex02 = readIndex02 + 1; // Advance to the next position in the array.
if (readIndex02 >= numReadings02) // If we're at the end of the array:
{
readIndex02 = 0; // Reset array index.
}
//Calculate the average:
average02 = total02 / numReadings02; // The average value it's the smoothed result.
//Serial.print("\tPeriod 2: ");
//Serial.print(PeriodBetweenPulses02);
//Serial.print("\tReadings 02: ");
//Serial.print(AmountOfReadings02);
//Serial.print("\tFrequency 02: ");
//Serial.print(FrequencyReal02);
Serial.print("\tRPM 02: ");
Serial.print(RPM02);
Serial.print("\tTachometer 02: ");
Serial.println(average02);
if (average == average02 || average == (average02 * 1.1) || average == (average02 - (average02 * 0.1))) {
digitalWrite(GLED, HIGH); // Turn the LED on
digitalWrite(RLED, LOW); // Turn the LED off
} else {
digitalWrite(RLED, HIGH); // Turn the LED on
digitalWrite(GLED, LOW); // Turn the LED off
}
} // End of loop.
void Pulse_Event() // The interrupt runs this to calculate the period between pulses:
{
PeriodBetweenPulses = micros() - LastTimeWeMeasured; // Current "micros" minus the old "micros" when the last pulse happens.
LastTimeWeMeasured = micros(); // Stores the current micros so the next time we have a pulse we would have something to compare with.
if (PulseCounter >= AmountOfReadings) // If counter for amount of readings reach the set limit:
{
PeriodAverage = PeriodSum / AmountOfReadings; // Calculate the final period dividing the sum of all readings by the
// amount of readings to get the average.
PulseCounter = 1; // Reset the counter to start over. The reset value is 1 because its the minimum setting allowed (1 reading).
PeriodSum = PeriodBetweenPulses; // Reset PeriodSum to start a new averaging operation.
int RemapedAmountOfReadings = map(PeriodBetweenPulses, 40000, 5000, 1, 10); // Remap the period range to the reading range.
RemapedAmountOfReadings = constrain(RemapedAmountOfReadings, 1, 10); // Constrain the value so it doesn't go below or above the limits.
AmountOfReadings = RemapedAmountOfReadings; // Set amount of readings as the remaped value.
} else {
PulseCounter++; // Increase the counter for amount of readings by 1.
PeriodSum = PeriodSum + PeriodBetweenPulses; // Add the periods so later we can average.
}
} // End of Pulse_Event.
//Second Tachometer
void Pulse_Event02() // The interrupt runs this to calculate the period between pulses:
{
PeriodBetweenPulses02 = micros() - LastTimeWeMeasured02; // Current "micros" minus the old "micros" when the last pulse happens.
LastTimeWeMeasured02 = micros(); // Stores the current micros so the next time we have a pulse we would have something to compare with.
if (PulseCounter02 >= AmountOfReadings02) // If counter for amount of readings reach the set limit:
{
PeriodAverage02 = PeriodSum02 / AmountOfReadings02; // Calculate the final period dividing the sum of all readings by the
// amount of readings to get the average.
PulseCounter02 = 1; // Reset the counter to start over. The reset value is 1 because its the minimum setting allowed (1 reading).
PeriodSum02 = PeriodBetweenPulses02; // Reset PeriodSum to start a new averaging operation.
int RemapedAmountOfReadings02 = map(PeriodBetweenPulses02, 40000, 5000, 1, 10); // Remap the period range to the reading range.
RemapedAmountOfReadings02 = constrain(RemapedAmountOfReadings02, 1, 10); // Constrain the value so it doesn't go below or above the limits.
AmountOfReadings02 = RemapedAmountOfReadings02; // Set amount of readings as the remaped value.
} else {
PulseCounter02++; // Increase the counter for amount of readings by 1.
PeriodSum02 = PeriodSum02 + PeriodBetweenPulses02; // Add the periods so later we can average.
}
}