IR Break Beam too Slow?

Hello,

First of all, I'm new to Arduino and the forum so apologies if I do something wrong in this post!

I am currently working on a project that is essentially a ballistic chronograph for measuring the speed of a projectile by recording the time between when IR break beam sensors are obstructed. However, unlike a traditional chronograph I have 8 sensor pairs that are tripped in order so that I can observe some non linear behavior. This code is also used to control the switching of a solenoid valve through a relay, but so far I have had zero problems with this part of the code.

At the start of this project I knew very little about electronics/soldering and decided to use what looked simple: Adafruit 3mm IR break beam sensor receiver pairs:

IR Sensors

I have been testing these sensors for awhile and have managed to get them working at low speeds (breaking one with a pen, dropping objects past them). Recently I have been trying to test them with much higher speed projectiles (300 ft/s >) and they never seem to trigger / record timestamps at these speeds. I have been doing some research on the forums and looking at other people's chronograph projects and feel like I'm in a little over my head. Right now I am unsure if this is a hardware or code problem and could use a little help/advice before I go chasing my tail around.

Here are all the specs for what I have built:

control board: ELEGOO Mega ATmega2560

Sensor spacing: 8 break beam pairs spaced out evenly 9 inches apart over a six foot span.

Space between sensor/transmitter: .5 inches apart, it is also worth noting that the sensors are almost entirely obscured from ambient light.

Projectile data: projectile is .5 inches long and estimated to be moving around 300-400 ft/s (just some napkin math, the sensors were supposed to tell me what the actual velocity was)

Here is the first version of the control code that I tried:

//single shot control code

//this code fires the gas gun, and clocks all of the IR break sensors, then prints times to the monitor
//THIS CODE WILL NOT WORK IF MULTIPLE TANKS ARE BEING FIRED

//Global variables

// initialize signal pins to the relay board 
int Relay1 = 2;
//int Relay2 = 3;
//int Relay3 = 4;
//int Relay4 = 5;

//initialize trigger button
int Trigger = 10;


//a series of counter variables are needed to clock when sensors are broken 
unsigned long startTime;
unsigned long endTime1;
unsigned long endTime2;
unsigned long endTime3;
unsigned long endTime4;
unsigned long endTime5;
unsigned long endTime6;
unsigned long endTime7;

//a series of variables are needed to calculate the duration of time between the start time and the time at which a sensor is broken

unsigned long Duration1;
unsigned long Duration2;
unsigned long Duration3;
unsigned long Duration4;
unsigned long Duration5;
unsigned long Duration6;
unsigned long Duration7;

//define all our sensor pinouts, these connect to the IR breakbeams 
#define sensorpin1 24
#define sensorpin2 22
#define sensorpin3 23
#define sensorpin4 25
#define sensorpin5 26
#define sensorpin6 27
#define sensorpin7 28

//create a variable to read the state of the trigger, value will change
int triggerpress = 0;

// these variables are for reading if IR breakbeams are broken or not
int sensorstate1 = 0, lastState1=0; 
int sensorstate2 = 0, lastState2=0;
int sensorstate3 = 0, lastState3=0;
int sensorstate4 = 0, lastState4=0;
int sensorstate5 = 0, lastState5=0;
int sensorstate6 = 0, lastState6=0;
int sensorstate7 = 0, lastState7=0;

//a variable to track if the timmer is running is needed as well, this will prevent the timer starting if a sensor is accidentlay obstructed
byte timerRunning;

void setup() {
  // put your setup code here, to run once:

//initialize sensor pins as inputs
pinMode(sensorpin1, INPUT);
pinMode(sensorpin2, INPUT);
pinMode(sensorpin3, INPUT);
pinMode(sensorpin4, INPUT);
pinMode(sensorpin5, INPUT);
pinMode(sensorpin6, INPUT);
pinMode(sensorpin7, INPUT);

//pins should be initilized in the HIGH state 
digitalWrite(sensorpin1, HIGH);
digitalWrite(sensorpin2, HIGH);
digitalWrite(sensorpin3, HIGH);
digitalWrite(sensorpin4, HIGH);
digitalWrite(sensorpin5, HIGH);
digitalWrite(sensorpin6, HIGH);
digitalWrite(sensorpin7, HIGH);

// setup relay pins as outputs
pinMode(Relay1,OUTPUT);

//set trigger pin as input 
pinMode(Trigger,INPUT);

//we need to pull our relay pin high to start, when pin is pulled low the relay will trigger
digitalWrite(Relay1,HIGH);

//Considering our button is only a two wire button, we want to set our pin to HIGH so when button is pressed voltage will flow to ground 
digitalWrite(Trigger,HIGH);
Serial.begin(9600);

}

