Nothing else happens when the "while" loop is exited

Hey guys,

I’m currently working on my first project (an irrigation system) and I have no programming background, so I’m sure it’s a simple problem but I can’t figure out the solution.

I want to switch on a relay for a pump until the flow sensor signals that enough water has been dispensed.

This is the relevant part of my code:

unsigned long flowDose = WD * countsPerMl;

if (currentMillis - lastPump > pumpInterval && SH < TH) {
  while (flowCount - previousFlowCount < flowDose) {
    //digitalWrite(pumpPin, HIGH);
    Serial.print("On");
    Serial.print("      ");
    Serial.println(flowCount / countsPerMl);
    pumpState = 1;
  }
}

else {
    //digitalWrite(pumpPin, LOW);
    Serial.print("Off");
    Serial.print("      ");
    Serial.println(flowCount / countsPerMl);
    pumpState = 0;
}


if (previousPumpState > pumpState) {
  lastPump = currentMillis;
  previousFlowCount = flowCount;
}

previousPumpState = pumpState;

}

SH is the soil humidity as measured by the sensor and TH is the threshold at which the pump is supposed to be switched on. pumpInterval is the minimum time between two runs of the pump. WD is the amount of water that is to be dispensed by the pump.

The relay was acting a bit weird, so I outcommented that line and instead made it put out “on” and the ml count of the flow sensor through serial.

Now to my actual problem: This is the first time I’m using a “while” loop and I must be doing something wrong. As long as the expression in the parentheses is true, the while loop is not exited which is what I intended. But as soon as the expression becomes false, I want it to leave the while loop and continue with the rest of the code. This doesn’t seem to happen. My serial monitor first shows “off”, then the expression becomes true and it shows “on”. But when the expression becomes false again, the serial monitor just stops completely, nothing else is happening. I expected it to keep saying “off”.

Can anyone help me out?

Thanks a lot in advance!

It's difficult to help without seeing all the code. However, as a starting point, put a Serial.print() call after your if statement, but before the while loop and print out the variables that control the while loop. It is likely that they are not what you think they are.

You mean this segment?

while (flowCount - previousFlowCount < flowDose) {
    //digitalWrite(pumpPin, HIGH);
    Serial.print("On");
    Serial.print("      ");
    Serial.println(flowCount / countsPerMl);
    pumpState = 1;
  }

If that statement that flowCount - previousFlowCount < flowDose is true, then you’ll be stuck in that while forever. Nowhere in that while loop do any of those three things ever change. If they don’t change and they were true to get you into that loop, then they’ll still be true until you change them. If something inside that while loop changed the value of one of those three variables then it might be possible for that comparison to come up false and you’ll exit the loop. But if you keep the values that make it true and never change them then it will be true forever.

Delta_G:
If that statement that flowCount - previousFlowCount < flowDose is true, then you’ll be stuck in that while forever. Nowhere in that while loop do any of those three things ever change.

flowCount changes indirectly. In the finished version, the pump will be activated within the while loop and this will set off the flow sensor, so that flowCount increases. As of now, I’m just blowing into the flow sensor. This part seems to work, because I can see the flowCount increase on the serial monitor, even within the while loop.

I can post the whole code, but to be honest it’s a bit of a mess and there are many halfway-finished parts that don’t make sense as they are:

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce2.h>

#define I2C_ADDR    0x20  // Define I2C Address for controller
#define BACKLIGHT_PIN  7
#define En_pin  4
#define Rw_pin  5
#define Rs_pin  6
#define D4_pin  0
#define D5_pin  1
#define D6_pin  2
#define D7_pin  3

#define  LED_OFF  0
#define  LED_ON  1
LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);


byte l[8] = {
  B00100,
  B00100,
  B00100,
  B00100,
  B00100,
  B00100,
  B00011,
  B00000,
};

long lastThDecrease = 0;
long lastThIncrease = 0;
long lastWdDecrease = 0;
long lastWdIncrease = 0;
long previousMillis = 0;

long keyDelay = 1000;
long keyRepeat = 100;
long humidityReadout = 250;

const int wdupPin = 9;
const int wddownPin = 8;
const int thupPin = 7;
const int thdownPin = 6;
const int resetPin = 1;
const int pumpPin = 4;
const int flowPin = 2;

int pumpState = 0;
int previousPumpState = 0;
unsigned long pumpInterval = 10000;

float countsPerMl = 5.35;

volatile unsigned long flowCount;
unsigned long lastFlowCount;


/*
int wdupState;
int wddownState;
int thupState;
int thdownState;
int resetState;
*/

int previousWdupState;
int previousWddownState;
int previousThupState;
int previousThdownState;

unsigned long lastWdupPush;
unsigned long lastWddownPush;
unsigned long lastThupPush;
unsigned long lastThdownPush;
//unsigned long lastWD;


int WD = 500;
int TH = 60;
int N = 3;
int SH;

unsigned long lastPump = 0;
unsigned long previousFlowCount = 0;

long debounceDelay = 20;

Bounce wdupBounce = Bounce();
Bounce wddownBounce = Bounce();
Bounce thupBounce = Bounce(); 
Bounce thdownBounce = Bounce();
Bounce resetBounce = Bounce();










 void setup() {
   
  Serial.begin(9600);
  
  pinMode(wdupPin, INPUT);
  pinMode(wddownPin, INPUT);
  pinMode(thupPin, INPUT);
  pinMode(thdownPin, INPUT);
  pinMode(resetPin, INPUT);
  pinMode(flowPin, INPUT);
  pinMode(pumpPin, OUTPUT);

  attachInterrupt(0, flow, FALLING);
  
  digitalWrite(pumpPin, LOW);


  wdupBounce.attach(wdupPin);
  wdupBounce.interval(debounceDelay);

  wddownBounce.attach(wddownPin);
  wddownBounce.interval(debounceDelay);
   
  thupBounce.attach(thupPin);
  thupBounce.interval(debounceDelay);
  
  thdownBounce.attach(thdownPin);
  thdownBounce.interval(debounceDelay);
  
 // resetBounce.attach(resetPin);
 // resetBounce.interval(debounceDelay);

  
  // Display
  
  lcd.begin(16, 2);
  
  lcd.createChar(0, l);

  
  lcd.setCursor(0, 0);
  lcd.print("SH:");
  
  lcd.setCursor(8, 0);
  lcd.print("TH:");
  
  lcd.setCursor(0, 1);
  lcd.print("WU:");

  lcd.setCursor(8, 1);
  lcd.print("WD:"); 
  
}




void flow () {
  flowCount += 1;
}




void loop() {
  
//Measure and display soil humidity

  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis > humidityReadout) {
    previousMillis = currentMillis;
    
    SH = analogRead(0);
    SH = map(SH, 0, 1023, 100, 0);

    
    lcd.setCursor(3, 0);
    lcd.print(SH);
    lcd.print("% ");
    
    if (SH < TH) {
      digitalWrite(pumpPin, HIGH);
    }
    else {
      digitalWrite(pumpPin, LOW);
    }
    }








//Displaying and setting the threshold
  
  //Threshold up
  
  thupBounce.update();

  int thupState = thupBounce.read();
  
  if (thupState == LOW) {
    previousThupState = LOW;
  }
    
  if (thupState == HIGH && previousThupState == LOW) {
      lastThupPush = currentMillis;
      previousThupState = HIGH;
     if (TH < 100) {
       TH = TH + 1;
     }
  }

  if (thupState == HIGH && previousThupState == HIGH && TH < 100 && (currentMillis - lastThIncrease) > keyRepeat && (currentMillis - lastThupPush) > keyDelay) {
      TH = TH + 1;
      lastThIncrease = currentMillis;
  }
  
  
  //Threshold down

  thdownBounce.update();

  int thdownState = thdownBounce.read();
  
  if (thdownState == LOW) {
    previousThdownState = LOW;
  }
    
  if (thdownState == HIGH && previousThdownState == LOW) {
      lastThdownPush = currentMillis;
      previousThdownState = HIGH;
     if (TH > 0) {
       TH = TH - 1;
     }
  }

  if (thdownState == HIGH && previousThdownState == HIGH && TH > 0 && (currentMillis - lastThDecrease) > keyRepeat && (currentMillis - lastThdownPush) > keyDelay) {
      TH = TH - 1;
      lastThDecrease = currentMillis;
  }
  

  lcd.setCursor(11, 0);
  lcd.print(TH);
  lcd.print("% ");
  
  
  
  
  
 
  
  
//Displaying total water usage

  unsigned long WU = WD * N;

  float WUL = WU * 0.001;

  if (WU < 10000) {
  lcd.setCursor(3, 1);
  lcd.print(WUL,1);
  lcd.write(byte(0));
  lcd.print(" ");
  }
  else {
  lcd.setCursor(3, 1);
  lcd.print(WUL,0);
  lcd.write(byte(0));
  lcd.print(" ");
  }
  
  
  
 
  
  
  
  
  
//Displaying and setting the water dosage

  //WD up

  wdupBounce.update();

  int wdupState = wdupBounce.read();
  
  if (wdupState == LOW) {
    previousWdupState = LOW;
  }
    
  if (wdupState == HIGH && previousWdupState == LOW) {
      lastWdupPush = currentMillis;
      previousWdupState = HIGH;
     if (WD < 10000) {
       WD = WD + 100;
     }
  }

  if (wdupState == HIGH && previousWdupState == HIGH && WD < 10000 && (currentMillis - lastWdIncrease) > keyRepeat && (currentMillis - lastWdupPush) > keyDelay) {
      WD = WD + 100;
      lastWdIncrease = currentMillis;
  }
  
  // WD down

  wddownBounce.update();

  int wddownState = wddownBounce.read();
  
  if (wddownState == LOW) {
    previousWddownState = LOW;
  }
    
  if (wddownState == HIGH && previousWddownState == LOW) {
      lastWddownPush = currentMillis;
      previousWddownState = HIGH;
     if (WD > 0) {
       WD = WD - 100;
     }
  }

  if (wddownState == HIGH && previousWddownState == HIGH && WD > 0 && (currentMillis - lastWdDecrease) > keyRepeat && (currentMillis - lastWddownPush) > keyDelay) {
      WD = WD - 100;
      lastWdDecrease = currentMillis;
  }
  
  float WDL = WD * 0.001;
  
  lcd.setCursor(11, 1);
  lcd.print(WDL,1);
  lcd.write(byte(0));
  lcd.print(" ");
  
  
  
  
  
  
  
  
// Pump

unsigned long flowDose = WD * countsPerMl;

if (currentMillis - lastPump > pumpInterval && SH < TH) {
  while (flowCount - previousFlowCount < flowDose) {
    //digitalWrite(pumpPin, HIGH);
    Serial.print("On");
    Serial.print("      ");
    Serial.println(flowCount / countsPerMl);
    pumpState = 1;
  }
}

else {
    //digitalWrite(pumpPin, LOW);
    Serial.print("Off");
    Serial.print("      ");
    Serial.println(flowCount / countsPerMl);
    pumpState = 0;
}


if (previousPumpState > pumpState) {
  lastPump = currentMillis;
  previousFlowCount = flowCount;
}

previousPumpState = pumpState;

}

Do you mean it gets changed by an interrupt? Then it should be declared volatile or it still won't change in the context of that loop.

If there is still more to the problem that you don't want to show then all I can do is wish you luck. The problem I pointed out is the problem in the code you posted. If you've got more code that you don't want to post then have fun with it, but please don't ask for help if you're not going to provide the information needed to help you.

In order to exit the while loop, something needs to break the condition, if this never happens, youll be stuck in the while forever - essentially and infinite loop.

Chances are, whatever you think you are doing, isnt what you are actually doing

step through each line at a time, and follow it through slowly.

I posted all of my code. My project is far from finished, that’s why there are still some missing parts. I’m not holding anything back.

flowCount is in fact declared as a volatile and it does change within the while loop. I think if there was a problem exiting the while loop, the statements would be repeated indefinitely. This is not the case, though. Once flowCount is so high that “flowCount - previousFlowCount < flowDose” becomes false, the repeated “On” on the serial monitor stops. But I would expect to see “Off” instead and that just doesn’t happen. Nothing happens, there is just no more output to the serial monitor.

English is not my native language, so maybe that’s why there is some confusion. I’m trying my best.

I have looked (briefly) at the code in Reply #3

You have everything stuffed into loop(). Technically that can work, but it makes it very difficult for a human to figure out what is going on.

Look at the Thread planning and implementing a program which illustrates how to put different activities into separate functions and keep the logic of the project separate from the actions.

If each piece is in its own function it can be tested separately and parts that are known to work can be ignored when searching for a problem elsewhere.

...R

Could this condition:

if (currentMillis - lastPump > pumpInterval && SH < TH)

still be true even when the flowCount is high enough to make the while loop condition false? In that case you’d go to the while loop but it is already false so it never runs and never prints “ON”, but since the if statement was true it never goes to the else and never prints “OFF”.

Throw a print line in between that if statement and the while loop and see if that keeps getting printed.

Robin2:
Look at the Thread planning and implementing a program which illustrates how to put different activities into separate functions and keep the logic of the project separate from the actions.

Thank you so much for this thread! My code is a mess and it often takes me ages to find the right line to change or fix something. I haven’t worked all the way through it yet, but so far it seems like you made that thread with people like me in mind…

Delta_G:
Could this condition:

if (currentMillis - lastPump > pumpInterval && SH < TH)

still be true even when the flowCount is high enough to make the while loop condition false?

Yes, it absolutely can.

In that case you’d go to the while loop but it is already false so it never runs and never prints “ON”, but since the if statement was true it never goes to the else and never prints “OFF”.

And that’s the solution, thank you so much! I feel really stupid, should have figured that out by myself.

I think I’ll clean up my code and try to rewrite that part so it actually works.

Once again, thank you all for your help. I can find and fix 99% of my errors but sometimes I just get stuck. It’s great to have some help when I need it and I’ll try to pass it on once I feel like know what I’m doing.