Plant watering system

Hello Everyone,

Im relaitive new to arduino,

starting a project to water my plant’s that are out of reache for watering.

So the idea is to read the moister of the soil. And then start a pump.

This sound simple and this part i get working.

But then. I need it to first read a water level if it is HIGH not enough water the pump wil run hot it must not start. And put out a LED.

If the pump starts it needs to stop after 2 seconds (because of overwatering.) i wand to put a timer in of 5 minutes and then start that pump again. I can not work with a delay, because i wand to water 6 to 8 plants with one arduino. And if the delay is 5 min it coult be pausible i run in to a loop. And some plants get no water.

Also is i work with a delay i wil not be able to read what moistere level the plants have in that 5 minute delay.

What hardware do i have, for my test i use a arduino nano. But i also have a arduino mega for more analog inputs.

8 pumps and 8 moistere sensors 8 relais to switch on the pumps.

The delay of 2 seconds for the pump wil be no problem.

The program that i do have is a put to gether from multiple examples.

Can anyone please help me?

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

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
int moisture1 = A0;
int moisture2 = A1;
int moisture3 = A2;
int moisture4 = A3;

// declare moisture values
int moisture1_value = 0 ;
int moisture2_value = 0 ;
int moisture3_value = 0 ;
int moisture4_value = 0 ;

// set water relays
int relay1 = 6;
int relay2 = 7;
int relay3 = 8;
int relay4 = 9;

//relay state    1:open   0:close
int relay1_state_flag = 0;
int relay2_state_flag = 0;
int relay3_state_flag = 0;
int relay4_state_flag = 0;


void setup()
{
  // initialize the LCD
  lcd.begin();

  // Turn on the blacklight and print a message.
  lcd.backlight();
    // declare relay as output
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);

}

void loop()
{



 float value1 = analogRead(A0);
  moisture1_value =map(value1,590,360,0,100); delay(20);
  if(moisture1_value<0){
    moisture1_value=0;
  }
  float value2 = analogRead(A1);
  moisture2_value =map(value2,600,360,0,100); delay(20);
  if(moisture2_value<0) {
    moisture2_value=0;
  }
  float value3 = analogRead(A2);
  moisture3_value =map(value3,600,360,0,100); delay(20);
  if(moisture3_value<0){
    moisture3_value=0;
  }
  float value4 = analogRead(A3);
  moisture4_value =map(value4,600,360,0,100); delay(20);
  if(moisture4_value<0) {
    moisture4_value=0;
  }
//aansturing pomp 1
  if (moisture1_value < 30)
  {
    digitalWrite(relay1, LOW);
    relay1_state_flag = 0;
    delay(1000);
    digitalWrite(relay1, HIGH);
  }
  else if (moisture1_value > 55)
  {
    digitalWrite(relay1, HIGH);
    relay1_state_flag = 1;
    delay(1000);
    digitalWrite(relay2, LOW);
  }
//aansturing pomp 2
  if (moisture2_value > 30)
  {
    digitalWrite(relay2, LOW);
    relay2_state_flag = 0;
    delay(50);
    
  }
  else if (moisture2_value < 55)
  {
    digitalWrite(relay2, HIGH);
    relay2_state_flag = 1;
    delay(50);
  }
//aansturing pomp 3
  if (moisture3_value < 30)
  {
    digitalWrite(relay3, LOW);
    relay3_state_flag = 0;
    delay(50);
    
  }
  else if (moisture3_value > 55)
  {
    digitalWrite(relay3, HIGH);
    relay1_state_flag = 1;
    delay(50);
  }
 //aansturing pomp 4
  if (moisture4_value < 30)
  {
    digitalWrite(relay4, LOW);
    relay4_state_flag = 0;
    delay(50);
    
  }
  else if (moisture4_value > 20)
  {
    digitalWrite(relay4, HIGH);
    relay4_state_flag = 1;
    delay(50);
  }


 lcd.setCursor(0,1);
 lcd.print(moisture1_value);
 lcd.setCursor(3,1);
 lcd.print("%");
  lcd.setCursor(4,1);
 lcd.print(moisture2_value);
 lcd.setCursor(7,1);
 lcd.print("%");
   lcd.setCursor(8,1);
 lcd.print(moisture3_value);
 lcd.setCursor(11,1);
 lcd.print("%");
  lcd.setCursor(12,1);
 lcd.print(moisture4_value);
 lcd.setCursor(15,1);
 lcd.print("%");
 

if(relay1_state_flag == LOW)
   {
  lcd.setCursor(0,0);
  lcd.print("1");
 } 
if(relay2_state_flag == LOW)
   {
  lcd.setCursor(1,0);
  lcd.print("2");
 } 
 if(relay3_state_flag == LOW)
   {
  lcd.setCursor(2,0);
  lcd.print("3");
 } 
 if(relay4_state_flag == LOW)
   {
  lcd.setCursor(3,0);
  lcd.print("4");
 } 

  delay(300000)
  lcd.clear();
}

