Yet another chronograph, serial output.

Hi,

So yes, this is yet another chronograph project, I am a novice at programming and using micro controllers, I've only had the Arduino UNO for an evening (but I feel I'm already slightly addicted, I should have got into this a long time ago). The code I do have is a bit of a mix of all the other chronograph topics I could find and some of the example code in the Arduino dev software.

I have two laser beams powered by the 3.3V ouput and two high speed optoschmidt sensors powered by the 5V output.
The optoschmidt sensors are wired up to pins 2 and 4, when the laser beam is pointed at the sensor they let 5V though to the pins.
So the pins are HIGH by default and LOW when a projectile breaks their beam.

This is my attempt so far:

//Laser gate chronograph, with serial output.

int sensorA= 4,sensorB= 2; //The two photodetectors are connected to pins 4 and 2
unsigned long start_time, stop_time, elapsed;
float distance, velocity;

void setup() {
  // initialize sensor A as an input
  pinMode(sensorA, INPUT);      
  // initialize sensor B as an input:
  pinMode(sensorB, INPUT);
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  // set distance between photodetectors in metres
  distance= 0.02;
}

void loop(){

  // Wait until triggerA becomes LOW (projectile cuts beam)  
  while (digitalRead(sensorA) == LOW) {     
    start_time=micros();  // record start time
    
    //triggerA was tripped, now wait for triggerB to trip  
    while(digitalRead(sensorB) == LOW) {
      stop_time=micros(); // record stop time  

      elapsed= stop_time - start_time; //How long did it take ?
      velocity= (distance * 1000000) / elapsed; // So, how fast was it going ?
      //Send it all through serial 
      Serial.println ("raw data:");
      Serial.println (" ");
      Serial.print ("Start=");
      Serial.println (start_time);
      Serial.print ("Stop=");
      Serial.println (stop_time);
      Serial.print ("Elapsed=");
      Serial.println (elapsed);
      Serial.print ("Projectile velocity=");
      Serial.print (velocity);
      Serial.println ("m/s");
      Serial.println ("___ ___ ___ ___");
      delay (2000); // don't go too crazy
    };
  }; 
}

The while loops don't seem to work like I'd imagined, I only get a reading when both sensors go LOW, when what I would want is to only get is a reading when sensor A is triggered followed by sensor B.
I have tried making sure that the while loop only works when one sensor is HIGH and the other LOW ( using &&) but that just produces nothing.

Here is what I get on the serial monitor:

Does anyone have any pointers as to where I'm going wrong ?

EDIT: Ah it those are actually slightly different results than was I was getting, the micros() start time is now not resetting, I was getting a constant 8microseconds difference on each cycle. I'm guessing this has happened since I changed the variables from int to unsigned long.

The while loops have to go. They are not accomplishing what you want.

On each pass through loop, you need to sense if sensorA is LOW. If so, record when sensorA goes HIGH, and set a flag.
On each pass through loop, you need to sense if sensorB is LOW. If so,record when sensorB goes HIGH, and set a flag.
On each pass through loop, you need to check whether both flags are set. If so, the difference in times will tell you the direction and speed.

I don't understand the logic there at all.

Seems to me that you ought to be looking for HIGH-to-LOW transitions on both inputs. That means you need to record the current state and compare to the previous state. When you detect a HIGH-to-LOW transition for input A you note the current time as the start time. When you get a HIGH-to-LOW transition for input B you note the current time as the end time, and do your sums based on the start and end time. With that algorithm you will be able to cope with breaking either beam any number of times.

It would also be sensible to debounce both inputs by putting a short delay after you have finished dealing with a transition, to prevent multiple triggers during a transition.

@PaulS

So something along these lines ? EDIT: Oh wait sorry, didn't read that properly, it's getting a bit late for me, I'll have to try again on a rested head. Thanks to both of you.

Void Loop () {
flagA =0;
flagB =0;

if (digitalRead(sensorA) == LOW) {     
    start_time=micros(); 
    flagA = 1;
   
    if(digitalRead(sensorB) == LOW) {
      stop_time=micros(); 
      flagB = 1;
      
       if (flagA = 1 && flagB =1) {
            elapsed= stop_time - start_time; 
            velocity= (distance * 1000000) / elapsed; // etc... post it to serial, delay...
} else;
} else;
} else;
}

@ PeterH, mmm OK, I'll give that a try. This wouldn't have been my approach either, I just happened to find a thread where people where suggesting the while loop blocking.

