Rpm readout with ir sensor

hi,
I am trying to use an intterupt function to get a rpm readout from my DC motor. The problem i am having is that it reactes up to 5 times sometimes per slot on the rotating disc. i don't know how i can prevent this from hapening.

int interrupt = 2;
bool teller;

volatile unsigned long current_millis;
volatile unsigned long old_millis;
volatile bool state;

unsigned long time1;

unsigned long rpm;
unsigned long frequentie;
float periode;

void setup() {
 
  pinMode(interrupt, INPUT);
  attachInterrupt(digitalPinToInterrupt(interrupt), toerental, RISING);
  Serial.begin(9600);
  
}

void loop() {
  
  current_millis1 = millis();
 
  if(state == 1 && time1 > 0){
    
    periode = (time1*2.0)/1000.0;
    frequentie = 1.0/periode;
    rpm = frequentie * 60ul;
    state = 0;
    
    Serial.println(periode);
    Serial.println(frequentie);
    Serial.println(rpm);
    
  }
      

}

void toerental(){

    if(teller == 1){
      
      current_millis = millis();
      time1 = current_millis - old_millis;
      teller = 0;
      state = 1;
      
    }
  
    if(teller == 0){
      
      old_millis = millis();
      teller = 1;
      
    }
   
  

    
    

  
  
}

What does that mean?

int interrupt = 2;
bool teller;

volatile unsigned long current_millis;
volatile unsigned long old_millis;
volatile bool state;
volatile bool ISRreceived = false;
unsigned long time1;

unsigned long rpm;
unsigned long frequentie;
float periode;

void setup() {
 
  pinMode(interrupt, INPUT);
  attachInterrupt(digitalPinToInterrupt(interrupt), toerental, RISING);
  Serial.begin(9600);
  
}

void loop() {
  
  current_millis1 = millis();

if ( ISRreceived )
{

    if(teller == 1){
      
      current_millis = millis();
      time1 = current_millis - old_millis;
      teller = 0;
      state = 1;
      
    }
  
    if(teller == 0){
      
      old_millis = millis();
      teller = 1;
      
    }
   
  

    
    

  
  
  ISRreceived=false;
}
 
  if(state == 1 && time1 > 0){
    
    periode = (time1*2.0)/1000.0;
    frequentie = 1.0/periode;
    rpm = frequentie * 60ul;
    state = 0;
    
    Serial.println(periode);
    Serial.println(frequentie);
    Serial.println(rpm);
    
  }
      

}

void toerental(){

if ( ISRreceived = false )
{
  ISRreceived = true;
}

}

minimize code execution in the ISR.

that when i set it to rising for example, it should only go in the function 1 time when the slot passes by. But it goes in this functies for 5-6 times. so the time is really short and i have no rpm readout.

i tested your code and it still enters the void 5-6 times in 1ms. is there a way to let the arduino ignore the other all the other times it enters the void except for the first?

Knipsel

debounce the IR receiver.
or

adding in a delay()

int interrupt = 2;
bool teller;

volatile unsigned long current_millis;
volatile unsigned long old_millis;
volatile bool state;
volatile bool ISRreceived = false;
unsigned long time1;

unsigned long rpm;
unsigned long frequentie;
float periode;

void setup() {
 
  pinMode(interrupt, INPUT);
  attachInterrupt(digitalPinToInterrupt(interrupt), toerental, RISING);
  Serial.begin(9600);
  
}

void loop() {
  
  current_millis1 = millis();

if ( ISRreceived )
{

    if(teller == 1){
      
      current_millis = millis();
      time1 = current_millis - old_millis;
      teller = 0;
      state = 1;
      
    }
  
    if(teller == 0){
      
      old_millis = millis();
      teller = 1;
      
    }
   
  

    
    

 delay(1000000000000000000000000000000000); 
  
  ISRreceived=false;
}
 
  if(state == 1 && time1 > 0){
    
    periode = (time1*2.0)/1000.0;
    frequentie = 1.0/periode;
    rpm = frequentie * 60ul;
    state = 0;
    
    Serial.println(periode);
    Serial.println(frequentie);
    Serial.println(rpm);
    
  }
      

}

void toerental(){

if ( ISRreceived = false )
{
  ISRreceived = true;
}

}

What is this talk of "entering the void"? It sounds drastic.

this is every time it enters the void teller.

Well if you think that entering the void sounds drastic, I voided this AM.