There is a programming-method with which you can run multiple actions at different points in time and still run your code very fast for other things that must run fast.

It is called non-blocking timing.
You yourself already use the same principle in your daily life.
Here is a tutorial that explains how this works

not sure what this is saying, but it sounds like you only want to run one or more pumps for a limited amount of time as well as some period where the pumps need to be off between on cycles.

if so, for each pump

  • turn the pump on if the sensor is < threshold
  • capture a timestamp
  • use timer to turn pump off the the timer expires and capture timestamp
  • use a timer to prevent repeating until timer expires

does this sound correct?

you might benefit from studying state machines. Here is a small introduction to the topic: Yet another Finite State Machine introduction

The water level is a water tank where the 8 pumps are in.
I think what you say is the easy'st way to start the project

This sounds a little bit more advance,
but i wil have a dive in to it to learn the principle.

thanks for the answer

Here is a version of your code where the 5 minute-waiting is done none-blocking.

You seem to be a real beginner in coding.
So I recommend
Take a look into this tutorial:

Arduino Programming Course

It is easy to understand and has a good mixture between explaining important concepts and example-codes to get you going. So give it a try and report your opinion about this tutorial.

In this code-version I made a "small" structural change.
The lines of code that do switch on / off the pumps is moved into its own function.

You can see a function as a group of compenents that build a senseful sub-unit.
Your plant-watering system is build from subunits

  • pumps
  • tubing
  • tank
  • microcontroller
  • etc.

The pump is build from multiple parts
These parts together are the pump
And in this sense you have parts of your code that do

switch on off the pumps

an these parts are put into its own function with name
switchOnOffPumps()

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

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
int moisture1 = A0;
int moisture2 = A1;
int moisture3 = A2;
int moisture4 = A3;

// declare moisture values
int moisture1_value = 0 ;
int moisture2_value = 0 ;
int moisture3_value = 0 ;
int moisture4_value = 0 ;

// set water relays
int relay1 = 6;
int relay2 = 7;
int relay3 = 8;
int relay4 = 9;

//relay state    1:open   0:close
int relay1_state_flag = 0;
int relay2_state_flag = 0;
int relay3_state_flag = 0;
int relay4_state_flag = 0;

unsigned long my5MinuteTimer;

void setup() {
  // use the serial monitor to make visible what is going on in your code
  Serial.begin(115200);
  Serial.println("Setup-Start");

  // initialize the LCD
  //lcd.begin();
  lcd.init();
  Serial.println("lcd.begin() done");

  // Turn on the blacklight and print a message.
  lcd.backlight();
  // declare relay as output
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);

  my5MinuteTimer = millis(); // initialise variable "my5MinuteTimer" with actual runtime
}


