Sprinkler System with monitoring

Hi,
This is my first post here.
I have recently bought an Uno and successfully programmed it to operate the solenoid valves for an eight zone sprinkler system.

Now I am trying to improve the system by implementing a moisture sensor.

The problem is the program above is using a delay() function so the valves will operate sequentially i.e. when one valve is ON all other valves will on OFF position until the first valve past that delay.

But I need to convert the delay() into millis() if I need to implement the sensor.

Is there anyway that I can implement the following without the delay().

Assuming three valves and one sensor;

Valve_1 programmed for 5mins
Valve_2 programmed for 3mins
Valve_3 programmed for 7mins

program starts:
valve 1 --> ON
uno monitoring the sensor continuously
5mins past
valve 1 --> OFF
valve 2 --> ON
uno monitoring the sensor continuously
3mins past
valve 2 --> OFF
valve 3 --> ON
uno monitoring the sensor continuously
7mins past
valve 3 --> OFF
All velves OFF until reset.

After several attempt my current thought is to have two arduino's.

One will monitor all the sensors and feed into the second, second will control the valves.

Hello and welcome :slight_smile:

Edit: Ok I think I have something that is close to what you want:

char str[32];

unsigned long
  time_now    = 0,
  time_past   = 0;

const uint8_t max_valves = 3;

const unsigned long valves_delays[ max_valves ] =
{
  60UL * 1000 * 5,
  60UL * 1000 * 3,
  60UL * 1000 * 7
};
  
uint8_t current_valve_id = 0; // first valve is id 0, not 1...

void setup()
{
  time_past = millis();
  current_valve_id = 0;
  EnableValve( current_valve_id );
}

void loop()
{
  if ( current_valve_id < max_valves )
  {
    time_now = millis();

    if ( time_now - time_past >= valves_delays[ current_valve_id ] )
    {
      time_past = time_now;

      DisableValve( current_valve_id );

      if ( ++ current_valve_id < max_valves )
        EnableValve( current_valve_id );
    }
  }
}

void DisableValve( uint8_t valve_id )
{
  sprintf( str, "Valve ID %d disabled\n", valve_id);
  Serial.print( str );
}

void EnableValve( uint8_t valve_id )
{
  sprintf( str, "Valve ID %d enabled\n", valve_id);
  Serial.print( str );
}

(Note: untested on Arduino, I’ve made this in another language (PAWN) so I could test on my PC… :))

Edited once again to make it work exactly as you described ;)…

I think this will help, I will try and post back..
http://playground.arduino.cc//Code/Scheduler

Valve_1 --> time_1
Valve_2 --> time_2
Valve_3 --> time_3

check v1(){
if time_1 > 0;{
valve_1 --> ON;
schedule valve_1 --> OFF in time_1;
}
check v2()
}

check v2(){
if time_2 > 0;{
schedule valve_2 --> ON in time_1;
schedule valve_2 --> OFF in time1 + time_2;
}
check v3()
}

void loop(){
scheduler.update();
if (Serial.available()){
check
Serial.flush();
}
}

void loop
If Serial available (
check v1;
)

......

const unsigned long valves_delays[] =
{
  60 * 1000 * 5,
  60 * 1000 * 3,
  60 * 1000 * 7
};

Despite the fact that the destination type is long, the intermediate values are all int (60, 1000, 5, 3, and 7 are ints). The calculations will, therefore be carried out using int logic, which will fail, because 60 * 1000 is 60000 which does not fit in an int.

At least one of the values in each calculation needs to be a non-int type.

const unsigned long valves_delays[] =
{
  60UL * 1000 * 5,
  60 * 1000UL * 3,
  60 * 1000 * 7UL
};

Doesn’t matter which one, or if all of them are.

Ok, thanks for correction I didn't know that, I thought the compiler was smarter than that :slight_smile:

Thanks guix,

I will try the code and let you know,

on the following though, is the >= correct or should it be just >

 if ( current_valve_id ++ >= 2 )
      current_valve_id = 0;

Thanks,

