IR Paintball Chronograph

In this project I am trying to measure the speed of paintballs moving at approximately 300 feet / second. Bare with me for this is my first project that's moving at 300 feet / second. I did some math and conversions and I believe what I'm doing is not too fast for the arduino to AnalogRead...

300ft/sec = 91.44 mm / 1mS
So the 16mm paint ball will move one full width in .176ms or 175.82 uS / 16 mm.

When looking at my IR Emitter and Detector they are both 5mm led's so I imagine it's a 5mm beam the ball is breaking.
| - 5mm - | < - Beam

@ 9uS/mm it blocks a 5mm beam in 45uS.
| (BALL | 11mm )

The ball will cross the beam for a full width (16mm) 175.82 uS / 16 mm
(BALL 11mm | ) |

So I did a simple test program and I see that with the beam being blocked for 175 micros I can AnalogRead at least 8 times while the ball is in front of the first sensor.

I am using code to set the prescale at 16 giving an ADC clock of 1MHz as stated here: Faster Analog Read? - #3 by jmknapp - Frequently-Asked Questions - Arduino Forum

Currently I'm able to drop a ball through the tube and get 9-10 Feet / second..

I just adjusted my code to only take the initial blocking of my first sensor because I believe the compressed air following the ball is throwing off my first sensor after the ball goes through it.
Here is my new code:

#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

  int averageFirst;
  int averageSecond;
  unsigned long start ;
  unsigned long endTime;
  int gateOne = 0;
  int i ;
  unsigned int val[20];
  unsigned int val2[20];
  unsigned int total;
  unsigned int total2;
  int times = 8;

  float seconds;
  
void setup() {


#if FASTADC
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;
#endif
pinMode(13, OUTPUT);     

  Serial.begin(9600) ;
  
  Serial.println("CHRONOGRAPH INITIATING: ") ;
  
  for (i = 0 ; i < times ; i++){
    val[i] = analogRead(0);
    delay(100);}
  for (i = 0 ; i < times ; i++){
    val2[i] = analogRead(5);
    delay(100);}

//Calculate Total
  for (int i = 0; i<times; i++){
    total+=val[i]; }
    
  for (int i = 0; i<times; i++){
    total2+=val2[i]; }
//Calculate Average
  averageFirst = total/times;
  averageSecond = total2/times;
  
  Serial.print("First Average: ");Serial.println(averageFirst);
delay(1000);
  Serial.print("Second Average: ");Serial.println(averageSecond);  
  Serial.println("READY...");
  Serial.println("");
i=0;
}

void loop() {
  if (analogRead(0)<averageFirst-5) {
  start = micros() ;
  i ++;
  gateOne ++;}          
  while (i==1){            
    if(analogRead(5)<averageSecond-5) {
      endTime=micros();
      
      if(gateOne==1){
        //Fire Completed Sucessfully
        Serial.println("End uS - Start uS: ");Serial.print(endTime);Serial.print(" - ");Serial.println(start);
        Serial.print(endTime - start);Serial.print(" uS");Serial.println(" / 1,000,000");
        seconds = (endTime-start)/1000000.0000;
        Serial.print(seconds);Serial.println(" S");
        Serial.print(".896 FT / ");Serial.print(seconds);Serial.print(" S");Serial.println("");
        Serial.print(0.8960/seconds);Serial.println(" FT/Sec");
        Serial.println("");
        delay(1000);
        gateOne=0;
        i=0;}
      else{
      Serial.println("Missed First Gate");delay(100);}        
            }
  }
}

