Misbehaving automatic watering system

I’m a beginner that is working on an automatic watering system based on Arduino. The hardware mainly consists of an Arduino Mega, moisture sensors, two servos, lcd screen and a water pump. The sketch, and this is where I think the problem lies, makes the hardware act in an unwanted way.

I want the sketch to check if any plant is dry, then water the plant one time if it is dry, and then wait 5 minutes before doing the same void loop again.

What I think the software is doing right now, is stopping where any of the if statements of the sketch is met (plant is dry) and then repeating that same if statement until it is not met. I’ll put my code down here and I’ll be thankful for any help I can get.

P.S. The last thing I did was trying to solve the issue by putting “return;” in the end of each if statement, hoping it would make the sketch “jump out” of the if statement and continue down the code. That didn’t work. Maybe it makes the sketch start from the beginning of the void loop?

#include <PCD8544.h>

#include <Servo.h>

//create servo objects
Servo horizontalServo;  
Servo verticalServo;

static PCD8544 lcd;

//Define the moisture sensor pins
const int analogInPin0 = A0;   
const int analogInPin1 = A1;
const int analogInPin2 = A2;
const int analogInPin3 = A3;
const int analogInPin4 = A4;


//variables that gather the moisture sensor data
int moistureValue0 = 0;        
int moistureValue1 = 0;
int moistureValue2 = 0;
int moistureValue3 = 0;
int moistureValue4 = 0;

//parameters for each plant
const int hServoPositionA0 = 9;
const int vServoPositionA0 = 121;
const int triggerValueA0 = 20;
const int wateringTimeA0 = 4000;

const int hServoPositionA1 = 28;
const int vServoPositionA1 = 128;
const int triggerValueA1 = 30;
const int wateringTimeA1 = 4000;

const int hServoPositionA2 = 78;
const int vServoPositionA2 = 137;
const int triggerValueA2 = 30;
const int wateringTimeA2 = 4000;

const int hServoPositionA3 = 120;
const int vServoPositionA3 = 129;
const int triggerValueA3 = 30;
const int wateringTimeA3 = 4000;

const int hServoPositionA4 = 164;
const int vServoPositionA4 = 121;
const int triggerValueA4 = 30;
const int wateringTimeA4 = 4000;

//water pump control pins
const int pumpAnodePin = 11;
const int pumpCathodePin = 12;

const int lcdBacklight = 13;

void setup() {
 
 // set resolution of the lcd screen
  lcd.begin(84, 48);
  
  // attach the servo on pin 9 to the servo object
  // attaches the servo on pin 10 to the servo object
  horizontalServo.attach(10);  
  verticalServo.attach(9);

  //set the pins to either input or output
  pinMode(analogInPin0, INPUT);
  pinMode(analogInPin1, INPUT);  
  pinMode(analogInPin2, INPUT);  
  pinMode(analogInPin3, INPUT);  
  pinMode(analogInPin4, INPUT);     
  
  pinMode(lcdBacklight, OUTPUT);

  pinMode(pumpAnodePin, OUTPUT);
  pinMode(pumpCathodePin, OUTPUT);

  digitalWrite(lcdBacklight, LOW);

  digitalWrite(pumpAnodePin, LOW);
  digitalWrite(pumpCathodePin, LOW);
   

}


void loop() {
    
    //gather moisture sensor data
    moistureValue0 = analogRead(analogInPin0);  
    moistureValue1 = analogRead(analogInPin1);
    moistureValue2 = analogRead(analogInPin2);
    moistureValue3 = analogRead(analogInPin3);
    moistureValue4 = analogRead(analogInPin4);

    //convert gathered data to a 0-100 scale
    moistureValue0 = map(moistureValue0, 0, 1023, 100, 0);  
    moistureValue1 = map(moistureValue1, 0, 1023, 100, 0);
    moistureValue2 = map(moistureValue2, 0, 1023, 100, 0);
    moistureValue3 = map(moistureValue3, 0, 1023, 100, 0);
    moistureValue4 = map(moistureValue4, 0, 1023, 100, 0);

  // Writing the moisture values on the LCD
  lcd.setCursor(20, 0);
  lcd.print("Moisture %");


  lcd.setCursor(0, 1);
  lcd.print("A0:");
  lcd.print(moistureValue0);
  lcd.print("  ");
  

  lcd.setCursor(0, 2);
  lcd.print("A1:");
  lcd.print(moistureValue1);
  lcd.print("  ");
  

  lcd.setCursor(0, 3);
  lcd.print("A2:");
  lcd.print(moistureValue2);
  lcd.print("  ");
  
  lcd.setCursor(0, 4);
  lcd.print("A3:");
  lcd.print(moistureValue3);
  lcd.print("  ");
  

  lcd.setCursor(0, 5);
  lcd.print("A4:");
  lcd.print(moistureValue4);
  lcd.print("  ");

  
  //comparing gathered moisture values with the trigger values for each respective plant
  if (moistureValue0<triggerValueA0)
    {
      horizontalServo.write(hServoPositionA0);
      delay(500);
      verticalServo.write(vServoPositionA0);
      delay(500);
      digitalWrite(pumpAnodePin, HIGH);
      delay(wateringTimeA0);
      digitalWrite(pumpAnodePin, LOW);
      return; //I thought return made the program/sketch exit the if statement and continued down the code but it seems that it does not.
      }

  if (moistureValue1<triggerValueA1)
    {
      horizontalServo.write(hServoPositionA1);
      delay(500);
      verticalServo.write(vServoPositionA1);
      delay(500);
      digitalWrite(pumpAnodePin, HIGH);
      delay(wateringTimeA1);
      digitalWrite(pumpAnodePin, LOW);
      return;
      }

  if (moistureValue2<triggerValueA2)
    {
      horizontalServo.write(hServoPositionA2);
      delay(500);
      verticalServo.write(vServoPositionA2);
      delay(500);
      digitalWrite(pumpAnodePin, HIGH);
      delay(wateringTimeA2);
      digitalWrite(pumpAnodePin, LOW);
      return;
      }

  if (moistureValue3<triggerValueA3)
    {
      horizontalServo.write(hServoPositionA3);
      delay(500);
      verticalServo.write(vServoPositionA3);
      delay(500);
      digitalWrite(pumpAnodePin, HIGH);
      delay(wateringTimeA3);
      digitalWrite(pumpAnodePin, LOW);
      return;
      }

  if (moistureValue4<triggerValueA4)
    {
      horizontalServo.write(hServoPositionA4);
      delay(500);
      verticalServo.write(vServoPositionA4);
      delay(500);
      digitalWrite(pumpAnodePin, HIGH);
      delay(wateringTimeA4);
      digitalWrite(pumpAnodePin, LOW);
      return;
      }

  //center the servos
  horizontalServo.write(100);
  verticalServo.write(110);

  //wait 5 minutes before doing the loop again
  delay(300000);
  
}

Well, it's nicely laid out code so we can all ready it easily. :slight_smile:

The 'return' does, in fact, jump out of the main LOOP but then comes back in at the top again and just repeats the same steps all over again. It's certainly poor programming practice to do it this way - better to put each watering block into its own method and then calling that from the LOOP, once for each plant or until you get a response from the underlying method indicating it has started the watering process so you can skip the remaining plant checks and wait for 5 mins (at least, this is what I believe you are trying to do).

I'd also put a few Serial.print statement in there (I know you have the LCD to help) as it's always useful to see what is actually going on compared to what you think is happening!

BTW did you intentionally map your moisture value 0-1023 to a reverse scale 100 to 0? Just askin' :slight_smile:

So just remove all the returns. The first time through it will probably water all of the plants one after the other. Then when they have enough water, it will only water one or two per 5-minute cycle.

Ralph_S_Bacon:
Well, it's nicely laid out code so we can all ready it easily. :slight_smile:

The 'return' does, in fact, jump out of the main LOOP but then comes back in at the top again and just repeats the same steps all over again. It's certainly poor programming practice to do it this way - better to put each watering block into its own method and then calling that from the LOOP, once for each plant or until you get a response from the underlying method indicating it has started the watering process so you can skip the remaining plant checks and wait for 5 mins (at least, this is what I believe you are trying to do).

I'd also put a few Serial.print statement in there (I know you have the LCD to help) as it's always useful to see what is actually going on compared to what you think is happening!

BTW did you intentionally map your moisture value 0-1023 to a reverse scale 100 to 0? Just askin' :slight_smile:

Thanks for the tips. Is a method the same thing as a function? I haven't started with those things yet. I guess the next step is to slim the code and make it better and more efficient. Right now though, I just want it to work.

Yeah, I reversed the scale when I found out that lower readings on the 0-1023 scale meant wet and higher readings meant dry.

MorganS:
So just remove all the returns. The first time through it will probably water all of the plants one after the other. Then when they have enough water, it will only water one or two per 5-minute cycle.

The moisture sensor does not pick up the new moisture level instantly - sometimes it can take a minute or two for the new moisture value to stabilize itself. This is why I want the if statement to only be executed once every 5 minutes, i.e. spray a plant once and then wait a bit for a new moisture value.

The thing that is happening with the code I posted above, is that the hardware soaks the plants with too much water.

I’ll try your advice. Just so that I understand: an if statement in my code will only run once every 5 minutes if I take away the return?

KJohansson:
Is a method the same thing as a function? I haven't started with those things yet. I guess the next step is to slim the code and make it better and more efficient. Right now though, I just want it to work.

Well, I call them methods but that's from C# not C++ so here they tend to call them functions. I would try and put something in a function like this, it is VERY easy to do:

int waterPlantOne(long secondsToWater) {
   Put your code here
}

where the 'int' is what you are returning to your calling loop, and the secondsToWater is a parameter you are passing to the function. You can pass several parameters, just define each one according to the data type.

If you don't want to pass a reply back to the loop, instead of 'int' in the above example (or any other data type) use 'void'. But it might be good to pass back some sort of 'OK' or 'FAIL' value via the int (or whatever, eg 0=OK 1= FAIL).

To call the function in your loop, using the example above you would use:

void Loop() {
   int reply = waterPlantOne(10);

   // Then do something whether it failed or succeeded
}

Yeah, I reversed the scale when I found out that lower readings on the 0-1023 scale meant wet and higher readings meant dry.

Yes, most resistance when dry (1023), same as my rain sensor.