Calculating time between two LDR's

I have an Arduino Uno, I’m trying to calculate the time it takes a runner to sprint from start to finish. 1st LDR is at the start line and the 2nd is at the finish line. my code works just fine, as far as errors are concerned. The problem is at the starting line, when the runner starts his/her sprint, the first leg passes the LDR activating the timer, however as the second leg crosses the LDR it resets the timer. Therefore, the time it takes the runner to reach the finish line is skewed. The time that is recorded is not from a standing position, it is from a running or “jump start” position. I have thought about using a SPDT relay, but thought I would give this forum a try and see if there was a way around this little hiccup I have. Any help would be much appreciated. I have included the sketch below. Please forgive if I have a few things out of line. Thanks in advance.

const int THRESHOLD_LOW = 600; //low point of ldr
const int THRESHOLD_HIGH = 780; /// high point of ldr
//HystereticRead takes the analog input of the LDR's and converts it to logic
int ldrPin1 = A0;
int ldrPin2 = A5;

int HystereticRead(int pin, int previousState) {
  int photo = analogRead(pin);
  if (previousState == LOW && photo >= THRESHOLD_HIGH) {
    return HIGH;
  }
  else if (previousState == HIGH && photo < THRESHOLD_LOW) {
    return LOW;
  }
  else {
    return previousState;
  }
}

void setup() {
  
Serial.begin(9600);

}

void loop() {
 
 static int state0, state1;
  static double time0, time1, time2;

  int new_state = HystereticRead(A0, state0);
 if (state0 == LOW && new_state == HIGH) {
    time0 = millis();

  }
  state0 = new_state;


  new_state = HystereticRead(A5, state1);
  if (state1 == LOW && new_state == HIGH) {
    time1 = millis();
    time2 = (time1 - time0) / 1000;
    Serial.println("time passed: (s)");
    Serial.println(time2);
  }
  state1 = new_state;
}[code]

Say you expect the sprint to last 10 seconds, but the second leg obscures the beam after 500 milliseconds (half a second) or less. So your routine can check that the elapsed time is greater than half a second before ending the timer.

You can make this simpler by using process states.

State 1 is waiting to start. It watches sensor 1 and ends when sensor 1 is tripped. Start time is recorded and state is changed to 2.

State 2 is waiting to finish. It watches sensor 2 and ends when sensor 2 is tripped. Elapsed time is calculated and displayed and state is changed to 1.

In code we use a variable to hold the value of the current process state, mode or step whichever suits and the technique is called finite state machine, is simpler than it sounds.

Indeed - states is the way to go. Then it doesn't matter when or how often the first sensor gets triggered, it's only the first trigger that matters.

I'm just a little helpless as to how to create and enter this important piece of coding

Study this tutorial about State Machines (there are many others on line). Very simple, really!

Sammygrind: Hey thanks smoke that is exactly what I'm going for, the relay option was my last ditch effort. Although I understand what you posted as an answer I'm just a little helpless as to how to create and enter this important piece of coding into my existing code. But i really do appreciate your help so far.

You keep some of it, I guess. Pin #'s....

If you can't connect an LDR to a digital pin then look into IR detectors (phototransistor with a black bulb, sees only near-IR) that do work. Then you read the pin. You might need a resistor & transistor.

If you restrict the view of any light sensor, put it at the back of a paper & tape tube (flat black inside is best) long enough to make a 'beam' and put a bright light source where the 'beam' points. When anything gets between you have a beam break. The tube cuts out extraneous light, the difference between light and shadow is greater.

How are the LDR's set up to get these? const int THRESHOLD_LOW = 600; //low point of ldr const int THRESHOLD_HIGH = 780; /// high point of ldr

If the LDR's can go digital LOW to HIGH then..... not compiled or tested,

const byte ldrPin1 = A0; // still going to read em digital, see if LDR can do it direct or needs a transistor
const byte ldrPin2 = A5; // LDR is LOW 
const byte ledPin = 13;

byte raceState = 1;
byte senseRead;

unsigned long raceStart, raceElapsed;

void setup()
{
  Serial.begin( 115200 ); // set your monitor to match, slow serial jams the output buffer quicker
  Serial.println( F( "Race times in milliseconds\n" ));

  pinMode( ledPin, OUTPUT );
  pinMode( ldr1, INPUT );
  pinMode( ldr2, INPUT );
}

void loop()
{
// State 1 is waiting to start. 
if ( raceState == 1 )
{
// It watches sensor 1 and ends when sensor 1 is tripped.
  if ( digitalRead( ldrPin1 ) == LOW )
  { 
  // Start time is recorded and state is changed to 2.
    raceStart = millis();
    raceState = 2;
    digitalWrite( ledPin, HIGH );
  }
}
else  // there's only 2 states here, no need for switch-case
{
// State 2 is waiting to finish. 
// It watches sensor 2 and ends when sensor 2 is tripped. 
  if ( digitalRead( ldrPin2 ) == LOW )
  {
  //Elapsed time is calculated and displayed and state is changed to 1.
    raceElapsed = millis() - raceStart;
    Serial.println( raceElapsed );
    digitalWrite( ledPin, LOW );
    raceState = 1;
  } 
}

As long as it flips from HIGH to LOW and back, isn’t that basically what

int HystereticRead(int pin, int previousState) {
  int photo = analogRead(pin);
  if (previousState == LOW && photo >= THRESHOLD_HIGH) {
    return HIGH;
  }
  else if (previousState == HIGH && photo < THRESHOLD_LOW) {
    return LOW;
  }
  else {
    return previousState;
  }
}

does?

Do you restrict the view of the LDR? That will get rid of a lot of ‘noise’ (light from all around) and allow better HIGH/LOW events. From what I saw of your LDR low… is that your switch-point or is 600 as low as it gets?

Sammygrind:
I have LDR’s set up as analog, I am using the runners shadow but The easiest way to explain what I was doing and The problem I was having was to use the runners second leg interupting The LDR start timer analogy.

2nd leg means adding states to get both 1st leg transitions then the 2nd leg shadow before switching to wait for finish.

I don’t see why 1st leg to cross both lines isn’t good, if the 1st leg is fast then the elapsed time will be longer. Starting on the 2nd leg crossing, the elapsed time is even less!

Perhaps though you want the finish to go by the 2nd leg across? Finish line is usually touch, not passed, yes?

The LDR is like 1/10 second slow. You can sense light faster with a led and a resistor, there’s a Playground project that has a led adjust itself to how well it is lit (dims with room light) and it does that faster than you can see (40 ms/25 FPS fools eyes, this takes < 5), about 20x faster than the LDR.

I think that you (plural) are making it way more complicated than it needs to be.

I modified Sammygrind’s original code. The main modification I made was to introduce a new variable called alreadyStarted. The purpose of this variable is so that, once the timer has started, it does not “start” again until after it “finishes”. (Please study the code to see how it achieves this.) I also cleaned up a bit of the number-crunching regarding calculation of times.

Here is the modified code:

const int THRESHOLD_LOW = 600; //low point of ldr
const int THRESHOLD_HIGH = 780; /// high point of ldr
//HystereticRead takes the analog input of the LDR's and converts it to logic
int ldrPin1 = A0;
int ldrPin2 = A5;

int HystereticRead(int pin, int previousState) {
  int photo = analogRead(pin);
  if (previousState == LOW && photo >= THRESHOLD_HIGH) {
    return HIGH;
  }
  else if (previousState == HIGH && photo < THRESHOLD_LOW) {
    return LOW;
  }
  else {
    return previousState;
  }
}

void setup() {
 
Serial.begin(9600);

}

void loop() {
 
 static int state0, state1;
  static unsigned long time0, time1; 
  static double time2;
  static boolean alreadyStarted = false;

  int new_state = HystereticRead(A0, state0);
 if (state0 == LOW && new_state == HIGH && alreadyStarted == false) {
    time0 = millis();
    alreadyStarted = true;
  }
  state0 = new_state;


  new_state = HystereticRead(A5, state1);
  if (state1 == LOW && new_state == HIGH && alreadyStarted == true) {
    time1 = millis();
    time2 = (time1 - time0) / 1000.0;
    Serial.println("time passed: (s)");
    Serial.println(time2);
    alreadyStarted = false;
  }
  state1 = new_state;
}

You don't need that additional alreadyStarted flag. The states of the LDRs themselves are enough for this finite state machine.

Ah, got the problem. You indeed need that extra flag, I was wrong in #11.

Nevertheless, it's a finite state machine. The triggering of sensor 1 puts it in the "timing" state, triggering of sensor 2 reads the time and puts it in "idle" state.

This is one of many tutorials that explain the principle.

Sammygrind: If what I am reading correctly with odometers post, the start timer will not be interrupted by any other "object" no matter how many times it passes over the 1st LDR the time will still continue to count until the finish line LDR has been breached. Because that is the problem i am having everything works as it should except the starting line LDR, if an "object" (leg, shadow etc...) Passes over starting line LDR after it has initiated the timer it resets and starts from the new time. I will try odometers code and see if that works. This issue has been chewing at my brain for some time and obviously you guys are much smarter than me on this. Again, I appreciate any and all info and advice.

Did you try what I gave you in reply 6?

It's not been compiled to test, let me know if anything holds it up. I would like it if you picked up on the idea of states/modes/steps as an approach to automation, it makes for less code to debug.