Trying get RPM Value with IR LED

Hi,

Im trying to mesure rpm of an motor with arduino and one QRD1114 IR Led.

the QRD1114 connection from Bildr, i use 220ohm and 4.7Kohms:

To making test im using one motor with ardumoto with same speed at 100 using analogWrite. I put one blank mark to make the “pulse”.

the first sketch i upload is to count the number of turns during 60s:

#define PwmPinMotorB 11
#define DirectionPinMotorB 13

unsigned long time1=0;
unsigned long time2=0;
int motorOn=0;
int count=0;


void setup(){

 Serial.begin(9600);
   pinMode(PwmPinMotorB, OUTPUT);
     pinMode(DirectionPinMotorB, OUTPUT);
digitalWrite(DirectionPinMotorB, LOW);
 
}


void loop(){
int valorQRD=analogRead(A0);
Serial.println(valorQRD);
if (valorQRD<100){
  count++;
}

if (motorOn==0){
time1=millis();
analogWrite(PwmPinMotorB, 100);
motorOn=1;
}


  
time2=millis();

if (time2>=time1+60000){
analogWrite(PwmPinMotorB, 0);
Serial.print(count);
Serial.println(" RPM");
motorOn=0;
count=0;
delay(5000);
}

 

  
}

he give me 550 turns during each 60s for pwm 100.

Now i know the rpm for this motor with 100 pwm.

I want to mesure the actual rpm, for this i use the code from Arduino Playground:
http://arduino.cc/playground/Learning/Tachometer

Just little modifications to add the ardumoto:

int val;
long last=0;
int stat=LOW;
int stat2;
int contar=0;
#define PwmPinMotorB 11
#define DirectionPinMotorB 13
int sens=100;  // this value indicates the limit reading between dark and light,
              // it has to be tested as it may change acording on the 
              // distance the leds are placed.
int nPalas=1; // the number of blades of the propeller

int milisegundos=250; // the time it takes each reading
void setup()
{
  pinMode(PwmPinMotorB, OUTPUT);
pinMode(DirectionPinMotorB, OUTPUT);
digitalWrite(DirectionPinMotorB, LOW);
analogWrite(PwmPinMotorB, 100);
  Serial.begin(9600);

}

void loop()
{
  val=analogRead(0);
  if(val<sens)
    stat=LOW;
   else
    stat=HIGH;

                          //indicate the state of the circuit.

   if(stat2!=stat){  //counts when the state change, thats from (dark to light) or 
                     //from (light to dark), remmember that IR light is invisible for us.
     contar++;
     stat2=stat;
   }
   if(millis()-last>=milisegundos){
     double rps=((double)contar/nPalas)/2.0*1000.0/milisegundos;
     double rpm=((double)contar/nPalas)/2.0*60000.0/(milisegundos);
     Serial.print((contar/2.0));Serial.print("  RPS ");Serial.print(rps);
     Serial.print(" RPM");Serial.print(rpm);Serial.print("  VAL ");Serial.println(val);
     contar=0;
     last=millis();
   }
}

but it´s not the results i expect:

2.50  RPS 10.00 RPM600.00  VAL 766
2.00  RPS 8.00 RPM480.00  VAL 805
2.00  RPS 8.00 RPM480.00  VAL 749
2.00  RPS 8.00 RPM480.00  VAL 827
2.50  RPS 10.00 RPM600.00  VAL 40
2.50  RPS 10.00 RPM600.00  VAL 771
2.00  RPS 8.00 RPM480.00  VAL 729
2.00  RPS 8.00 RPM480.00  VAL 807
3.00  RPS 12.00 RPM720.00  VAL 867
2.00  RPS 8.00 RPM480.00  VAL 925
2.00  RPS 8.00 RPM480.00  VAL 765
2.00  RPS 8.00 RPM480.00  VAL 782
3.00  RPS 12.00 RPM720.00  VAL 832
2.00  RPS 8.00 RPM480.00  VAL 924
2.00  RPS 8.00 RPM480.00  VAL 764
2.00  RPS 8.00 RPM480.00  VAL 809
3.00  RPS 12.00 RPM720.00  VAL 799
2.00  RPS 8.00 RPM480.00  VAL 753
2.00  RPS 8.00 RPM480.00  VAL 826
3.00  RPS 12.00 RPM720.00  VAL 855
2.00  RPS 8.00 RPM480.00  VAL 901
2.00  RPS 8.00 RPM480.00  VAL 761
2.00  RPS 8.00 RPM480.00  VAL 810
3.00  RPS 12.00 RPM720.00  VAL 850
2.00  RPS 8.00 RPM480.00  VAL 922
2.00  RPS 8.00 RPM480.00  VAL 751
2.00  RPS 8.00 RPM480.00  VAL 774
3.00  RPS 12.00 RPM720.00  VAL 901
2.00  RPS 8.00 RPM480.00  VAL 794
2.00  RPS 8.00 RPM480.00  VAL 882
3.00  RPS 12.00 RPM720.00  VAL 784
2.00  RPS 8.00 RPM480.00  VAL 745
2.00  RPS 8.00 RPM480.00  VAL 968
2.00  RPS 8.00 RPM480.00  VAL 743
3.00  RPS 12.00 RPM720.00  VAL 840
2.00  RPS 8.00 RPM480.00  VAL 766
2.00  RPS 8.00 RPM480.00  VAL 822
3.00  RPS 12.00 RPM720.00  VAL 758
2.00  RPS 8.00 RPM480.00  VAL 882
2.00  RPS 8.00 RPM480.00  VAL 788
2.50  RPS 10.00 RPM600.00  VAL 50
2.50  RPS 10.00 RPM600.00  VAL 717
2.00  RPS 8.00 RPM480.00  VAL 784
2.00  RPS 8.00 RPM480.00  VAL 854
3.00  RPS 12.00 RPM720.00  VAL 763
2.00  RPS 8.00 RPM480.00  VAL 857
2.00  RPS 8.00 RPM480.00  VAL 904
2.00  RPS 8.00 RPM480.00  VAL 783
3.00  RPS 12.00 RPM720.00  VAL 806
2.00  RPS 8.00 RPM480.00  VAL 779
2.00  RPS 8.00 RPM480.00  VAL 801
3.00  RPS 12.00 RPM720.00  VAL 834
2.00  RPS 8.00 RPM480.00  VAL 767
2.00  RPS 8.00 RPM480.00  VAL 863
2.50  RPS 10.00 RPM600.00  VAL 44
2.50  RPS 10.00 RPM600.00  VAL 885
2.00  RPS 8.00 RPM480.00  VAL 761
2.00  RPS 8.00 RPM480.00  VAL 943
3.00  RPS 12.00 RPM720.00  VAL 857
2.00  RPS 8.00 RPM480.00  VAL 761
2.00  RPS 8.00 RPM480.00  VAL 895
3.00  RPS 12.00 RPM720.00  VAL 827
2.00  RPS 8.00 RPM480.00  VAL 749
2.00  RPS 8.00 RPM480.00  VAL 852
3.00  RPS 12.00 RPM720.00  VAL 400
2.00  RPS 8.00 RPM480.00  VAL 844
2.00  RPS 8.00 RPM480.00  VAL 916
2.00  RPS 8.00 RPM480.00  VAL 766
3.00  RPS 12.00 RPM720.00  VAL 922
2.00  RPS 8.00 RPM480.00  VAL 776
2.00  RPS 8.00 RPM480.00  VAL 941
3.00  RPS 12.00 RPM720.00  VAL 754
2.00  RPS 8.00 RPM480.00  VAL 741
2.00  RPS 8.00 RPM480.00  VAL 958
2.00  RPS 8.00 RPM480.00  VAL 751
3.00  RPS 12.00 RPM720.00  VAL 776

i have constant speed but rpm change a lot, i know the speed is +/-550rpm by results in my first test but from 480 to 720 it´s a huge difference!

Every time val<100 i have a good value=600 (+/- 550), i know is when the blank mark pass by the IR Led.

Any ideas?

Because the sample time is so small, you are only getting around 2.5 revs during the sample time. The sample interval might start just before the IR beam has been interrupted, or just after, or while it is being interrupted. Combined with a little variation in motor speed, this gives you either 4, 5 or 6 edges per sample interval, leading to the results you are getting.

A better way is to time the interval between one rising edge and the next, or the interval between one falling edge and the next. This is typically done by having the rising edge generate an interrupt. In the interrupt service routine, call millis() or micros() to get the time, and subtract the time recorded at the previous interrupt.

Hi,

Thanks for your reply, i have write a new sketch based on your advice:

#define PwmPinMotorB 11
#define DirectionPinMotorB 13

unsigned long time[1];
unsigned long timeBetweenRising=0;
int index=0;
int count=0;
int Led=0;



//---------------------------------------------------------
void setup(){

  Serial.begin(9600);

  pinMode(PwmPinMotorB, OUTPUT);
  pinMode(DirectionPinMotorB, OUTPUT);
  digitalWrite(DirectionPinMotorB, LOW);
  analogWrite(PwmPinMotorB, 35);

  attachInterrupt(0, sample, RISING);

  pinMode(6,OUTPUT);

}

//---------------------------------------------------------
void loop(){



  Serial.println(timeBetweenRising);
  double rpm=2.0*60000.0/(timeBetweenRising);
  Serial.println(rpm);

  delay(500);


}

//---------------------------------------------------------
void sample(){



    if (Led==0){
      digitalWrite(6,HIGH);
      Led=1;
    }
    else
    {
      digitalWrite(6,LOW);
      Led=0;
    }

    if (count==1){
      index=1;
      time[index]=millis();
      timeBetweenRising=time[1]-time[0];  
      time[0]=time[1];
    } 

    if (count==0){
      time[index]=millis();
      count++;
    } 
  

}

I have put one LED to on/off each time the interrupt is On at low speed, i can see the led blink when the QRD pass on blank mark but i think is too sensible because the LED blink inconstant out of blank mark.

i think is because the resistance i use, or can i use on interrupt based in analog Value from QRD?

Edit: If i change the resistor 4.7K for 2.2k i have better results but it´s not enough, in 10 samples i have 2 false:(

if i use the 4.7Kohm in 10 samples i have 5/6 false...

I think have found the solution:
I use the 4.7Kohm resistor and 220ohm, is the best value i have found…
And i make on shunt between pin 2 and A0 to can read the analog value at the same time i use interupt, and i say if i found one interupt, check also the anolog value before count the sample:

#define PwmPinMotorB 11
#define DirectionPinMotorB 13

unsigned long time[1];
unsigned long timeBetweenRising=0;
int index=0;
int count=0;
int Led=0;
int lastrpm=0;


//---------------------------------------------------------
void setup(){

  Serial.begin(9600);

  pinMode(PwmPinMotorB, OUTPUT);
  pinMode(DirectionPinMotorB, OUTPUT);
  digitalWrite(DirectionPinMotorB, LOW);
  analogWrite(PwmPinMotorB, 255);

  attachInterrupt(0, sample, RISING);

  pinMode(6,OUTPUT);

}

//---------------------------------------------------------
void loop(){




  int rpm=2*60000/(timeBetweenRising);

//If i have a crazy value, replace by last good rpm
if (rpm<0) rpm=lastrpm;
  lastrpm=rpm;
Serial.println(rpm);

delay(500);



}

//---------------------------------------------------------
void sample(){

int valueQDR=analogRead(A0);
if (valueQDR<=600){
    if (Led==0){
      digitalWrite(6,HIGH);
      Led=1;
    }
    else
    {
      digitalWrite(6,LOW);
      Led=0;
    }

    if (count==1){
      index=1;
      time[index]=millis();
      timeBetweenRising=time[1]-time[0];  
      time[0]=time[1];
    } 

    if (count==0){
      time[index]=millis();
      count++;
    } 
}

}

now i have good values for high and low speed:

0
0
1518
1518
1666
1690
1690
1666
1666
1714
1666
1690
1690
1714
1739
1690
1690
1714
1690
1690
1714
1714
1714
1714
1690

Please comment.

cmcta82: I think have found the solution: I use the 4.7Kohm resistor and 220ohm, is the best value i have found... And i make on shunt between pin 2 and A0 to can read the analog value at the same time i use interupt, and i say if i found one interupt, check also the anolog value before count the sample:

That should not be necessary. I suspect that the wires from the IR sensor are picking up noise from the motor drive wires. Keep them well away from the motor drive wires, and consider using shielded cable. As a last resort, connect a 1nF or 10nF capacitor between the input pin and ground.

Your code is not correct as it stands, "unsigned long time[1]" should be "unsigned long time[2]". However, it can be simplified. This version assumes you have taken care of the noise, so it doesn't do the analogRead:

#define PwmPinMotorB 11
#define DirectionPinMotorB 13

volatile unsigned long prevTime = 0;
volatile unsigned int timeBetweenRising=0;
int Led=0;

//---------------------------------------------------------
void setup(){
  Serial.begin(9600);

  pinMode(PwmPinMotorB, OUTPUT);
  pinMode(DirectionPinMotorB, OUTPUT);
  digitalWrite(DirectionPinMotorB, LOW);
  analogWrite(PwmPinMotorB, 255);
  attachInterrupt(0, sample, RISING);
  pinMode(6,OUTPUT);
}

//---------------------------------------------------------
void loop(){
  disable();
  unsigned int copyTime = timeBetweenRising;
  enable();
  int rpm = (copyTime != 0) ? 60000/copyTime : 0;
  Serial.println(rpm);
  delay(500);
}

//---------------------------------------------------------
void sample(){
  unsigned long now = millis();
  if (prevTime != 0)
  {
    timeBetweenRising = (unsigned int)(now - prevTime);
    if (Led==0){
      digitalWrite(6,HIGH);
      Led=1;
    }
    else
    {
      digitalWrite(6,LOW);
      Led=0;
    }
  }
  prevTime = now;
}
[/quote]

Sorry but what is the functions enable() and disable()? you call the function but don´t appear on your code

Sorry, disable() should be noInterrupts(), and enable() should be interrupts().

ok is good but without Cap, with the cap in High speed the QDR don´t detect the blank mark, i don´t know why.

Just a little question, im learning C with Arduino and don´t understand the syntax of this line:

  int rpm = (copyTime != 0) ? 60000/copyTime : 0;

is an equal with conditions?for what is the "?"

The cap will cause the circuit to ignore short pulses from the IR sensor. How short depends on the product of the values of the capacitor and the associated pullup resistor. A capacitance of 10nF in conjunction with a pullup resistor of 4k7 would be OK down to pulses as short as 47us or so. Maybe you misread 10nF (= 0.01uF) in my post as 10uF?

The ''?' and ':' operators in C/C++ form a conditional expression, so "(copyTime != 0) ? 60000/copyTime : 0" means "if copyTime not-equal-to 0 then 60000/copyTime else 0". I added this to avoid a division by zero when the system starts up.