With this older code I had a problem where I received a calculated time of 56uS = (7874836 - 7874780) meaning the ball was moving at "16000.00 FT/Sec" :astonished: =( :

#define FASTADC 1

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
  int averageFirst;
  int averageSecond;
  unsigned long start ;
  unsigned long endTime;
  int gateOne = 0;
  int i ;
  unsigned int val[20];
  unsigned int val2[20];
  unsigned int total;
  unsigned int total2;
  int times = 8;

  float seconds;
  
void setup() {


#if FASTADC
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;
#endif
pinMode(13, OUTPUT);     

  Serial.begin(9600) ;
  
  Serial.println("CHRONOGRAPH INITIATING: ") ;
  
  for (i = 0 ; i < times ; i++){
    val[i] = analogRead(0);
    delay(100);}
  for (i = 0 ; i < times ; i++){
    val2[i] = analogRead(5);
    delay(100);}

//Calculate Total
  for (int i = 0; i<times; i++){
    total+=val[i]; }
  for (int i = 0; i<times; i++){
    total2+=val2[i]; }
//Calculate Average
  averageFirst = total/times;
  averageSecond = total2/times;
  
  Serial.print("First Average: ");
  Serial.println(averageFirst);
delay(1000);
  Serial.print("Second Average: ");
  Serial.println(averageSecond);  
  Serial.println("READY...");
  Serial.println("");

}

void loop() {
  if (analogRead(0)<averageFirst-5) {
  start = micros() ;
  gateOne=1;}          
            
if(analogRead(5)<averageSecond-5) {
  endTime=micros();
  if(gateOne==1){
    Serial.println("End uS - Start uS: ");Serial.print(endTime);Serial.print(" - ");Serial.println(start);
    Serial.print(endTime - start);Serial.print(" uS");Serial.println(" / 1,000,000");
    seconds = (endTime-start)/1000000.0000;
    Serial.print(seconds);Serial.println(" S");
    Serial.print(".896 FT / ");Serial.print(seconds);Serial.print(" S");Serial.println("");
    Serial.print(0.8960/seconds);Serial.println(" FT/Sec");
    Serial.println("");
    delay(1000);
    gateOne=0;}
  else{
  Serial.println("Missed First Gate");delay(100);}        
        }

}

I've attached what the jig looks like and a simple schematic drawing of how I have the emitter and detector connected w/ the RadioShack ir 276-0142 information.

Doing an analogRead takes 104 µS so you are probably better off (much better off) eliminating that.

Either set up a comparator externally or use the internal comparator. That works in a couple of clock cycles.

Example here:

I have an example here where I timed something which passed a laser beam:

Provided the threshold of the detector is around the digitalRead threshold (ie. fairly low to fairly high, eg. 1v to 4v) then a digitalRead should be all you need.

#define FASTADC 1

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

void setup() {
  int start ;
  int i ;
  unsigned long totalTime;
  
#if FASTADC
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;
#endif

  Serial.begin(9600) ;
  Serial.print("ADCTEST: ") ;
  start = micros() ;
  for (i = 0 ; i < 1000 ; i++)
    analogRead(0) ;
    totalTime=micros() - start;
  Serial.print(totalTime) ;
 Serial.println(" uS (1000 calls)") ;
 Serial.print(totalTime/1000);Serial.println("uS / Call");
}

void loop() {
}

Correct me if I'm wrong but I'm getting 17 Microseconds per call?

Oh, OK, you changed the prescaler. That would be faster, but the other solutions would do a reading faster again than that.

If you use the input capture unit you can capture the trigger time pretty accurately (within 125 nS, I think).

Anyway, what was your original question?

Well I have a few, I'm not sure how accurate this is going to be? I know I did almost no low level code so every line is going to kill time and add onto my total microseconds.

Also could you look over my code and just see that it is organized properly as I'm very new to programming in general and It seems to be working at slow speeds ex: dropping a ball or weight but into the high speeds I'm not sure if my variable types are correct for what's going on.

Also, where my LDR or Infrared detector is I do not have it connected to 5v at all. I only have it going to Arduino analog in from ground. When I analogRead in noise I see from 98-101 until I block the ir beam and it drops from 98 down to about 60. Is it necessary to add in the resistor to hot, if not what can that improve or change?

I think you have to take into account that when the beam is blocked, the IR detector does not 'snap' from rail to rail instantly, but will have an envelope, ramping up, then later, ramping down. There is no guarantee that the threshold for what you consider the 'trigger point' will be in the same place, with regard to the amount of beam blocked by the ball on entry, and unblocked on exit/ In other words, the ramp up may be a different slope than the ramp down.

The other point you wondered about is the effect of the air being compressed and causing a variation in timing. This is a real concern, and it also include the possible interference of the turbulent, and possibly lower pressure air behind the ball.

Were I doing it, I might want to place two sensors some distance apart (not very far), and check the leading edges of each occultation of the beam, setting the comparator to trigger at some arbitrary level (experiment to find the most consistent level). This should remove both the 'ramp' and air problems. For calibration, perhaps an AC synchronous motor of known RPM, driving an arm with a thin metal 'flag' to interrupt the beam.

lar3ry, The sensors at a certain distance apart I have already on my first post I added a photo of the tube and sensors one at the front and one at the back and across from each is the ir transmitter to 5v. The sensors are about .896 feet apart. I imagine the furthest sensor from the starting point should not have the thick compressed gas behind it as bad as the first sensor will have. That is why I believe my reading was so fast the first sensor was still triggering while the ball finally passed my second sensor. So I wanted to put a counter (variable i) on my first sensor and only use the initial value (

   if (analogRead(0)<averageFirst-5) {
  start = micros() ;
  i ++;
  gateOne ++;}

). and then while that counter is = 1 check the other sensor

while (i==1){            
    if(analogRead(5)<averageSecond-5) {
      endTime=micros();
      
      if(gateOne==1){
        //Fire Completed Sucessfully

The gateOne variable is to check that the ball didn't miss the first sensor.. otherwise

else{
      Serial.println("Missed First Gate");delay(100);}

chris_a:
I've attached what the jig looks like and a simple schematic drawing of how I have the emitter and detector connected w/ the RadioShack ir 276-0142 information.

Your attachments are not a big success:

20131111_195506.jpg (0 KB - downloaded 2 times.)
20131111_194129.jpg (0 KB - downloaded 2 times.)

Zero bytes each.

Here's a sketch I used to time a ball passing a light, if that helps:

const byte LED = 12;
const byte photoTransistor = 2;

unsigned long startTime;
volatile unsigned long elapsedTime;
volatile boolean done;

void ballPasses ()
{
  // if low, ball is in front of light
  if (digitalRead (photoTransistor) == LOW)
    {
    startTime = micros (); 
    }
  else
    {
    elapsedTime = micros () - startTime;  
    done = true;
    }
    
  digitalWrite (LED, !digitalRead (LED));  
}


void setup ()
{
  Serial.begin (115200);
  Serial.println ("Timer sketch started.");  
  pinMode (LED, OUTPUT);
  attachInterrupt (0, ballPasses, CHANGE);
}

void loop ()
  {
  if (!done)
    return;
    
  Serial.print ("Time taken = ");
  Serial.print (elapsedTime);
  Serial.println (" uS");
  done = false;
  }

That uses interrupts to detect the transition "fairly" quickly. (An interrupt takes about 3 µS to enter the ISR proper).

I have another example which uses the input capture unit for fairly precise measurements:

Woops! Disconnected my phone before I finished posting.

My Basic schematic and the parts info:

and the very ugly ir tube

Your detector circuit looks a little unusual. Can you give the part number of the detector please?

The picture is blown up, huge... if you scroll it right you'll see the specs on the back of the packaging and the other half of the chronograph.

That is what I was asking about on my reply #6 IR Paintball Chronograph - #7 by system - Programming Questions - Arduino Forum

chris_a:
Also, where my LDR or Infrared detector is I do not have it connected to 5v at all. I only have it going to Arduino analog in from ground. When I analogRead in noise I see from 98-101 until I block the ir beam and it drops from 98 down to about 60. Is it necessary to add in the resistor to hot, if not what can that improve or change?

A circuit I dug up showed a 220 ? resistor in series with the detector LED.

You'll likely need a resistor from pin A0 to +5. The light falling on the detector varies the conductance of the sensor, which is a transistor. The resistor and the detector will form a voltage divider. I think the value should be around 250 - 300 ohms.

I wasn't sure about that part.. I just ran the LDR from ground to the analog in pin and it seems to read consistently right at 100 until you block the beam of light then it drops down to about 60 all without the extra resistor to 5v. Do you think this could cause a problem? I guess if I do not use 5v I will not see "rising" or "falling" if I used an interrupt since i'm not going from 0v - 5v.

Without the resistor, you will ge a lot of noise, and a very small swing in input level. You definitely need it.

Alright sweet, I'll add that in tonight and hopefully test it out tomorrow ! Worst case I'll just have to recode it all using interrupts. Thanks for all the input! :slight_smile: Tomorrow I'll report back on what kind of numbers I get.