While loops not stopping when condition is false?

Maybe I'm misinformed about while loops, but from what I know, they're supposed to loop indefinitely under some condition and then break when that condition becomes false. When I start up my sketch, it gets to a while loop and gets stuck there even when the condition changes. Here is the code in question:

void hide() {

  tone(piezo, 3000, 1000);    //tones a piezo buzzer to signal start
  delay(3000);
  
  int val = volts(phTransistor);    //assigns val to a function that calculates the voltage coming through a phototransistor
  
  while (val == 0.00) {    //while val is 0.00 volts in a pitch black room,
    
      maneuver(200, 200, 1000);  //drive servo car forward
      maneuver(200, -200, 500);    //turn servo car
  
  }

  //when while loop breaks, continue through function to the end

  maneuver(0, 0, -1);    //stops servos and detaches them
  
  for (int i = 0; i < 5; i++) {    //tone piezo 5 times to signal light was shined on phototransistor
    
    tone(piezo, 3000, 500);
    delay(550);
    
    
  }
  
}

The servo car just keeps driving no matter how long I shine my light on it, and I'm 100% certain it is registering more than 0.00 volts.

  int val = volts(phTransistor);    //assigns val to a function that calculates the voltage coming through a phototransistor
  
  while (val == 0.00) {    //while val is 0.00 volts in a pitch black room,
    
      maneuver(200, 200, 1000);  //drive servo car forward
      maneuver(200, -200, 500);    //turn servo car
  
  }

You get val right before you enter the while loop, but never update it when inside the loop.

You also assign the result of volts() to an int, and then try and compare it with a float. Pick float or int and stick with that.

Hi JackSac67,

You are correct about loops. They do what you say, and unfortunately they also do what you code them to do!

I assume you are referring to the while loop. It looks like you want to repetitively perform 2 maneuvers whenever the sensor returns 0.00V, until the sensor detects light.

Here's the likely culprit: your sensor is not returning exactly 0.00 volts in the dark. The values may be very close to zero, but will never be exactly zero for a number of reasons. I suggest that you do something more like:

while (val < 0.1) { //while in a pitch black room
...
}

Of course, you'll need to determine the correct value for what constitutes a dark room.

... Also, as a general rule, you shouldn't use an equality comparison for floating point values. Rounding errors, due to the fact that the value is represented with a finite number of bits, makes equality near impossible. Always check using the floating point absolute value function like this to see if the two values are close enough to be equal:

if (fabs(var1 - var2) < 0.01) ...

Pat.

Here's the likely culprit:

No, the culprit is that the variable being tested is not being updated in the body of the loop, though I agree that testing for equality of floats is usually a Bad Thingtm.

You get val right before you enter the while loop, but never update it when inside the loop.

You also assign the result of volts() to an int, and then try and compare it with a float. Pick float or int and stick with that.

1st part makes sense, 2nd part was my own brain fart. In regards to part 1, how should I go about updating within the loop? Using something like a variable called old_val or something to store the previous value and then checking if there was a change with an if statement; i.e. if (val > 0.01 && old_val < 0.01) {do stuff} old_val = val?

Here's the likely culprit: your sensor is not returning exactly 0.00 volts in the dark. The values may be very close to zero, but will never be exactly zero for a number of reasons. I suggest that you do something more like:

while (val < 0.1) { //while in a pitch black room
...
}

Of course, you'll need to determine the correct value for what constitutes a dark room.

... Also, as a general rule, you shouldn't use an equality comparison for floating point values. Rounding errors, due to the fact that the value is represented with a finite number of bits, makes equality near impossible. Always check using the floating point absolute value function like this to see if the two values are close enough to be equal:

if (fabs(var1 - var2) < 0.01) ...

Thanks for the info on floats, makes more sense.

In regards to part 1, how should I go about updating within the loop?

Add a call to volts() inside the while body, assigning the value to val.

Got it to work, thanks. While I'm at it, does anyone have any ideas on how to make the loop break almost instantaneously when light hits the transistor? After it registers it loops through once more then goes on.

You just need to call break

if(val > 0)
    break;

