Measure speed of brake disc with optical sensor

Hello,
For a school project I need to measure the speed of a rotating mass to brake it afterwards.
I wanted to measure the speed of the rotation using the bike brake disc (16holes) I mounted on it. To do so, I mounted an optical sensor (Sharp GP1A57HRJ00F).
I am also using a clock through a generator to calculate every 100ms (so using a 10Hz square 0-+5V) the speed of the mass (That's what my teacher told me to do).
Here's the code :


#define HT 3
#define SO 2

float speed_ ;

long T;
unsigned long T1;
unsigned long T2;
unsigned long dT_op;

void setup() {
  Serial.begin(9600);
  Serial.println(" initialisation ");
  
  pinMode(HT, INPUT);
  attachInterrupt(1, Clock, RISING);
  pinMode(SO, INPUT);
  attachInterrupt(0, readSO, RISING);
}

void  Clock(){
  dT_op = T2-T1;
  speed_ = (1e6) / (16*dT_op);  
}

void readSO(){
  noInterrupts();
  T1=T2;
  T2=micros();
  interrupts();
}

void loop() {
  Serial.print(" speed ");
  Serial.print(speed_);
  Serial.print ("  T1 ");
  Serial.print(T1);
  Serial.print("  T2 ");
  Serial.print(T2);
  Serial.print("  dt ");
  Serial.println(dT_op);
  delay(500);
}

The issue I'm having is that sometimes the speed jumps up for no reason (and so the time differential plummets) and I can't see why...
I verified that the other smaller holes didn't interfere with the sole hole I'm measuring from.
It doesn't seem to be correlated to the speed it's rotating (between 0 and 50 rounds per seconds).

Here's a photo of the project :

It's my first time on the forum so I might be missing some crucial information for you to help me.
Sorry for the possible english mistakes, it is not my first languages.

In hopes that you can help me !

readS0 does not need to disable interrupts - in an interrupt, they're already disabled

You shouldn't really be doing so much arithmetic in an interrupt, just recording variables.

Anything more than a single byte variable needs to be accessed (copied) with interrupts disabled in the loop function.

Shared variables should be qualified as "volatile".

Changing them to volatile seems to have helped (not as much weird values) !!
Some still remain so I'll see to limit the use of arithmetic in an interrupt..

image

Thanks a lot for your speed and help !

What about copying them with interrupts disabled?

In the loop function i suppose?
But then the clock function is useless if i only copy them every 500 delay.

I'll try it out when I can !
Thanks

Please post schematics showing how the opto is connected. I see possible mistakes there.

Are you saying you're only sensing one single hole on the entire periphery of the disc?  It appears there would be possibly a dozen other holes that could also be sensed.

No no as you can see from the first picture I sent there are blocs of 3 holes at different radius from the center / height. What I wanted to say is that I only took the one that was the furthest from the center (and all along the disc there are 16 in total, thus the division by 16 in the code)

Sorry for the possible misunderstanding

I will when I get back access to it but tested it on an oscilloscope and worked fine so I doubt the mistakes come from it

Any pictures from the oscilloscope?
My reason for asking is You define the input as INPUT, not INPUT_PULLUP. Most opto outputs are open collector and then a pullup resistor is needed.

From the datasheet: To prevent photointerrupter from faulty operation caused by external light, do not set the detecting face to the external light.

Possibly, some light shielding would help.

Thanks. That is an opto model DeLuxe having an internall pullup.
No problem reading that like You do.
I've used a lot more simple once in the past.
Be aware of glowing bulbs. They radiate some IR that can interfere. Sunshine is obvious....

You have a good signal from the opto. No problem there.

You really need to pay attention to this advice.

Your print outs indicate a potential issue with this, as the T1 and T2 values give the correct dt value when subtracted with a calculator even though the printed value of dt is wrong.

It seems to work !!

Here's the new program :


#define HT 3
#define SO 2

float speed_ ;

volatile long T1;
volatile long T2;
unsigned long T2_c;
unsigned long T1_c;
unsigned long dT_op;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println (" ");
  Serial.println(" démarrage ");
  
  pinMode(HT, INPUT);
  //attachInterrupt(1, Clock, RISING);
  pinMode(SO, INPUT);
  attachInterrupt(0, readSO, RISING);
}


//void  Clock(){
//  dT_op = T2-T1;
//  speed_ = (1e6) / (16*dT_op);
//}

void readSO(){
  T1=T2;
  T2=micros();

}

void loop() {
  // put your main code here, to run repeatedly:
  noInterrupts();
  T2_c = T2;
  T1_c = T1;
  interrupts();
  dT_op = T2_c-T1_c;
  speed_ = (1e6)/ (16*dT_op);   //A rentrer dans le noInterrupts?
  
  Serial.print(speed_);
  Serial.print ("  T1 ");
  Serial.print(T1);
  Serial.print(" T1_c ");
  Serial.print(T1_c);
  Serial.print("  T2 ");
  Serial.print(T2);
  Serial.print(" T2_c ");
  Serial.print(T2_c);
  Serial.print("  dt ");
  Serial.println(dT_op);
  delay(300);
}

If you have any optmisation ideas please tell me but if not thanks a lot for all of your help!

//volatile long T1;
volatile unsigned long T1;
//volatile long T2;
volatile unsigned long T2;

The volatile variables picking values from micros() should be typed as unsigned long.

What is the maximum RPM you want to read?

Maximum 60 RPS so 3600 RPM (tested this afternoon and worked)