void loop() {
  // put your main code here, to run repeatedly:

  //first we need to read the state of the pushbutton value
  triggerpress = digitalRead(Trigger);

 //we also need to read all the sensors to see if any are broken with digitalread 
  sensorstate1 = digitalRead(sensorpin1);
  sensorstate2 = digitalRead(sensorpin2);
  sensorstate3 = digitalRead(sensorpin3);
  sensorstate4 = digitalRead(sensorpin4);
  sensorstate5 = digitalRead(sensorpin5);
  sensorstate6 = digitalRead(sensorpin6);
  sensorstate7 = digitalRead(sensorpin7);

 //first loop controls the firing of the tank and the starting of the timer 
 
 if (timerRunning == 0 && triggerpress == LOW) {   
    startTime = micros();
    digitalWrite(Relay1,LOW);
    timerRunning = 1;
 }
 
 //these next loops will control the clocking of each IR breaksensor 

 if (timerRunning == 1 && sensorstate1 == LOW){
   endTime1 = micros();
 }

  if (timerRunning == 1 && sensorstate2 == LOW){
   endTime2 = micros();
 }

  if (timerRunning == 1 && sensorstate3 == LOW){
   endTime3 = micros();
 }

  if (timerRunning == 1 && sensorstate4 == LOW){
   endTime4 = micros();
 }

  if (timerRunning == 1 && sensorstate5 == LOW){
   endTime5 = micros();
 }

 if (timerRunning == 1 && sensorstate6 == LOW){
   endTime6 = micros();
 }

 if (timerRunning == 1 && sensorstate7 == LOW){
   endTime7 = micros();
   digitalWrite(Relay1,HIGH);
 }
 
 //Finnaly our durations must be calculated and displayed to the serial monitor
 
 if(timerRunning == 1 && endTime7 > 0){
    timerRunning = 0;

    Duration1 = endTime1 - startTime;
    Duration2 = endTime2 - startTime;
    Duration3 = endTime3 - startTime;
    Duration4 = endTime4 - startTime;
    Duration5 = endTime5 - startTime;
    Duration6 = endTime6 - startTime;
    Duration7 = endTime7 - startTime;

    Serial.print("Sensor 1 trip in microseconds:");
    Serial.println(Duration1);

    Serial.print("Sensor 2 trip in microseconds:");
    Serial.println(Duration2);

    Serial.print("Sensor 3 trip in microseconds:");
    Serial.println(Duration3);

    Serial.print("Sensor 4 trip in microseconds:");
    Serial.println(Duration4);

    Serial.print("Sensor 5 trip in microseconds:");
    Serial.println(Duration5);

    Serial.print("Sensor 6 trip in microseconds:");
    Serial.println(Duration6);

    Serial.print("Sensor 7 trip in microseconds:");
    Serial.println(Duration7);

  }

}

When I run this code I can open the valve and fire the system, but nothing prints to the monitor and the relay switch never gets turned off. I tried a second version of the code that is similar, but made a bit more simple to see if the code was taking too long to run:

//single shot control code

//this code fires the gas gun, and clocks all of the IR break sensors, then prints times to the monitor
//THIS CODE WILL NOT WORK IF MULTIPLE TANKS ARE BEING FIRED

//Global variables

// initialize signal pins to the relay board 
int Relay1 = 2;
//int Relay2 = 3;
//int Relay3 = 4;
//int Relay4 = 5;

//initialize trigger button
int Trigger = 10;


//a series of counter variables are needed to clock when sensors are broken 
unsigned long startTime;
unsigned long endTime1;
unsigned long endTime2;
unsigned long endTime3;
unsigned long endTime4;
unsigned long endTime5;
unsigned long endTime6;
unsigned long endTime7;

//a series of variables are needed to calculate the duration of time between the start time and the time at which a sensor is broken


//define all our sensor pinouts, these connect to the IR breakbeams 
#define sensorpin1 24
#define sensorpin2 22
#define sensorpin3 23
#define sensorpin4 25
#define sensorpin5 26
#define sensorpin6 27
#define sensorpin7 28

//create a variable to read the state of the trigger, value will change
int triggerpress = 0;


//a variable to track if the timer is running is needed as well, this will prevent the timer starting if a sensor is accidently obstructed
byte timerRunning;

void setup() {
  // put your setup code here, to run once:

//initialize sensor pins as inputs
pinMode(sensorpin1, INPUT);
pinMode(sensorpin2, INPUT);
pinMode(sensorpin3, INPUT);
pinMode(sensorpin4, INPUT);
pinMode(sensorpin5, INPUT);
pinMode(sensorpin6, INPUT);
pinMode(sensorpin7, INPUT);

//pins should be initilized in the HIGH state 
digitalWrite(sensorpin1, HIGH);
digitalWrite(sensorpin2, HIGH);
digitalWrite(sensorpin3, HIGH);
digitalWrite(sensorpin4, HIGH);
digitalWrite(sensorpin5, HIGH);
digitalWrite(sensorpin6, HIGH);
digitalWrite(sensorpin7, HIGH);

// setup relay pins as outputs
pinMode(Relay1,OUTPUT);

//set trigger pin as input 
pinMode(Trigger,INPUT);

//we need to pull our relay pin high to start, when pin is pulled low the relay will trigger
digitalWrite(Relay1,HIGH);

//Considering our button is only a two wire button, we want to set our pin to HIGH so when button is pressed voltage will flow to ground 
digitalWrite(Trigger,HIGH);
Serial.begin(9600);

}

void loop() {
  // put your main code here, to run repeatedly:

  //first we need to read the state of the pushbutton value
  triggerpress = digitalRead(Trigger);


 //first loop controls the firing of the tank and the starting of the timer 
 
 if (timerRunning == 0 && triggerpress == LOW) {   
    startTime = micros();
    digitalWrite(Relay1,LOW);
    timerRunning = 1;
 }
 
 //these next loops will control the clocking of each IR breaksensor 
 while(timerRunning == 1){
// if ( digitalRead(sensorpin1) == LOW){
//   endTime1 = micros();
// }
//
//  if ( digitalRead(sensorpin2) == LOW){
//   endTime2 = micros();
// }
//
//  if ( digitalRead(sensorpin3) == LOW){
//   endTime3 = micros();
// }
//
//  if (digitalRead(sensorpin4) == LOW){
//   endTime4 = micros();
// }
//
//  if (digitalRead(sensorpin5) == LOW){
//   endTime5 = micros();
// }
//
// if (digitalRead(sensorpin6) == LOW){
//   endTime6 = micros();
// }

 if (digitalRead(sensorpin7) == LOW){
   endTime7 = micros();
   timerRunning = 0;
   digitalWrite(Relay1,HIGH);
 }
 }
 //Finnaly our durations must be calculated and displayed to the serial monitor
 
 if(timerRunning == 0 && endTime7 > 0){

    Serial.print("Shot Start:");
    Serial.println(startTime);

//    Serial.print("Sensor 1 trip in microseconds:");
//    Serial.println(endTime1);
//
//    Serial.print("Sensor 2 trip in microseconds:");
//    Serial.println(endTime2);
//
//    Serial.print("Sensor 3 trip in microseconds:");
//    Serial.println(endTime3);
//
//    Serial.print("Sensor 4 trip in microseconds:");
//    Serial.println(endTime4);
//
//    Serial.print("Sensor 5 trip in microseconds:");
//    Serial.println(endTime5);
//
//    Serial.print("Sensor 6 trip in microseconds:");
//    Serial.println(endTime6);

    Serial.print("Sensor 7 trip in microseconds:");
    Serial.println(endTime7);

    //clear all time storage variables 

    startTime = 0;
    endTime1 = 0;
    endTime2 = 0;
    endTime3 = 0;
    endTime4 = 0;
    endTime5 = 0;
    endTime6 = 0;
    endTime7 = 0;    

  }

}

*I commented out the other sensors here just to see if trying to read all eight sensors was too slow, but this didn't fix the problem.

Both codes produce the same results: If I trip sensor 7 with a slow moving object, the relay is switched off and micros() is printed to the serial monitor. I can then shoot a projectile through and nothing is displayed and the relay is never switched off. This seems to me like the event is happening too fast for the sensors to catch.

I am wondering if this is a hardware limitation or just bad coding on my part? The sensors I am using list a response time of >2ms but the data sheet is very sparse so I'm not really sure what kind of sensor is actually being used. When looking at other peoples projects I have seen the OPL550 IR photo transistors used which have a pullup time listed in nanoseconds.

So are there some tricks I can do to make my code run faster(interrupts, direct port manipulation)? Or do I need to tear up my wiring harness and replace my IR receivers with something faster? Any help is appreciated and apologies again if there is not enough info here! I am happy to elaborate further as needed

You sholud be expecting a rather quick pulse...