Well at least it's a function.

Some small? changes to the code in post #1 doing a self test of the pwm frequency on pin 3 ...

i tested your code. i still get 1 valid reading and 1 weird reading at once (see picture).

Knipsel

when i measure and calculate my rpm like this, i get around 2100 rpm.

void toerental(){
  
  current_millisT = millis();
  old_state = new_state;
  new_state = digitalRead(sensor);
  
  if(old_state != new_state && new_state == 0){
    
      state = HIGH; 
      teller++; 
      
      }
      
  if(state && teller == 1){ 
    
    old_millisP = millis();
    state = LOW; 
    
    }
    
  if(state && teller == 2){
    
    current_millisP = millis()- old_millisP;
    teller = 0;
    state = LOW;
    
    }
    
   if(current_millisT - old_millisT >= 500){
    
    periode = (current_millisP*2.0)/1000.0;
    frequentie = 1.0/periode;
    rpm = frequentie * 60ul;
    old_millisT = current_millisT;
    waarde = map(analogRead(pot), 0, 1023, 0, 100);
    
   }
}

when i use your method, i get around 5000rpm. this same result as i got when i measured the amount of times the disk made a full rotation in 1 second. i todd this wasn't correct so i dismissed it. is there an reason why the measurement above shows so different results?

For starts:

  • your project goal(s)
  • bounce or noise on signal
  • lack of diagram (schematic)
  • links and datasheets for components

its not hard.

i know that i have bounce on my signal, i just don't know how to fix this for an ir sensor. this is a school project so the components are given by them and i don't see any serial numbers on them. i don't have a schematic ready yet and i will provide them once there ready. the purpose of this is that a need a rpm measurement on startup. i need to type in a rpm that i want and then the DC motor needs to get to that rpm in exactly 10 seconds, regardless of the rpm i set it to. and while the motor is starting, i need to see the rpm i gave up and the rpm it is running at. and i have everything except for the rpm reading.

Regarding bounce, you could resolve this in hardware or software (or both).

  • Hardware would be adding a small RC filter, or possibly just capacitor
  • A possible software method would be to not do any calculations or anything until a certain amount of time has expired.
  • You should be able to find examples on this forum or web to review

i think i will need to do this in software for the teachers to be happy. so if a take my calculations out and wait for stable reading, my values that store in "old_millis" and "current_millis" won't be correct or can i make them that they only react to the last change?

How about reacting to the first change, then stop further calculations or anything until a certain amount of time has expired?

yeah that probably is better. how do i go about that? interrupt.end ?

The method would be similar to the bounce without delay example.
In this case, you won't be able to prevent the multiple interrupts from happening (during the noise or bounce), but you could respond to the first interrupt, then wait for a time period to expire before responding to the next valid interrupt. Need to monitor elapsed time (micros()) from the first interrupt.

Here's an example that would need modification. Could give some ideas for your project, as it's similar. Good luck!

const byte   wings = 4; // no of wings of rotating object, for disc object use 1 with white tape on one side
volatile unsigned long us, prevPulseUs, pulseUs, prevPulseUsCopy, pulseUsCopy;
unsigned long pulsePeriod, rpm;

void isr() { //interrupt service routine
  us = micros();
  if ((us - pulseUs) > 2000) { //limited to 500Hz max
    prevPulseUs = pulseUs;
    pulseUs = us;
  }
}

bool timeOut(unsigned long ms) { //ms timer
  volatile unsigned long prevMs, now;
  now = millis();
  if ((now - prevMs) >= ms) {
    prevMs = now;
    return true;
  }
  return false;
}

void setup() {
  pinMode(3, OUTPUT);  // pwm test: disconnect sensor, then add jumper from pin 2 to 3
  analogWrite(3, 127); // pwm @ 490 Hz = 29400 rpm / wings
  attachInterrupt(digitalPinToInterrupt(2), isr, RISING); //attaching the interrupt
}

void loop() {

  if (timeOut(1000)) {
    cli(); //stop interrupts
    prevPulseUsCopy = prevPulseUs;
    pulseUsCopy = pulseUs;
    sei(); //allow interrupts
    pulsePeriod = pulseUsCopy - prevPulseUsCopy;
    if (!pulseUsCopy || !prevPulseUsCopy) pulsePeriod = 10000000; // startup 0 rpm
    rpm = 1000000 / pulsePeriod * 60 / wings; //calculates rpm
    // print results
  }
}