Can't Break From While Loop

Hello!

I'm getting back into Arduino after having not used it for a while. I've tried searching for the solution but have not any luck.

I am trying to create a device for a manual machine feeding operation. The idea is that after the operator inserts parts, an ultrasonic sensor will pick up that the parts have passed, wait 8 seconds, then then turn on a green LED to signal the operator to load the next part.

I am trying to use a while loop to say that if the distance is greater than the max possible distance a part can be on the conveyor, to start the countdown then turn on the green LED, but if the distance is less than what the max distance could be, then break out of the while loop.

The break command does not seem to be working as intended however and once the green LED turns on, it does not turn off. I am certain I am doing this wrong, but I am not sure how.


//Defines Pin Numbers
const int trigPin = 2;
const int echoPin = 3;
const int redLED = 4;
const int greenLED = 7;
const int threshold = 42;
//Defines Variables
long duration;
int distance;



void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  digitalWrite(redLED, HIGH);
  digitalWrite(greenLED, LOW);

  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  // Measure distance converted to inches
  distance = ((duration*0.034/2)/2.54);

  while (distance > 42){
    delay(8000);
    digitalWrite(redLED, LOW);
    digitalWrite(greenLED, HIGH);
    if (distance < 42){
      break;
    }
  
  }
    

}

I was thinking that the If statement inside the while loop would check that. If a part passed by it on the conveyor, the distance would drop to less than 42 and break the loop.

If you don't have a distance= something in the while loop, it will not change.

In other words: you need to re-measure the changing distance whenever you what do do something based on the changed difference.

1 Like

but you are not re-calculating distance.

1 Like

So I would need to move this code that checks the distance from the sensor into the while loop?

digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  // Measure distance converted to inches
  distance = ((duration*0.034/2)/2.54);

Once you are in the loop, the variable called distance is never getting changed. It has the same value as when you entered the loop and you're stuck forever!

if (distance < 42){
      break;
    }

Once you are reading distance inside the loop and everything is working, that's not necessary because distance is checked at the top of the loop and the loop will only be repeated if >42.

Ther are times when it's helpful to break-out of a loop early or with a different condition, etc., but you shouldn't need it here.

1 Like

I'd move code out of the while loop and use the loop() itself.

Maybe take a look at:

...and consider that you want to measure the distance, set a timer, and control a couple lights based on the distance and timer.

I thought I had this coding correct, but it still does not seem to be working right. I'm trying to make an indicator to help set the rate for a manual feeding operation. The idea is to use an ultrasonic sensor to detect when a part passes by. After 8 seconds the red light turns off and the green light turns on to indicate that it is time to feed another part.
The code I have does not seem to be working though, the red LED just stays on. Is there a problem in my code that I'm not seeing?


//Defines Pin Numbers
const int trigPin = 2;
const int echoPin = 3;
const int redLED = 4;
const int greenLED = 7;
const int threshold = 42;
//Defines Variables
long duration;
int distance;


unsigned long countdownStart = 0;
const unsigned long countdownDur = 8000;


void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  Serial.begin(9600);
}

void loop() {

  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  // Measure distance converted to inches
  distance = ((duration*0.034/2)/2.54);

if(distance < 42) {
  digitalWrite(redLED, HIGH);
  digitalWrite(greenLED, LOW);
  countdownStart = 0;
} else {
  if (countdownStart == 0) {
    countdownStart = millis();

  }

  if (millis()- countdownStart >= countdownDur) {
    digitalWrite(redLED, LOW);
    digitalWrite(greenLED, HIGH);
  }
}
    
}

Check that this baud rate matches the setting in the IDE.

Yes it does

there is no print nor use of the Serial instance so that's not the issue

--
@galachad

➜ you can get rid of the Serial.begin() as you don't use Serial.

One possible issue is that you read your sensor too often and so might still be reading echos from the previous ping. (you send a ping, do some testing and the loop loops. The next time you come read the sensor, the distance could be seen as lower than 42 because the ultrasound are right there and so you turn the red led on again.)

I've seen people using 60 milliseconds between measurements as a good rule of thumb

I would recommend to make a state machine for the code and not use delay but for testing if that's the issue try adding

  delay(60);

at the end of the loop function

You should ALWAYS check for zero because that is what you get if pulsln() times-out.

Ummm, oops?

Your code:

image

is setting countdownStart = 0 if distance < 42.

This is not resetting the timer, as I believe you think it is. The way to reset (or hold reset) the timer is to constantly set it equal to millis(). That way the difference between them is zero. The way to make the timer run is to just stop resetting countdownStart.

if (distance < 42) {
    digitalWrite(redLED, HIGH);
    digitalWrite(greenLED, LOW);
    countdownStart = millis();
  } 
  else {
   // if (countdownStart == 0) {
   //    countdownStart = millis();
   // }

    if (millis() - countdownStart >= countdownDur) {
      digitalWrite(redLED, LOW);
      digitalWrite(greenLED, HIGH);
    }
  }

See it run in WOKWI: distance alarm - Wokwi ESP32, STM32, Arduino Simulator.

Clicking on the sensor in run mode brings up a slider to vary distance.

OP is using this variable as a flag to decide if it's the first time he enters in the else - then this is where he is acquiring the start value of millis which will be not null (it will kinda fail only if he is unlucky and millis() rolled over and is at 0 )

here is an example using a state machine and not polling the sensor too often

I added a buzzer which gives you an audio clue when the operator can slide in the next part. The buzzer will also emit a sound if a new part is positioned too early.

The serial monitor gives clues on what's going on

hopefully the state machine is pretty trivial to follow

click to see the code
//Defines Pin Numbers
const byte trigPin = 2;
const byte echoPin = 3;
const byte redLED = 4;
const byte buzzerPin = 5;
const byte greenLED = 7;


//Defines other constants and variables
const unsigned long threshold_cm = 106 ; // cm that is ~42 inches
unsigned long partDetectionStartTime = 0;
unsigned long partDetectionEndTime = 0;
const unsigned long idleDelay = 3000;

enum {READY_TO_FEED, PART_PRESENT, WAITING} state = READY_TO_FEED;

unsigned long distance() {
  static unsigned long previousDistance_cm = 0;
  const unsigned long delayBetweenPings = 60;
  static unsigned long previousPing = -delayBetweenPings;

  if (millis() - previousPing >= delayBetweenPings) {
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    previousPing = millis();
    previousDistance_cm = pulseIn(echoPin, HIGH) * 0.0343 / 2;
  }
  return previousDistance_cm;
}

void signalReady(bool ready) {
  if (ready) {
    digitalWrite(redLED, LOW);
    digitalWrite(greenLED, HIGH);
  } else {
    digitalWrite(redLED, HIGH);
    digitalWrite(greenLED, LOW);
  }
}

void setup() {
  pinMode(buzzerPin, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  signalReady(true);
  Serial.begin(115200);
}

void loop() {

  switch (state) {
    case READY_TO_FEED: // check if a part is detected
      if (distance() < threshold_cm) {
        partDetectionStartTime = millis();
        signalReady(false);
        state = PART_PRESENT;
        // debug
        Serial.println(F("Part in front of the sensor."));
      }
      break;

    case PART_PRESENT: // check if the part is gone
      if (distance() >= threshold_cm) { // consider an hysteresis
        partDetectionEndTime = millis();
        state = WAITING;
        // debug
        Serial.print(F("The part stayed "));
        Serial.print((partDetectionEndTime - partDetectionStartTime) / 1000.0, 2);
        Serial.println(F("s in front of the sensor."));
      }
      break;

    case WAITING: // should wait for idleDelay
      if (millis() - partDetectionEndTime >=  idleDelay) { // did we wait long enough ?
        signalReady(true);
        state = READY_TO_FEED;
        // debug
        Serial.println(F("OK, we waited long enough, get the next part."));
        tone(buzzerPin, 1500, 50); delay(80);
        tone(buzzerPin, 500, 50); 
      }
      else if (distance() < threshold_cm) { // operator did not wait for the right amount of time. Complain loudly
        tone(buzzerPin, 3000, 1000);
        Serial.println(F("OPERATOR MISTAKE!! Please wait long enough to get the next part."));
        partDetectionStartTime = millis();
        signalReady(false);
        state = PART_PRESENT;
      }
      break;
  }
}

1 Like

Nice!

@galachad,

Your two or more topics on the same or similar subject have been merged.

Please do not duplicate your questions as doing so wastes the time and effort of the volunteers trying to help you as they are then answering the same thing in different places.

Please create one topic only for your question and choose the forum category carefully. If you have multiple questions about the same project then please ask your questions in the one topic as the answers to one question provide useful context for the others, and also you won’t have to keep explaining your project repeatedly.

Repeated duplicate posting could result in a temporary or permanent ban from the forum.

Could you take a few moments to Learn How To Use The Forum

It will help you get the best out of the forum in the future.

Thank you.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.