If your .5 length object is moving at 300ft/S then that's 3600 inches/S. That will be 1/3600 or it will cover an inch in 0.00027S so your pulse would be 1/2 that for a pulse duration of 0.000189S

This is substantially faster than ">2mS" specifications

An interrupt driven approach might be a better idea... at least look into it..

9" apart means that will trigger sequentially at about 9 * 0.00027s between them... or 0.0025

I'm sure there are optics that work this quickly, my Oehler used them... wonder why yours don't respond quicker...

Do you have a scope to put on them?


There are a number of these on the market now that use the small radar to get a speed on the projectile...


You have all the sensors defined as inputs.

Write to them like they were outputs, to write to them for a high state even though they are inputs?

Some processors will allow this but it enables the pull-up resistors on that input.


It might be easier to code an array of them

byte sensory_array[] = {
                          22, 23, 24, 25, 26, 27, 28
                       };

This will set up all of them as outputs...

for (x = 0; x < sizeof(sensory_array); ++x)
             pinMode(sensor_array[x], INPUT);

Good luck

:smiley_cat:

1 Like

First of all, the sensors must obviously be fast enough to catch the projectile. You could rig up a test using a little spinning flag or something on a motor to break a light beam repeatedly, and make sure that it's actually happening.

You know you can initialize input pins as INPUT_PULLUP, right? No need to open them and write a HIGH to them.

I think this would work better if you start a timer when the gun is fired, and use interrupt-on-change functions when each sensor's light beam is interrupted. As the interrupts occur, you could grab the current timer quantity. But it obviously depends on having sensors that actually respond to the input!

Is start with code, and rewrite it using interrupts.

1 Like

I'd start with the code and write a version for 1 sensor only. Dont comment out it still looks too much, just write a very simple sketch.
Also forget the messing about with writes, and put a resistor pull up on the sensor.

The receiver is *open collector transistor output* which means that you do need a pull up resistor if you want to read a digital signal off the signal wire. Most microcontrollers have the ability to turn on a built in pull up resistor. If you do not, connect a 10K resistor between the white wire of the receiver and the red wire. If you want to control a relay or LED or whatever, it can sink up to 100mA to ground

I'd choose a pull up that gives about 1mA so 4k7 or 3k3 depending on the supply voltage; And a suitable decoupling capacitor on the supply rails.

Get it working to light an LED as soon as the beam is broken, for testing. Then adapt your sketch to use an interrupt on a negative edge.

really wont help!

Response Time: <2 ms

I find it hard to believe its that slow.

1 Like

It definitely is! I wish the documentation actually had useful info in it, that's literally how its written.

I can get access to a scope and have a vague Idea on how to use one, I will give this a shot and try and read the response time

also thanks for the array pointer! I come from a MATLAB background so I'm not sure why I didn't try an array first.

Thanks for the help!

I had actually considered this, I have a few extra sensor pairs lying around so that shouldn't be too hard to rig up.

I did not know this! Like I said I'm very new to all this, I will include that as well

sounds like interrupts might be a goof first option before pulling out my receivers.

How do you intend to 'aim' the projectile... ?

I used to teach people to shoot, most couldn't hit a 3mm target at 10 feet... :thinking:

Good luck

:smiley_cat:

1 Like

I will give that a shot, not exactly sure what that does but I will do some more reading! Also not sure what a decoupling capacitor does but will look into that as well. thanks for the pointers!

So do I after doing some reading on phototransistors and photodiodes, unfortunately that's all the details you get in the documentation provided. I guess this should be a lesson to me on picking components with more robust documentation.

The sensors are actually mounted inside air tight brackets along the barrel with "viewports" drilled. They are actually being used to view internal ballistic phenomenon, so aiming is not a problem in this case.

You are absolutely right though, hitting that beam would be almost impossible any distance from the muzzle, let alone hitting eight!

Odds are very good your IR sensor circuit is simply not fast enough**. To test, use an oscilloscope to monitor the output when you fire your projectile. If the scope can't see a pulse, your software won't. If you need to, get someone with some real comprehension of single-shot triggering of a scope to give you a hand, because what you're trying to do isn't super-easy for someone who doesn't know how to use one.
Also, be very sure your circuit performance is 'orthogonal', i.e. has the same delay-to-on and delay-to-off times, otherwise that will mess your velocity calculation up. It would really be best for you to characterize your sensor using hardware only, so that you can verify these effects and account for them if your data is important to you.
If you do verify that your IR sensor 'sees' the projectile, the next step is a very, very simple code. For one sensor, simply mirror the digital input state to a digital output state. NO serial printing, no other fluff. Just something like:

int sensorpin1 = 24;
int Relay1 = 2;

void setup() {
  pinMode(sensorpin1, INPUT);
  pinMode(Relay1, OUTPUT);
}

void loop() {
  digitalWrite(Relay1, digitalRead(sensorpin1));
}

After all, this represents the BEST* your Arduino can do. Then, use your scope's two channels to monitor the input and output signals simultaneously, looking at the pulse relationship.
'* - As others are intimating, you may be able to respond faster with an interrupt structure or other more advanced technique; however, first make sure you can walk... then strap in, because their suggestions will take you into advanced territory!
'** - a schematic along with component identification for each sensor would be required to do any performance estimate on paper, hence I'm suggesting you measure the actual performance.

1 Like

John, I suspect the "sensor" is actually an IR LED and it takes that long to collect enough photons to trigger conduction in the junction. A real photodiode would certainly be faster.

You could try this

void setup()
{
  pinMode (LED_BUILTIN, OUTPUT);
  attachInterrupt (digitalPinToInterrupt (2), detected, FALLING);  // D2
}
void detected ()
{
  digitalWrite(LED_BUILTIN, HIGH);
}
void loop()
{
}
1 Like

IR Break Beam too Slow? Well, we need to know the length of the projectile and the range of speed that you would like to measure.

Lets assume your sensors are perfectly aligned and electrically configured such that you can achieve 2ms response. Lets also assume the length of the projectile is 1 inch.
In this case, for the beam to be broken for at least 2ms, the maximum projectile speed to measure would be 500 inches per sec = 41.667 fps = 28.43 mph.

If the projectile length is 6 inches, then the for the beam to be broken for at least 2ms, the maximum projectile speed to measure would be 3000 inches per sec = 250 fps = 170.46 mph.

In the above examples, higher speeds cannot be measured. Projectile length, sensor alignment and electrical configuration is important.

Sorry, forgot the pullup

void setup()
{
  pinMode (LED_BUILTIN, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt (digitalPinToInterrupt (2), detected, FALLING);  // D2
}
void detected ()
{
  digitalWrite(LED_BUILTIN, HIGH);
}
void loop()
{
}
1 Like

@ZX80 I suggest you add a bit of descriptive text to explain just what this is supposed to accomplish for the OP.

Years ago, about 10 years ago I bought some just in case sensors for my ballistic chronograph. The sky screens used a Motorola 3056 Photo Detector which has been replaced by a NTE 3032 part number. This is the data sheet. I paid about $2.00 USD each and about choked when I saw today's prices. Cheapest I saw was about $10 USD on Amazon. However you might be able to get some ideas from the data sheet. If I can catch (detect) a small bullet traveling at 4,000 FPS I am pretty sure anything like these will work for your application. The chronograph was an old Oehler 35P. The chrono used an 8 MHz clock if I recall correctly. Then too all of this only applies if the sensors are the problem. I am not suggesting you buy $100 USD in sensors. :slight_smile:

Ron

1 Like

I appreciate all this very helpful advice! I am going to try out some of the simplified codes and brush the rust off my oscilloscope skills to actually do some measurements! I will report back with some results when I have them and update the thread (hopefully with a solution).

thanks again for all the help! this is just the advice I needed to get my head straight.

@madmax303 : I cant agree with @Paul_KD7HB that the sensor is an IR LED, although the emitter likely will be.
However his rematrk about the carriers made me think;
MAYBE the intensity is MUCH too high - as they are designed to work at 10" spacing and you have them at 0.5 inch.
The receiver is getting swamped with a LOT of free carriers that have to leave the junction before the output will change.

@camsysca, @ZX80 have given you an idea of the code, without/with interrupts.
A very brief break will not be enough to operate a relay so I'd suggest you use the ZX80 version, perhaps with a delay and

digitalWrite(LED_BUILTIN, LOW);

to reset the led.

Also - the "receiver" is "OPEN COLLECTOR" which to me indicates the receiver is NOT a simple photodiode.
So whetever they have in there could be slow.

I'd suggest you replace the "receiver" with a proper IR photodiode - such as this

@johnerrington I disagree. Context is important, John. I was suggesting to the OP that he strip his code to the minimum, I used his variable names for familiarity. I wasn't expecting relay performance, hence I suggested he use an oscilloscope!!! to measure real performance. My suggestion to @ZX80 was to provide more than code, because the OP is clearly unfamiliar with some aspects of micro code, and might not understand the relevance of interrupts vs straight IO polling.