0

I am currently working an automatic home gardening system. There are two moisture sensors and two solenoid valves to allow water to plant two different zones. Preferably, I want to water the plants for x amount of time. I started programming this and I noticed that with my code below, if one solenoid valve goes off, the entire program will delay until one of the solenoid valves closes and thus the other solenoid valve will not open until the other one is closed.

However, I want to prevent this from happening and I want the program to continue reading the moisture readings while one of the solenoid valves is open so that the other solenoid valve can open whenever the zone needs water as well.

int solenoidPin1 = 1; //connected to Digital 1
int solenoidPin2 = 2; //connected to Digital 2
void setup(){
  pinMode(solenoidPin1, OUTPUT);
  pinMode(solenoidPin2, OUTPUT);
}
void loop(){
  int moisture1 = analogRead(moisturePin1);
  int moisture2 = analogRead(moisturePin2);
  if (moisture1 <= 250){ //if moisture is less than reading 250
  digitalWrite(solenoidPin1, HIGH); //switch solenoid ON
  delay(300000); //water for five minutes
  digitalWrite(solenoidPin1, LOW); //switch solenoid OFF
  }
  else {                     
  digitalWrite(solenoidPin1, LOW); //switch solenoid OFF
  }

  if (moisture2 <= 250){ //if moisture is less than reading 250
  digitalWrite(solenoidPin2, HIGH); //switch solenoid ON
  delay(300000); //water for five minutes
  digitalWrite(solenoidPin2, LOW); //switch solenoid OFF
  }
  else {                     
  digitalWrite(solenoidPin2, LOW); //switch solenoid OFF
  }
}

I was thinking that I should do the following for the conditioning part. Will this work instead for what I'm trying to do?

  if (moisture1 <= 250){ //if moisture is less than reading 250
  digitalWrite(solenoidPin1, HIGH); //switch solenoid ON
  }
  else if (moisture1 >= 550){                    
  digitalWrite(solenoidPin1, LOW); //switch solenoid OFF
  }

  if (moisture2 <= 250){ //if moisture is less than reading 250
  digitalWrite(solenoidPin2, HIGH); //switch solenoid ON
  }
  else if (moisture2 >= 550){
  digitalWrite(solenoidPin2, LOW); //switch solenoid OFF
  }

Note: I'm using a capacitive moisture sensor, so the output voltage/reading is higher the drier the dirt is.

3

The essence of your problem is that the delay() function blocks your program until the delay is over. This is only acceptable if the program is doing one single things at a time. If you need to do multiple things in parallel, like monitoring two moisture sensors, this approach does not work.

Then, the first thing is to get read of delay() and instead use millis() for managing time in a non blocking fashion. The essence of this approach is to never wait. Instead, if it is time to do something right now, you do it. If not, you just don't do it this time, but you don't wait. This is nicely covered in the Blink without delay Arduino tutorial. You should get familiar with this technique, as it is essential for many Arduino projects.

Your problem is slightly more complex than blinking an LED though, as the valve can be in two different states, and the action to perform depends on that state. This is managed by a software construct called a finite state machine. Essentially you have to remember the current state and, depending on it, you decide what to do and what the next state should be. Here is how you could do the timed watering with this approach:

static enum { OFF, ON } stateValve1, stateValve2;
static uint32_t startedValve1, startedValve2;  // time when started

if (stateValve1 == OFF && analogRead(moisturePin1) <= 250) {
    digitalWrite(solenoidPin1, HIGH); //switch solenoid ON
    startedValve1 = millis();
    stateValve1 = ON;
} else if (stateValve1 == ON
        && millis() - startedValve1 >= WATERING_TIME) {
    digitalWrite(solenoidPin2, LOW); //switch solenoid OFF
    stateValve1 = OFF;
}

// and the same for valve 2.

For more in-depth coverage of the subject, I recommend you read the Majenko's tutorial on finite state machines

At this point, the next step would be to throw in some modularization in order to make the code less repetitive. I suggest making a class for representing a “timed valve”. The valve has a turnOn() method that turns it on, but does nothing if the valve is actually already on. The class takes care of turning the valve off when the time has elapsed. But since it cannot do that all by itself, you have to call an update() method periodically to give it a chance to check whether it's time to go off. Here is my implementation of that class:

const uint32_t WATERING_TIME = 100000;  // for example

class TimedValve
{
public:
    TimedValve(uint8_t pin) : valvePin(pin), state(OFF) {}
    void turnOn() {
        if (state == ON) return;  // do nothing if already ON
        digitalWrite(valvePin, HIGH);
        state = ON;
        timeStarted = millis();
    }
    void update() {
        if (state == ON && millis() - timeStarted >= WATERING_TIME) {
            digitalWrite(valvePin, LOW);
            state = OFF;
        }
    }
private:
    uint8_t valvePin;
    enum { OFF, ON } state;
    uint32_t timeStarted;
};

And you would use it like this:

const int solenoidPin1 = 1;
const int solenoidPin2 = 2;
const int moisturePin1 = A1;
const int moisturePin2 = A2;

void setup(){
    pinMode(solenoidPin1, OUTPUT);
    pinMode(solenoidPin2, OUTPUT);
}

void loop()
{
    static TimedValve valve1(solenoidPin1), valve2(solenoidPin2);

    // Turn the valves ON if needed.
    if (analogRead(moisturePin1) <= 250)
        valve1.turnOn();
    if (analogRead(moisturePin2) <= 250)
        valve2.turnOn();

    // Let them turn OFF if it's time.
    valve1.update();
    valve2.update();
}
  • Excellent. Thank you so much for you help. I had no idea about the Arduino being finite state machine. I also didn't know you could create an "object" with the Arduino IDE. Interesting... – KingDuken Mar 20 '17 at 22:56
  • @KingDuken: Any computer is, in a sense, a finite state machine (an “FSM” for short). But here, I am not using the term to describe what the Arduino actually is, but rather as a name for a software construct, i.e. a way to write a program. This construct could be implemented on any computer, big or small. The Wikipedia article I linked to describes the FSM as an abstract model of computation, whereas Majenko's tutorial is a good introduction to the FSM as a software construct, with a bias towards Arduino programming. – Edgar Bonet Mar 21 '17 at 8:34
  • I finally managed to get around and test this out. AND IT WORKS! However, I wasn't able to use D1 because using the TX pin isn't exactly ideal. I don't know why but it worked when I used a different digital pin. Thank you again for your help! – KingDuken Mar 31 '17 at 22:11
  • There is one slight problem. I need it delay before the water cycle because the moisture sensor is really sensitive and if it detects water before the water settles in the soil, it will turn off the water valve. – KingDuken Mar 31 '17 at 22:32
  • @KingDuken: I can't understand your last comment. With this program, the moisture sensor controls the opening of the valve, but not the closing: the valve closes only after a fixed watering time. The sensor cannot “turn off the water valve”. – Edgar Bonet Apr 1 '17 at 7:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.