void loop() {

  float value1 = analogRead(A0);
  moisture1_value = map(value1, 590, 360, 0, 100); delay(20);
  if (moisture1_value < 0) {
    moisture1_value = 0;
  }

  float value2 = analogRead(A1);
  moisture2_value = map(value2, 600, 360, 0, 100); delay(20);
  if (moisture2_value < 0) {
    moisture2_value = 0;
  }

  float value3 = analogRead(A2);
  moisture3_value = map(value3, 600, 360, 0, 100); delay(20);
  if (moisture3_value < 0) {
    moisture3_value = 0;
  }

  float value4 = analogRead(A3);
  moisture4_value = map(value4, 600, 360, 0, 100); delay(20);
  if (moisture4_value < 0) {
    moisture4_value = 0;
  }

  // HERE were your lines of code that do aansturing pomp 1-4
  // I moved them into their own function with name switchOnOffPumps()
  // CHECK if 300000 milliseconds = 5 minutes time have passed by
  if ( TimePeriodIsOver(my5MinuteTimer, 300000)  ) { // replaces your delay(300000)
  // only if REALLY 300000 milliseconds = 5 minutes time have passed by
  // execute the lines of code inside function with name "switchOnOffPumps()"
  switchOnOffPumps();
  }

  lcd.setCursor(0, 1);
  lcd.print(moisture1_value);
  lcd.setCursor(3, 1);
  lcd.print("%");
  lcd.setCursor(4, 1);
  lcd.print(moisture2_value);
  lcd.setCursor(7, 1);
  lcd.print("%");
  lcd.setCursor(8, 1);
  lcd.print(moisture3_value);
  lcd.setCursor(11, 1);
  lcd.print("%");
  lcd.setCursor(12, 1);
  lcd.print(moisture4_value);
  lcd.setCursor(15, 1);
  lcd.print("%");


  if (relay1_state_flag == LOW) {
  lcd.setCursor(0, 0);
    lcd.print("1");
  }

  if (relay2_state_flag == LOW) {
  lcd.setCursor(1, 0);
    lcd.print("2");
  }

  if (relay3_state_flag == LOW) {
  lcd.setCursor(2, 0);
    lcd.print("3");
  }

  if (relay4_state_flag == LOW) {
  lcd.setCursor(3, 0);
    lcd.print("4");
  }

  // no delay needed because the "delaying is done by the if-condition
  // if (TimePeriodIsOver(my5MinuteTimer, 300000)
  // delay(300000)
} // END of function loop()


void switchOnOffPumps() {
  //aansturing pomp 1
  if (moisture1_value < 30) {
    digitalWrite(relay1, LOW);
    relay1_state_flag = 0;
    delay(1000);
    digitalWrite(relay1, HIGH);
  }
  else if (moisture1_value > 55) {
    digitalWrite(relay1, HIGH);
    relay1_state_flag = 1;
    delay(1000);
    digitalWrite(relay2, LOW);
  }

  //aansturing pomp 2
  if (moisture2_value > 30) {
    digitalWrite(relay2, LOW);
    relay2_state_flag = 0;
    delay(50);

  }
  else if (moisture2_value < 55) {
    digitalWrite(relay2, HIGH);
    relay2_state_flag = 1;
    delay(50);
  }

  //aansturing pomp 3
  if (moisture3_value < 30) {
    digitalWrite(relay3, LOW);
    relay3_state_flag = 0;
    delay(50);

  }
  else if (moisture3_value > 55) {
    digitalWrite(relay3, HIGH);
    relay1_state_flag = 1;
    delay(50);
  }

  //aansturing pomp 4
  if (moisture4_value < 30) {
    digitalWrite(relay4, LOW);
    relay4_state_flag = 0;
    delay(50);

  }
  else if (moisture4_value > 20) {
    digitalWrite(relay4, HIGH);
    relay4_state_flag = 1;
    delay(50);
  }
  lcd.clear();

} // END of function switchOnOffPumps()



// easy to use helper-function for non-blocking timing
// explanation see here
// https://forum.arduino.cc/t/example-code-for-timing-based-on-millis-easier-to-understand-through-the-use-of-example-numbers-avoiding-delay/974017
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

I have done this to make it easier to understand that only that part of the code
that switches on/off the pumps gets executed only once every 5 minutes.
While all the other code is executed all the time without any pausing

Your code has three functional sub-units

  1. reading in moisture-values
  2. switching on / off pumps
  3. updating LCD

Here is a code-version where these three sub-units are placed each in their own function
If you start comparing the single lines of coe you will see it are the exact same lines of code as you wrote it. Just grouped into three functions
with the names

  1. readInMoistureValues()
  2. switchOnOffPumps()
  3. updateLCD()
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
int moisture1 = A0;
int moisture2 = A1;
int moisture3 = A2;
int moisture4 = A3;