Void Loop () {

Posting code that actually compiles is preferred.

 else;

The else is not mandatory. If you have nothing to do, leave it out.

if (digitalRead(sensorA) == LOW) {     
    start_time=micros(); 
    flagA = 1;
   
    if(digitalRead(sensorB) == LOW) {
      stop_time=micros(); 
      flagB = 1;

Why is the reading of sensorB happening only is sensorA is low? If the direction doesn't matter, the flags are unnecessary.

            velocity= (distance * 1000000) / elapsed; // etc... post it to serial, delay...

Literals, like 1000000 are interpreted as ints, in the absence of directives to the contrary. 1000000 is NOT an valid int value. You need to add UL to the end to tell the compiler that the value is to be interpreted as an unsigned long.

Will distance * 1000000UL fit in whatever variable type distance is?

Posting code that actually compiles is preferred.

:blush:
That makes a lot of sense.

Distance is defined as float so that should be ok, right ?(in this case it ends up being 15000 when multiplied)
I could just give it a pre-multiplied initial value for the sake of this learning code.

Ok, I changed the code and it seems to work, I can't yet tell if the readings are accurate but at least they make sense. Positive/negative values depending or order of triggering, faster trigger lead to higher velocity....

int sensorA= 4,sensorB= 2; //The two photodetectors are connected to pins 4 and 2
boolean flagA, flagB; 
unsigned long start_time, stop_time;
long elapsed; // as it could be negative
float distance, velocity;

void setup() {
  // initialize sensor A as an input
  pinMode(sensorA, INPUT);      
  // initialize sensor B as an input:
  pinMode(sensorB, INPUT);
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  // set distance between photodetectors in metres and x 1 000 000
  distance= 15000;
  //reset flags
  flagA= 0;
  flagB= 0;
}

void loop(){

  if (digitalRead(sensorA) == HIGH) {     
    start_time=micros(); 
    flagA = 1;
  }

  if(digitalRead(sensorB) == HIGH) {
    stop_time=micros(); 
    flagB = 1;
  }

  if(flagA == 1 && flagB == 1) {
    elapsed= stop_time - start_time; //How long did it take ?
    velocity= distance / elapsed; // So, how fast was it going ?
    
    //Send it all through serial 
    Serial.println (" "); //etc...
    
    // rest flags for next loop
    flagA=0;
    flagB=0;
    delay (10); // don't go too crazy
  }
}

Is this what you meant ?

That's close to what I had in mind, but you still aren't looking for transitions. To implement the algorithm I described you would need to store the previous state for each input, read the new state and see whether they differ. When they do, record the time.

You have some choices to make about exactly what you want the algorithm to achieve. I've assumed you want to trigger on the leading edge of the beam being obstructed. That makes most sense to me. But you could trigger on the trailing edge if you want (take the time when the beam comes back). You need to decide what to do if your inputs to the two sensors don't come alternately as you're expecting i.e. one sensor triggers multiple times without the other one triggering.

My suggestion is to [re]start timing every time the first beam is broken, but only produce a result the first time the second beam is broken. So that suggests you need these state variables:

previous state of the start input (HIGH/LOW)
previous state of the end input (HIGH/LOW)
whether it is currently timing (boolean)
start time (only valid when timing)

Within the loop you also need to know the current time, and the state of the two inputs.

That's close to what I had in mind

It's what I had in mind, too.

The points that PeterH makes about only recording time/setting flags at the transition are important, and need to be part of the final project.

hehe I was refering to PaulS's mind :wink: Still haven't got around to using the transition as the trigger.

Leading edge or trailing edge doesn't make much of a difference, a part from maybe the fact that this chronograph will be used mainly with air powered devices and the condensation plume formed with each shot tends to be denser behind the projectile and maybe make false triggering more likely... ?

As far as multiple triggering is concerned, I think in most cases the first trigger would probably be the right one.

Mmm need to think this through... I'll build up a proper rig and do some testing to see how it reacts in the real world.

Thanks for the insight and advice, this is proving to be very educational :slight_smile:

You need to decide how to deal with missed triggers, and false triggers.

Maybe you can use your knowledge if the expected timing relationship between real triggers to deal with these. For example you might be able to say that real triggers for the same input won't occur any closer than 1 second apart, and the 'end' trigger will occur within a tenth of a second of the 'start' trigger, or something like that.

To test your rig, try dropping a bb through it vertically. Gravity is 9.8 meters per second.
The farther apart the sensors are the less the timing of the flags will be an issue.

Gravity is 9.8 meters per second.

sp. "Gravity is 9.8 metres per second per second".

Ummm sometimes?
http://mrdata.usgs.gov/geophysics/gravity.html

Velocity is distance/time
Gravity is acceleration
Acceleration is (velocity exit - velocity entrance) = velocity/time (when pointing down measuring gravity)
I am working on a chrony also.
I am trying to figure out how to calibrate it.
Could there be a timing issue if your sensors were say 30 feet away from the board?
photo resistors or phototransistors?
how do you know the response time of a component?

Ummm sometimes?

No, always. Gravity, as you point out, is acceleration, which is change in velocity (m/s) over time (so, m/s/s).

how do you know the response time of a component?

Look at its data sheet.

I am trying to figure out how to calibrate it.

If you're trying to calibrate using gravity, then you need to take into account that the object will have accelerated (though not quite at 9.81ms-2 unless you're testing in a vacuum) whilst passing between the photogates.

could there be a timing issue if your sensors were say 30 feet away from the board?

No, the signal travels at close to the speed of light through the wire, IIRC about 1nS per foot in copper. I doubt the accuracy you're looking for will be affected by the difference between a short wire and a long wire. Apart from that if the start and stop sensors are a similar distance away the delay will be about the same.


Rob

IIRC about 1nS per foot in copper

In a vacuum, yes, but quite a bit slower in copper.
Still not anything to worry about over 10 metres.