Well it was correct, but not for what you wanted to do..my code did loop like: 0, 1, 2, 0, 1, 2.. an infinite amount of time. That's not what you wanted, sorry I'm tired...

Now I have edited my post and I think it's now working exactly how you want, try it :wink:

Hi guix,

Just test the code (lunch time) and it is working as expected.

I need to polish it to suit my requirements and I will post my code sometime today if possible.

Thank you so very much for posting the code in no time....

Cheers,

EDIT: Code updated 2012 12 21

OK so far this is what I have done…

uint8_t ledPin[] = {
  2,3,4,5,6,7,8,9,10,11,12}; //Pins assinged for valves
uint8_t master_valve_id = 10; //master valve is ledPin[10] which is pin 12 in Arduino
uint8_t pinCount = 11; // pinCount to initially make the pins to LOW
uint8_t myInts[16]; // to store the input from serial
unsigned long
time_now    = 0,
time_past   = 0;

const uint8_t max_valves = 8;  
unsigned long valves_delays[ max_valves ];
uint8_t current_valve_id = 0;  //first valve to turn on

void setup()
{
  Serial.begin(19200);
  delay(1000);
  Serial.println("1");
  time_past = millis();
  uint8_t thisPin;
  for (thisPin = 0; thisPin < pinCount; thisPin++)
  {
    pinMode(ledPin[thisPin], OUTPUT);      
    digitalWrite(ledPin[thisPin], LOW);
  }

}

void loop()
{
  if(Serial.available()>0){

    for(uint8_t i = 0 ; i < 8; i++) {
      myInts[2*i] = Serial.parseInt();
      myInts[2*i+1] = Serial.parseInt();
      valves_delays[i] =   60UL * 1000 * myInts[2*i+1];
//      Serial.println(valves_delays[i]); //for testing
    }
    EnableValve(master_valve_id);
    current_valve_id = 0;
    for (uint8_t l=0; l<8; l++){
      if (valves_delays[current_valve_id] > 0){
        EnableValve(current_valve_id );
        break;
      }
    }
    Serial.end();
  }


  if ( current_valve_id < max_valves)
  {
    time_now = millis();

    if ( time_now - time_past > valves_delays[ current_valve_id ] )
    {
      time_past = time_now;

      DisableValve(current_valve_id );

      current_valve_id = current_valve_id + 1;

      if ( current_valve_id < max_valves ){


        if (valves_delays[current_valve_id] > 0)
          EnableValve(current_valve_id );
        else {
          jump_valve_id();
          if ( current_valve_id < max_valves)
            EnableValve(current_valve_id );
          else
            DisableValve(master_valve_id);
        }
      }
      else
        DisableValve(master_valve_id);

    }
  }
}


void jump_valve_id(){
  uint8_t l =0;
  uint8_t m = max_valves - current_valve_id;

  for (uint8_t l=0; l<m; l++){
    if (valves_delays[current_valve_id] > 0)
      break;

    else
      current_valve_id = current_valve_id + 1;
  }
}

void DisableValve(uint8_t valve_id )
{
  digitalWrite(ledPin[valve_id], LOW);
}

void EnableValve(uint8_t valve_id )
{
  digitalWrite(ledPin[valve_id], HIGH);
}

Python code running periodically on a FreeBSD 9.1 server:

#!/usr/bin/env python
import serial, time
onfile = file('spr_status' , 'r')
on_off = int(onfile.read())
if on_off ==1:
        print 'Sprinkler status On'
        infile = file('daily.conf' , 'r')
        text = infile.read()
        ser = serial.Serial(port='/dev/cuaU2', baudrate=19200)
        time.sleep(1)
        ser.setDTR(level=False)
        byte = ser.read(size=1)
        print byte,
        time.sleep(1)
        while byte =="1":
                ser.write(text),
                break
else:
        print 'Sprinkler Status Off'

Text file with valve numbers and timings in minutes:

1,30
2,30
3,45
4,30
5,30
6,30
7,45
8,45

When you run the python code through the pc the valves turning on and off on specified intervals in the text file.

TO DO: incorporate monitoring.

Any comments on the code is very welcome and appreciated.

Thanks with regards,

unouser:
2. For some reason I have to keep the for loop for Serial.parseInt() to one more than the actual array length. otherwise the loop running all over again for 16 times with zeros...

Bad, very bad idea, you risk program corruption, crashes... soon or later. Find the cause of the problem instead, and fix it the proper way :slight_smile:

I also recommend using proper indentation because your code is hard to follow.

  while(Serial.available()>0){
  for(int i = 0 ; i < 17; i++) {
      myInts[i] = Serial.parseInt();//etc. 
}

If there is 1 byte of serial data, read 17 ints and put them in a 16 element array. Probably not the best idea of the day.

Updated the code;

the reason for TODO: 2 was when I tried to print the array to serial, it invokes the serial port and run the for loop once again..

int ledPin[] = {2,3,4,5,6,7,8,9,10,11,12};                 // LED connected to digital pin 10
int pinCount = 11;
int myInts[16];
...
int incomingByte = 0;   // for incoming serial data

const int max_valves = 8;

You don’t have a lot of memory on a Arduino, and you know those numbers will never exceed 255 and will never be below 0, so by replacing those ‘int’ by ‘byte’ or ‘uint8_t’ (I prefer the last), it will take twice less memory.

long valves_delays[ max_valves ];

In your case it won’t make any difference, but you know those numbers will never be below 0 so why removing ‘unsigned’ ? It will not make your code faster or use less memory.

I think it’s important to use the right data type, so the code is easier to understand.

 if(Serial.available()>0){
    for(int i = 0 ; i < 16; i++) {
      myInts[i] = Serial.parseInt();
    } 
   
    if (myInts[0]>0)
    {
      for (int j =0; j<8; j++){
      unsigned long k = (2 * j + 1);

      valves_delays[j] =   60UL * 1000 * myInts[k];
      }
  
    current_valve_id = 0;
    EnableValve(current_valve_id );
    }
 }

You should really make this part more robust :slight_smile:

Oh and also, rename ledPin to valves_pins or something…

UPDATE:

  1. change data type from int to uint8_t for better memory as per guix,
  2. Now the valves will not turn on for zero intervals (rather than turning on and off in millisecond)
  3. Optimized for loop as per guix
uint8_t ledPin[] = {2,3,4,5,6,7,8,9,10,11,12}; 
uint8_t master_valve_id = 10;
uint8_t pinCount = 11;
uint8_t myInts[16];
unsigned long
  time_now    = 0,
  time_past   = 0;

const uint8_t max_valves = 8;
unsigned long valves_delays[ max_valves ];
uint8_t current_valve_id = 20;

void setup()
{
  Serial.begin(9600);
  time_past = millis();
  uint8_t thisPin;
  for (thisPin = 0; thisPin < pinCount; thisPin++)
  {
    pinMode(ledPin[thisPin], OUTPUT);      
    digitalWrite(ledPin[thisPin], LOW);
  }
}

void loop()
{
 if(Serial.available()>0){
    for(uint8_t i = 0 ; i < 8; i++) {
      myInts[2*i] = Serial.parseInt();
      myInts[2*i+1] = Serial.parseInt();
      valves_delays[i] =   60UL * 1000 * myInts[2*i+1];
      Serial.println(valves_delays[i]);
      }
    EnableValve(master_valve_id);
    current_valve_id = 0;
    for (uint8_t l=0; l<8; l++){
      if (valves_delays[current_valve_id] > 0){
      EnableValve(current_valve_id );
      break;}
    }
    }
 
 Serial.read();
  
  if ( current_valve_id < max_valves)
  {
    time_now = millis();

    if ( time_now - time_past > valves_delays[ current_valve_id ] )
    {
      time_past = time_now;

      DisableValve(current_valve_id );

      current_valve_id = current_valve_id + 1;

      if ( current_valve_id < max_valves ){


        if (valves_delays[current_valve_id] > 0)
            EnableValve(current_valve_id );
        else {
          jump_valve_id();
          if ( current_valve_id < max_valves)
            EnableValve(current_valve_id );
          else
            DisableValve(master_valve_id);
           }
        }
      else
        DisableValve(master_valve_id);

    }
  }
}


void jump_valve_id(){
      uint8_t l =0;
      uint8_t m = max_valves - current_valve_id;
    
      for (uint8_t l=0; l<m; l++){
      if (valves_delays[current_valve_id] > 0)
      break;
    
      else
      current_valve_id = current_valve_id + 1;
    }
}

void DisableValve(uint8_t valve_id )
{
  digitalWrite(ledPin[valve_id], LOW);
  Serial.print("Turning OFF valve");
  Serial.println(ledPin[valve_id]);
}

void EnableValve(uint8_t valve_id )
{
  digitalWrite(ledPin[valve_id], HIGH);
  Serial.print("Turning ON valve");
  Serial.println(ledPin[valve_id]);
}

There are 11 elements in the array, with index values 0 to 10. There is no position 11.

Thanks PaulS;

updated and also found out that lead to solving another bug too.

Updated (Post #8) the code for robust Serial communication and cleaned up some unwanted Serial.print commands.

Hi,

I have modified my code to get a reliable serial communication and to use a standalone ATMEGA328P-PU microcontroller.

The issue now is the system works perfectly on Arduino IDE serial monitor, however when I tried to invoke the system via python it does not respond.

Can you please help me to overcome this issue.

Arduino Code:

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

const uint8_t max_valves = 7;  
uint8_t master_valve_id = 0; //master valve is ledPin[10] which is pin 12 in Arduino
uint8_t myInts[max_valves]; // to store the input from serial
unsigned long
time_now    = 0,
time_past   = 0;

unsigned long valves_delays[ max_valves ];
uint8_t current_valve_id = 0;  //first valve to turn on


byte valves[max_valves] = {
  B00000011,
  B00000101,
  B00001001,
  B00010001,
  B00100001,
  B01000001,
  B10000001};

char* areas[max_valves] = {
  "Back Grdn Beds",
  "Alfresco",
  "Backyard 1",
  "Backyard 2",
  "Frontyard 1",
  "Frontyard 2",
  "Frnt Grdn Beds",
};

//Pin connected to Data in (DS) of 74HC595
const int dataPin = 10;
//Pin connected to latch pin (ST_CP) of 74HC595
const int latchPin = 11;
//Pin connected to clock pin (SH_CP) of 74HC595
const int clockPin = 12;

const int led = 13;

void setup()
{
  Serial.begin(9600);
//  Serial.setTimeout(1);

  lcd.begin (20,4);

  lcd.clear();
  lcd.setCursor(0,1);  

  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
  pinMode(led, OUTPUT);

  DisableValve(current_valve_id);    // turn the LED off by making the voltage LOW


}

void loop()
{

  if(Serial.available()>0){
    char letter = Serial.read();
    if (letter == 115){
    time_past = millis();
      Serial.println(letter);
      DisableValve(current_valve_id);    // turn the LED off by making the voltage LOW

      time_array();
    }
  }

  test_time();
}

void test_time(){
  for (uint8_t j = 0; j< max_valves; j++){
    if (valves_delays[j] > 0)
      run_valves();
  }
}

void time_array(){
  for(uint8_t i = 0 ; i < max_valves; i++) {
    myInts[i] = Serial.parseInt();
    Serial.println(myInts[i]);
  }
  Serial.println("done");
  time_array_new();
}

void time_array_new(){
  uint8_t z,y = 0 ;
  for( z = 0 ; z < max_valves; z++) {
    y  = y + myInts[z];
  }
  if (y > 0)
  {  
    convert_ul();
    enable_mastervalve();

  }
  else 
  {  
    DisableValve(current_valve_id);    // turn the LED off by making the voltage LOW
  }




}


void convert_ul(){
  for(uint8_t k = 0 ; k < max_valves; k++) {
    valves_delays[k] =   60UL * 1000 * myInts[k];
  }
}


void enable_mastervalve(){
  current_valve_id = 0;
  for (uint8_t l=0; l<max_valves; l++){
    if (valves_delays[current_valve_id] > 0){
      digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
      Serial.println("LED ON");
      EnableValve(current_valve_id );
      break;
    }
  }
}

void run_valves(){


  if ( current_valve_id < max_valves)
  {
    time_now = millis();


    if (time_now - time_past > 1000){
      Serial.println(time_now - time_past);
      lcd.setCursor(0,3);
      int rem_time = myInts[current_valve_id]-(time_now - time_past)/1000/60;

      if( rem_time < 10){

        lcd.print("Mins Left:   ");
        lcd.print(rem_time);
      }

      else if(rem_time < 100){
        lcd.print("Mins Left:  ");
        lcd.print(rem_time);
      }
      else {
        lcd.print("Mins Left: ");
        lcd.print(rem_time);
      }

    }

    if ( time_now - time_past > valves_delays[ current_valve_id ] )
    {
      time_past = time_now;

      DisableValve(current_valve_id );

      current_valve_id = current_valve_id + 1;

      if ( current_valve_id < max_valves ){

        if (valves_delays[current_valve_id] > 0)
        {
          digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
          Serial.println("LED ON");

          EnableValve(current_valve_id );
        }
        else {
          jump_valve_id();
          if ( current_valve_id < max_valves){
            digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
            Serial.println("LED ON");

            EnableValve(current_valve_id );
          }
          else
            DisableValve(current_valve_id );
        }
      }
      else
        DisableValve(current_valve_id );    // turn the LED off by making the voltage LOW


    }
  }
}

void jump_valve_id(){
  uint8_t l =0;
  uint8_t m = max_valves - current_valve_id;

  for (uint8_t l=0; l<m; l++){
    if (valves_delays[current_valve_id] > 0)
      break;

    else
      current_valve_id = current_valve_id + 1;
  }
}

void DisableValve(uint8_t valve_id )
{
  registerWrite(B00000000);
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  Serial.println("LED Off");


  valves_delays[valve_id] = 0;
  lcd.clear();
  lcd.setCursor(3,1);
  lcd.print("Sprinklers Off");
}

void EnableValve(uint8_t valve_id )
{
  registerWrite(valves[valve_id]);
}

void registerWrite(byte valve_id2) {
  byte bitToSet = 0;

  digitalWrite(latchPin, LOW);
  bitToSet = valve_id2;
  shiftOut(dataPin, clockPin, MSBFIRST,bitToSet);
  digitalWrite(latchPin, HIGH);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Sprinklers On");
  lcd.setCursor(0,1);
  lcd.print("Area: ");
  lcd.print(areas[current_valve_id]);
  lcd.setCursor(0,2);
  lcd.print("Run Time: ");
  lcd.print(myInts[current_valve_id]);
  lcd.print("Mins");

}

Python Code:

#!/usr/bin/env python
import serial, time
text = "s0,0,1,0,0,0,1"
ser = serial.Serial(port='/dev/ttyUSB0', baudrate=9600, timeout=5)
#ser.setDTR(level=False)
byte = ser.read(size=1)
#print byte,
#while byte =="1":
#       print text
#ser.setDTR(0)
#time.sleep(0.1)
#ser.setDTR(1)
#time.sleep(5)
#text2 = ser.read()
print byte
ser.write(text)
print text
#       break

Opening and closing the serial port resets the Arduino. Using the Serial Monitor, there is a period of time between when the Serial Monitor opens and you finish typing the first thing you want sent to the Arduino. During that time, the Arduino resets and is ready to accept data from the serial port.

In the python script, there is no looping and there are no delays. The script starts, resets the Arduino, reads a byte (without waiting for there to be one to read), sends something, and then ends, resetting the Arduino again.

It's not surprising, then, that the "Arduino doesn't work with Python".

Hi PaulS,

Thanks for your reply, please note that I am not using the Arduino but a standalone Atmega328, which is connected to a CP210x USB to Serial converter without the reset pin on Atmega328 attached.