// declare moisture values
int moisture1_value = 0 ;
int moisture2_value = 0 ;
int moisture3_value = 0 ;
int moisture4_value = 0 ;

// set water relays
int relay1 = 6;
int relay2 = 7;
int relay3 = 8;
int relay4 = 9;

//relay state    1:open   0:close
int relay1_state_flag = 0;
int relay2_state_flag = 0;
int relay3_state_flag = 0;
int relay4_state_flag = 0;



unsigned long my5MinuteTimer;

void setup() {
  // use the serial monitor to make visible what is going on in your code
  Serial.begin(115200);
  Serial.println("Setup-Start");

  // initialize the LCD
  //lcd.begin();
  lcd.init();
  Serial.println("lcd.begin() done");

  // Turn on the blacklight and print a message.
  lcd.backlight();
  // declare relay as output
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);

  my5MinuteTimer = millis(); // initialise variable "my5MinuteTimer" with actual runtime
}


void loop() {
  readInMoistureValues();
  
  if ( TimePeriodIsOver(my5MinuteTimer, 300000)  ) { // replaces your delay(300000)
    // only if REALLY 300000 milliseconds = 5 minutes time have passed by
    // execute the lines of code inside function with name "switchOnOffPumps()"
    switchOnOffPumps();
  }

  updateLCD();
} // END of function loop()



void readInMoistureValues() {
  float ADCvalue;
  ADCvalue = analogRead(A0);
  moisture1_value = map(ADCvalue, 590, 360, 0, 100); delay(20);
  if (moisture1_value < 0) {
    moisture1_value = 0;
  }

  ADCvalue = analogRead(A1);
  moisture2_value = map(ADCvalue, 600, 360, 0, 100); delay(20);
  if (moisture2_value < 0) {
    moisture2_value = 0;
  }

  ADCvalue = analogRead(A2);
  moisture3_value = map(ADCvalue, 600, 360, 0, 100); delay(20);
  if (moisture3_value < 0) {
    moisture3_value = 0;
  }

  ADCvalue = analogRead(A3);
  moisture4_value = map(ADCvalue, 600, 360, 0, 100); delay(20);
  if (moisture4_value < 0) {
    moisture4_value = 0;
  }
} // END of function readInMoistureValues()


void updateLCD() {
  lcd.setCursor(0, 1);
  lcd.print(moisture1_value);
  lcd.setCursor(3, 1);
  lcd.print("%");
  lcd.setCursor(4, 1);
  lcd.print(moisture2_value);
  lcd.setCursor(7, 1);
  lcd.print("%");
  lcd.setCursor(8, 1);
  lcd.print(moisture3_value);
  lcd.setCursor(11, 1);
  lcd.print("%");
  lcd.setCursor(12, 1);
  lcd.print(moisture4_value);
  lcd.setCursor(15, 1);
  lcd.print("%");


  if (relay1_state_flag == LOW) {
    lcd.setCursor(0, 0);
    lcd.print("1");
  }

  if (relay2_state_flag == LOW) {
    lcd.setCursor(1, 0);
    lcd.print("2");
  }

  if (relay3_state_flag == LOW) {
    lcd.setCursor(2, 0);
    lcd.print("3");
  }

  if (relay4_state_flag == LOW) {
    lcd.setCursor(3, 0);
    lcd.print("4");
  }
} // END of function updateLCD()


void switchOnOffPumps() {
  //aansturing pomp 1
  if (moisture1_value < 30) {
    digitalWrite(relay1, LOW);
    relay1_state_flag = 0;
    delay(1000);
    digitalWrite(relay1, HIGH);
  }
  else if (moisture1_value > 55) {
    digitalWrite(relay1, HIGH);
    relay1_state_flag = 1;
    delay(1000);
    digitalWrite(relay2, LOW);
  }

  //aansturing pomp 2
  if (moisture2_value > 30) {
    digitalWrite(relay2, LOW);
    relay2_state_flag = 0;
    delay(50);

  }
  else if (moisture2_value < 55) {
    digitalWrite(relay2, HIGH);
    relay2_state_flag = 1;
    delay(50);
  }

  //aansturing pomp 3
  if (moisture3_value < 30) {
    digitalWrite(relay3, LOW);
    relay3_state_flag = 0;
    delay(50);

  }
  else if (moisture3_value > 55) {
    digitalWrite(relay3, HIGH);
    relay1_state_flag = 1;
    delay(50);
  }

  //aansturing pomp 4
  if (moisture4_value < 30) {
    digitalWrite(relay4, LOW);
    relay4_state_flag = 0;
    delay(50);

  }
  else if (moisture4_value > 20) {
    digitalWrite(relay4, HIGH);
    relay4_state_flag = 1;
    delay(50);
  }
  lcd.clear();

} // END of function switchOnOffPumps()



// easy to use helper-function for non-blocking timing
// explanation see here
// https://forum.arduino.cc/t/example-code-for-timing-based-on-millis-easier-to-understand-through-the-use-of-example-numbers-avoiding-delay/974017
boolean TimePeriodIsOver (unsigned long & startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

here's another approach. less code. you're free to ignore it, as well as ask questions

see The C Programming Language, enum pg 39, arrays pg 32, struct pg 127, arrays of struct pg 132, struct initialization pg 133

the concept of using a table, the ctl[] array, makes the code easier to change parameters

all ctls start in the Off state. The tmr is false so they check the sensor immediatly. if below the threshold, the motor is turned on, and timer started by setting tmr to true and capturing a timestamp in ctl [n].msec.

when the state is on, it checks if tmr is true and if the current time, msec is greater than the timestamp, .msec by MsecPeriodOff. It also checks if the sensor is now above the Threshold

if either the timer expire or the sensor > Thresold, it turns the motor off but restarted the timer.

with the state On again, that case exits the loop early, i.e. continue if the timer hasn't expired, skipping the test if the sensor < Threshold

enum { Off = HIGH, On = LOW };

struct Ctl {
    const byte    PinSensor;
    const byte    PinPump;
    byte          state;
    const char   *desc;

    bool          tmr;
    unsigned long msec;
};

Ctl ctl [] = {
 //  sensor pump state   desc
    {    A0,  13,  Off, "Pump A" },
    {    A1,  12,  Off, "Pump B" },
    {    A2,  11,  Off, "Pump C" },
};

const int Nctl = sizeof(ctl)/sizeof(Ctl);

// -------------------------------------

const int Threshold = 200;

const unsigned long MsecPeriodOn  = 1000;
const unsigned long MsecPeriodOff = 2000;


// -----------------------------------------------------------------------------
void
loop (void)
{
    unsigned long msec = millis ();

    for (int n = 0; n < Nctl; n++)  {
        if (Off == ctl [n].state)  {
            // if timer on, wait for pump Off period to expire
            if (ctl [n].tmr && msec - ctl [n].msec < MsecPeriodOff)
                continue;
            ctl [n].tmr = Off;

            // check if pump needs to be turned on
            if (Threshold > analogRead (ctl [n].PinSensor))  {
                digitalWrite (ctl [n].PinPump, On);
                ctl [n].state = On;
                ctl [n].msec  = msec;
                ctl [n].tmr   = true;
                Serial.print   (ctl [n].desc);
                Serial.println (" On");
            }
        }

        else {
            // turn off pump if timer expired OR watered enough
            if ((ctl [n].tmr && msec - ctl [n].msec >= MsecPeriodOff)
                    || Threshold < analogRead (ctl [n].PinSensor))  {
                digitalWrite (ctl [n].PinPump, Off);
                ctl [n].state = Off;
                ctl [n].msec  = msec;
                ctl [n].tmr   = true;

                Serial.print   (ctl [n].desc);
                Serial.println (" Off");
            }
        }
    }

    delay (500);
}

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);

    for (int n = 0; n < Nctl; n++)  {
        pinMode      (ctl [n].PinPump, OUTPUT);
        digitalWrite (ctl [n].PinPump, Off);

        Serial.print   (ctl [n].desc);
        Serial.println (" initialized");
    }
}