JackSac67:
Got it to work, thanks. While I'm at it, does anyone have any ideas on how to make the loop break almost instantaneously when light hits the transistor? After it registers it loops through once more then goes on.

assign the variable at the end of the loop, then the loop test is the next thing to happen...

  int val = volts(phTransistor); 
  while (val == 0.00) {    //while val is 0.00 volts in a pitch black room,
    
      maneuver(200, 200, 1000);  //drive servo car forward
      maneuver(200, -200, 500);    //turn servo car
      val = volts(phTransistor); // ready for next loop test
  }

This is a common idiom and can be done slightly more elegantly with a for-loop:

  for (int val = volts(phTransistor) ; val == 0.00 ; val = volts(phTransistor)) {
      maneuver(200, 200, 1000);  //drive servo car forward
      maneuver(200, -200, 500);    //turn servo car
  }

However I think what you really want is to cancel the maneuver() calls, so you'll have to place code in that routine to test for the cancel condition as well...

This is a common idiom and can be done slightly more elegantly with a for-loop

There is NOTHING elegant about abusing the for loop that way. Mixing types doesn't make sense. Nor does not incrementing anything. The while loop is designed for looping an unknown number of times. The for loop was designed for looping a known number of times. Either can be pressed into service in the other role, but it is not more elegant to do so.

You just need to call break

I had tried this beforehand and it yielded the same result. However it may have been due to placement, as

int val = volts(phTransistor);
while (val == 0.00) { //while val is 0.00 volts in a pitch black room,

maneuver(200, 200, 1000); //drive servo car forward
maneuver(200, -200, 500); //turn servo car
val = volts(phTransistor); // ready for next loop test
}

this makes more sense

Ok, but just don't forget what I said about val==0.00 !!

MarkT:
This is a common idiom and can be done slightly more elegantly with a for-loop:

  for (int val = volts(phTransistor) ; val == 0.00 ; val = volts(phTransistor)) {

maneuver(200, 200, 1000);  //drive servo car forward
      maneuver(200, -200, 500);    //turn servo car
  }

Yuck!

Why not simply use a while loop? This is, after all, precisely what it was designed for.

  while (volts(phTransistor) == 0.00) // zero volts means the room is dark
  {
      maneuver(200, 200, 1000);  //drive servo car forward
      maneuver(200, -200, 500);    //turn servo car
  }

Personally I'd rather see a threshold comparison rather than checking for exactly zero, and as designed the check only occurs when the car has completed the two actions, but those are separate issues.

PaulS:

This is a common idiom and can be done slightly more elegantly with a for-loop

There is NOTHING elegant about abusing the for loop that way. Mixing types doesn't make sense. Nor does not incrementing anything. The while loop is designed for looping an unknown number of times. The for loop was designed for looping a known number of times. Either can be pressed into service in the other role, but it is not more elegant to do so.

Then you need to see the standard idioms for Iterators in Java using for-loops, and start to think about what elegance means in an imperative language - expressiveness, conciseness, referential transparency and all that.... In Java you typically go:

    for (Iterator it = cookies.iterator () ; it.hasNext () ;)
    {
      ...
    }

Which declares the variable "it" local to the loop (not polluting the namespace of surrounding code and allowing the same loop to be
copy/pasted without issue.

The alternative would be:

    Iterator it = cookies.iterator () ;
    while (it.hasNext ())
    {
      ...
    }

which both leaves the namespace pulluted with the "it" variable and the idiom cannot be copy/pasted without alteration as that would create a duplicated variable declaration:

    Iterator it = cookies.iterator () ;
    while (it.hasNext ())
    { 
      ...
    }
    .....
    Iterator it = cookies.iterator () ;  /// ERROR - have to reuse the variable by assignment, or create another one.
    while (it.hasNext ())
    {
      ...
    }

And remember the for loop in C, just like Java, is purely syntactic sugar in the first place, it is exactly equivalent to a while loop in a block...
So we could code our Java example as:

    {
      Iterator it = cookies.iterator () ;
      while (it.hasNext ())
      {
         ...
      }
    }

and use another layer of braces, but a for-loop does this for us and keeps the control-structure all on one neat line - that's elegance (simple, powerful, clear). Now if only there was a neat way to avoid the repetition in the C example here...