Checking Whether It’s Time to Turn a Valve On or Off

Next, let’s look at the data from the RTC and figure out how we’ll use this to decide whether it’s time to turn something on or off. If you go back to the RTC example ds1307, you’ll see how the time is printed:

Serial.print(now.hour(), DEC);

Conveniently, this is already a number, so comparing with the hours and minutes we have stored will be easy.

In order to access the RTC, you’ll need to add parts of the ds1307 example to your program. At the top, before setup(), add this:

#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 rtc;

This time we won’t use the analogue input pins to provide 5V and GND. Why? Because analogue inputs are scarce; there are only six of them, and we already lost two for the 12C interface to the RTC. At the moment our project doesn’t need any analogue inputs, but we might think of something later.

In setup() you’ll need the following:

  #ifdef AVR
    Wire.begin();
  #else
    // I2C pins connect to alt I2C bus on Arduino Due
    Wire1.begin(); 
  #endif
    rtc.begin();

Now think about what you need to do: as long as the current time is greater than the time you want to turn a valve ON, and less than the time you want to turn the valve OFF, you should turn it on. At all other times you want it turned OFF.

From the RTC library you can get the current time like this:

  dateTimeNow = rtc.now();

and then you can access parts of the time like this:

    dateTimeNow.hour()
    dateTimeNow.minute()

Can you see the problem? We’ve stored time as a four-digit number, where the first two digits are the hour and the last two digits are the minutes. We can’t do a mathematical comparison without separating this number into hours and minutes, and then the comparison gets complicated.

It would be nice if we could have just one number. We can do this if, instead of storing the time as hours and minutes, we just converted the hours to minutes and stored the number of minutes since midnight. That way, we only have to deal with one number and the mathematical comparison is easy. (We have to remember never to turn on the water before midnight and off after midnight. This would be another opportunity for a sanity check to make our program more robust.)

The following code illustrates this:

int nowMinutesSinceMidnight = (dateTimeNow.hour() * 60) +
  dateTimeNow.minute();

and then the comparison looks like this:

if ( ( nowMinutesSinceMidnight >= onOffTimes[valve][ONTIME]) &&
    ( nowMinutesSinceMidnight < onOffTimes[valve][OFFTIME]) ) 
{
        digitalWrite(??, HIGH);
}
else 
{
        digitalWrite(??, LOW);
}

Wait a minute, what about those question marks? We need to know the pin number of each valve. Our for() loop just counts off the valves: 0, 1, and 2. We need a way to indicate what pin number corresponds to which valve. We can use an array:

int valvePinNumbers[NUMBEROFVALVES];

By using the same constant variable we created earlier, this array will always have exactly the same number of rows as the other array, even if you later change the number of valves.

In setup() we’ll insert the correct pin numbers into the array:

valvePinNumbers[0] = 6; // valve 0 is on pin 6
valvePinNumbers[1] = 8; // valve 1 is on pin 8
valvePinNumbers[2] = 3; // valve 2 is on pin 3
NOTE

Whenever you need to look up some information based on an index, an array is a good way to do that. Think of it as a lookup table.

Now we can fix our question marks:

if ( ( now.hour() > onOffTimes[valve][onTime]) &&
     ( now.hour() < onOffTimes[valve][offTime]) ) {

    Serial.println("Turning valve ON");
    digitalWrite(valvePinNumbers[valve], HIGH);
}
else {
    Serial.println("Turning valve OFF");
    digitalWrite([valve], LOW);
}

One last thing: we’re going to need to separate the four-digit numbers in the array into hours and minutes. It might be easier to do that when the user types in the information. We’ll ask the user to add a : between the hours and minutes, we’ll read them as separate numbers, do the conversion to minutes since midnight right there, and store the minutes since midnight in the array. When we print the times we’ll have to convert this back to the usual time format. Example 8-2 now shows our modified expectValveSetting() function along with the new printSettings() function.

NOTE

You have probably thought of two or three different ways of doing this. Most programming problems, in fact most engineering problems, can be solved many different ways. A professional programmer might consider efficiency, speed, memory usage, perhaps even cost, but as a beginner you should use whatever is easiest for you to understand.

Example 8-2. The expectValveSetting() function
/*
 * Example 8-2. expectValveSetting() and printSettings() functions
 * Read a string of the form "2N13:45" and separate it into the 
 * valve number, the letter indicating ON or OFF, and the time
 */

void expectValveSetting() {

  // The first integer should be the valve number
  int valveNumber = Serial.parseInt();

  // the next character should be either N or F
  char onOff = Serial.read();

  int desiredHour = Serial.parseInt(); // get the hour

  // the next character should be ':'
  if (Serial.read() != ':') {
    Serial.println("no : found"); // Sanity check
    Serial.flush();
    return;
  }

  int desiredMinutes = Serial.parseInt(); // get the minutes

  // finally expect a newline which is the end of the sentence:
  if (Serial.read() != '\N') { // Sanity check
    Serial.println("You must end your request with a Newline");
    Serial.flush();
    return;
  }

  // Convert desired time to # of minutes since midnight
  int desiredMinutesSinceMidnight 
    = (desiredHour*60 + desiredMinutes);
 
  // Put time into the array in the correct row/column
  if ( onOff == 'N') { // it's an ON time
    onOffTimes[valveNumber][ONTIME] 
    = desiredMinutesSinceMidnight;
  }
  else if ( onOff == 'F') { // it's an OFF time
    onOffTimes[valveNumber][OFFTIME] 
    = desiredMinutesSinceMidnight;
  }
  else { // user didn't use N or F
    Serial.print("You must use upper case N or F ");
    Serial.println("to indicate ON time or OFF time");
    Serial.flush();
    return;
  }

  printSettings(); // print the array so user can confirm settings
} // end of expectValveSetting()

void printSettings(){
  // Print current on/off settings, converting # of minutes since 
  // midnight back to the time in hours and minutes
  Serial.println();
  for (int valve = 0; valve < NUMBEROFVALVES; valve++) {
    Serial.print("Valve ");
    Serial.print(valve);
    Serial.print(" will turn ON at ");

    // integer division drops remainder: divide by 60 to get hours
    Serial.print((onOffTimes[valve][ONTIME])/60);
    Serial.print(":");

    // minutes % 60 are the remainder (% is the modulo operator) 
    Serial.print((onOffTimes[valve][ONTIME])%(60));

    Serial.print(" and will turn OFF at ");
    Serial.print((onOffTimes[valve][OFFTIME])/60); // hours
    Serial.print(":");
    Serial.print((onOffTimes[valve][OFFTIME])%(60)); // minutes
    Serial.println();
  }
}

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *