how to stop a loop

I am trying to program a photogate whereby you slide a business card down the center of a breadboard and calculate the speed in miles per hour - based on the time when the first photogate is blocked and the time the second one is blocked - they will both be blocked at the same time.

The problem is that if I keep both blocked, it keeps returning values even after it does the calculations one time.

I'm trying to use the break command but maybe I'm using it incorrectly.
What am I doing wrong and how do I fix it?

float distance =2.75;
int ledStart = 8;
int ledEnd = 9;

int gate1 = 0;
int gate2 = 0;

long starttime=0;
long endtime=0;

float velocity = 0;
long timeDiff = 0;
float milesPerHour=0;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(ledStart,OUTPUT); //set led pins as output
pinMode(ledEnd, OUTPUT);

}

void loop() {
// put your main code here, to run repeatedly:
digitalWrite(ledStart,HIGH); //turn and keep light on
digitalWrite(ledEnd, HIGH);

gate1 = analogRead(A1);
gate2 = analogRead(A2);

if ((gate1 < 600) && (gate2 > 600)); // gate 1 is blocked and gate 2 is clear
starttime =millis();

while (gate1 < 600) { //as long as gate 1 is blocked check gate 2
if (gate2 <600) { //if gate 2 is also blocked

long endtime= millis(); //record how long till gate 2 got blocked

timeDiff =endtime-starttime; //calculate time difference
velocity = distance / timeDiff; // calculate speed in inches / millisecond
milesPerHour = velocity * 58.82; // convert to miles/hour
Serial.println (starttime); //print times and speed
Serial.println (endtime);
Serial.println (timeDiff);
Serial.print ("The speed is: ");
Serial.print(milesPerHour,6);
Serial.println (" miles per hour");

break;
}

gate1 = analogRead(A1);
gate2 = analogRead(A2);
}

}

I think you are using the break command wrong, but you don't need it anyway. You have two options instead:

  1. Implement your code as a Finite State Machine (just google it)

  2. Use a flag variable. In other words, you would have a Boolean variable that controlled when the loop is to stop. In the loop, you would say:

bool flag = true;

while(flag)
{
  if(//something)
  {
    flag = flase;
  }
  else
  {
    flag = true;
  }
}

flag = true;

Within this "while(flag)" loop, you can can include all of the other loops and if statements.

First up, you should put your code in [code]code tags[/code], not inline. It's not too late to edit and fix that. And when you do, it's a good idea to also get rid of some of those blank lines. They serve no purpose and make it harder to read.

'starttime' and 'endtime' need to be 'unsigned long', not 'long'. A 'long' isn't big enough.

Make these 'const', too. Change:-

float distance =2.75;
int ledStart = 8;
int ledEnd = 9;

to

const float distance = 2.75;
const byte ledStart = 8;
const byte ledEnd = 9;

Edit: And I'd go with using a flag in the 'if' statement. Set it after a successful measurement, then clear it outside the 'while' loop.

Sounds like a good time to use state change

If first photogate changes state, starttime = millis

If second photogate changes state, endtime = millis

endtime - starttime ----calculations----etc mph

INTP:
Sounds like a good time to use state change

If first photogate changes state, starttime = millis

If second photogate changes state, endtime = millis

endtime - starttime ----calculations----etc mph

I think a flag is quicker and easier in this case.

@efeigenbaum, something else I just noticed - get rid of the semicolon at the end of this 'if' statement:-

if ((gate1 < 600) && (gate2 > 600)); // gate 1 is blocked and gate 2 is clear

A state change detection as described by INTP will do the trick.

You also have a bug

  if ((gate1 < 600) && (gate2 > 600)); // gate 1 is blocked and gate 2 is clear

starttime = millis();

Because of the semicolon at the end of the if, starttime will be set to millis() every iteration of loop(). It can easily be spotted if you use the auto-format function in the IDE (T). Both of the above lines have the same indentation indicating that the second line does not depend on the first one.

sterretje:
A state change detection as described by INTP will do the trick.

Either using a flag or state change detection would work, of course, but state change detection will use more SRAM, (2 variables, one per detector), plus more code.
I think that for something so simple, a flag is more then adequate.

And i'd already mentioned that semicolon bug. :wink:

And i'd already mentioned that semicolon bug. :wink:

I know, but I already typed it and did not want my energy to go to waste :smiley: I'm slow early in the morning.

Actual reason that I kept it in is that I gave some additional information.

sterretje:
I know, but I already typed it and did not want my energy to go to waste :smiley: I'm slow early in the morning.

Actual reason that I kept it in is that I gave some additional information.

Yeah, it was a good idea to point out that "Auto Format" would highlight the problem. I didn't think of that.
Yet another reason to "Auto Format" code. :wink:
(I wasn't really complaining. :slight_smile: )

OldSteve:
(I wasn't really complaining. :slight_smile: )

I know; hence al the smileys in the last few posts :smiley:

The posts following a 1-post-noob-getting-extraordinary-help is the prime place for our geek bonding moments.

Thanks. I'll try some of these ideas. I'm really new to programming...

OldSteve, can you explain what you mean by putting my code in "code tags, not inline"?

Also, I got it work using flags but....

It will do the process one time as long as the first gate is not blocked. If I move the object back to keep the first gate blocked but the second one open, it will calculate a speed again (albeit an incorrect one). Any ideas on how to fix it?

I wonder if I need to change it to using status information instead...

Send print command only when both are blocked after both have been open. It will constantly be reading, you won't gain much by actually trying to stop the loop and having to restart it, but you can control when it throws you a number. Don't worry that it keeps reading, just change when it prints.

So I figured out a way...I put an if statement at the top to set the flag to true only if both gates are open at the start.

It seems to be working just fine.

Thanks, everyone, for all your help! I've learned a lot!

Edit: I see I'm a little too late.

efeigenbaum:
It will do the process one time as long as the first gate is not blocked. If I move the object back to keep the first gate blocked but the second one open, it will calculate a speed again (albeit an incorrect one). Any ideas on how to fix it?

I wonder if I need to change it to using status information instead...

Not without seeing your code :wink:

const float distance =2.75;
const int ledStart = 8;
const int ledEnd = 9;

void setup()
{
  // do your setup here
  ...
  ...
}

void loop()
{
  // remember times when gates were blocked
  static unsigned long timeGate1 = 0;
  static unsigned long timeGate2 = 0;

  // a flag indicating that a calculation was done and the result was displayed
  bool fCompleted = false;

  // variables for calculation
  float velocity = 0;
  long timeDiff = 0;
  float milesPerHour=0;

  // read gate pins
  int gate1 = analogRead(A1);
  int gate2 = analogRead(A2);

  // reset timings if both gates are not blocked but both were blocked before
  // also indicate that the next time we must calculate / display again
  if(gate1 > 600 && gate2 > 600)
  {
    timeGate1 = timeGate2 = 0;
    fCompleted = false;
  }

  // if gate1 was not blocked yet, remember the time
  if(timeGate1 == 0 && gate1 < 600)
  {
    timeGate1 = millis();
  }

  // if gate2 was not blocked yet, remember the time
  if(timeGate2 == 0 && gate2 <600)
  {
    timeGate2 = millis();
  }

  // if both times are set, we can calculate and display
  // additional condition that it should not have been calculated and displayed before
  if(timeGate1 != 0 && timeGate2 != 0 && fCompleted == false)
  {
    // calculate speed and display
    ...
    ...

    // indicate that we're done with this measurement
    fCompleted = true;
  }
}

The above will detect swipes from left to right and from right to left. I have moved all your variables to loop() as you don't use them anywhere else; the constants are still global. Read up on 'scope'

The code uses the times as flags. Initial values are zero. If a time is zero and the corresponding gate is blocked, the time will be remembered. Only when both times are not zero, a calculation is done; there is the additional condition that this was not done before.

The first if block will reset the static variables if both gates are not blocked so a new measurement can be done.

Not tested, not compiled. Oh, and the code does not implement the leds :wink:

Note:
loop() is a function that is called repeatedly; therefore there is normally no need to use while statements inside loop().

Other note;
There is one potential week point in here and that is if a swipe does not go through both gates. You can add a timeout code if that possibility exists.

efeigenbaum:
OldSteve, can you explain what you mean by putting my code in "code tags, not inline"?

When posting code, instead of posting it as you did in the opening post, do what you did above. Paste the code, then put [code] immediateley before the code, then [/code] immediately after the code. It'll be placed in a scrolling text box then, with a "Select" option, making it much easier to read or copy and paste into an IDE. It also stops the code being corrupted by the forum software, which often changes the code to italics or adds smilies.

The easier way to add code tags is to paste the code, select it then press the </> button, which will automatically add code tags.

It's explained even more fully in this post, (item #7):- How to use this forum