The sketch below is the working version of the Hot Water Cylinder controller. To test it, make up some really short holidays (like 20 or 30 seconds long) and put them on the SD card in a new file. Change the file name in the sketch to look at the test file, and then change the Alarm.delay() to something more meaningful for the length of “Holiday” you have created. If you are doing it on a weekend, you may also need to adjust the “weekends” in the fictional file too. This version is for a NORMALLY OPEN relay. The NORMALLY CLOSED variant can be found here.

Related Pages

The extra libraries required for this sketch are:

You can also download the libraries from the following webpage: A Collection of Arduino Libraries Used in This Project.

The sketch below can be downloaded from here: TimerDriverv3.ino

/*
TimerDriverv3 - Arduino 1.0.5 sketch
This sketch brings together the three development sketches
SDFileContentLoader3, AlarmSequencerv2.ino, and LogicTester
to form a sketch that loads the alarm times off the SD Card,
enables the Real Time Clock, update:s the alarm array and
determines which are the next alarms to be set then loads
them into two triggerOnce alarms which will enable or disable
the Hot Water Cylinder Controller.

A necessary modification was to move the Number of Fields (NoF)
variableout of the Setup to be a global variable. The same needed
to be done for the times[] array but as this was a variable
size array we will rely on the NoF to limit the fields to
work with and set the array to be the maximum possible size which
will be 24 fields.  A number of variables have been defined as
time_t so that the triggerOnce() alarms are loaded correctly.
*/

#include <SD.h>
#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
#include <TimeAlarms.h>	// this library will allow us to set alarms - ultimately these alarms will be read from the SD Card


// Note that even if it's not used as the CS pin, the hardware
// CS pin (10 on most Arduino boards,
// must be left as an output or the SD library functions will
// not work.  In the Sparkfun SD Shield the CS is on
// Arduino Pin 8.  The SD Shield also uses pins 11 and 13 - avoid them.
const int chipSelect = 8;
int fieldIndex = 0;  //The current field being received
time_t Nowtime = 0;  //This will hold the current time

boolean StatusFlag = true;  // Is the HWC enabled or not? TRUE = enabled (default)
  //A yellow LED will be put on pin13 to show when the HWC is enabled based on StatusFlag
boolean ListExceeded = false; //LED pin control.A Red LED will be used to indicate that the
  //SD Card's list of available times has been exceeded. Basically if this goes off you
  //will need to put more date:s in the SD Card.
int InDexer = 2;	//InDexer is the array pointer that indicates which is the next holiday period to be used.
time_t Alarmdate:On=0;	//This is the next date: to enable the HWC
time_t Alarmdate:Off=0;	//This is the next date: to disable the HWC
int NoF = 24;      //This is an arbitory number for the Number of Fields in the time[] array
                    // it is filled from the SD Card to reflect the actual data provided.
time_t times[24];  // This is the array that will ultimately be
                    // loaded with alarm date:s from the SD Card
int ledPin = 7;    // yellow LED connected to digital pin 7 to indicate HWC enabled Status
int redLED = 6;    // redLED connected to pin 6 to indicate the need for updating the SD Card data
int HWCPin = 5;    // signal to the HWC controller on pin 5. This avoids conflicts with SD Card
                   // commandeered pins.


void setup()
{
  pinMode(ledPin, OUTPUT);  //Set DigitalPin 7 to Output for HWC status indication
  pinMode(redLED, OUTPUT);  //Set DigitalPin 6 to Output for SDCard file Status
  pinMode(HWCPin, OUTPUT);  //Set DigitalPin 5 to Output for signal to HWC controller
  //Open Serial Communications and wait for the port to open:
  Serial.begin(9600);
  // Make all weekend alarm date:s and holiday period pointers current.
  
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  setSyncInterval(320543);    // set the interval to resynchronise the internal clock
                              // this is set toabout 3.7ish days.
  if(timeStatus()!= timeSet)  // this makes sure the RTC service is going 
     Serial.println("Unable to sync with the RTC");
  else
     Serial.println("RTC has set the system time");


  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect))
  {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  File dataFile = SD.open("times.csv");

    // if the file is available, read from it:
  if (dataFile)
  {
  // In order to correctly size the array the first two digits
  // off the file will be read as the Number of Fields to use.
  // NoF is the number of comma separated fields we expected.

    char ch = dataFile.read();  // Read the first entry - tens
    NoF = 10 * (ch-'0');    //Change ASCII digit to a tens digit
    ch = dataFile.read();    // Read the second entry
    NoF = NoF + ch-'0';    // Add the ones digit to the tens digit
    ch = dataFile.read(); // read the next figure which will be a comma, but ignore it.
//    unsigned long times[NoF];  //This is the array to hold the values now with correct size.
    Serial.print(" Number of Useful Fields Expected ");
    Serial.println(NoF);
    
    
    if (dataFile.available())
    {
      for(fieldIndex = 0; fieldIndex < NoF; fieldIndex ++)
      {
        times[fieldIndex]=dataFile.parseInt(); //get a numerical value
      }
      Serial.print(fieldIndex);
      Serial.println(" Fields recieved:");
      for(int i = 0; i < fieldIndex; i++)
      {
        Serial.println(times[i]);
      }
    }
    dataFile.close();


    Nowtime = now(); //This pulls the current time off the system which is hopefully synced to the RTC.
    Serial.println("Time rightnow");
    Serial.println(Nowtime);
    Serial.println("Original alarms times");
    Serial.println(times[0]);
    Serial.println(times[1]);
    // Weekend catchup.This jumps the alarm date:s ahead a week at a time until they
    // are in the future.
    while(times[0]<Nowtime)
    {
      times[0]=times[0]+604800;  //Add a week to the number (604800 seconds)
      times[1]=times[1]+604800;
    }
    Serial.println("Update:d alarms times");
    Serial.println(times[0]);
    Serial.println(times[1]);
  // Step through the times[] array components to find the next START of the holiday period.
  while (times[InDexer]<Nowtime)	// Check to see if the date: is in the future.
  {
    InDexer = InDexer + 2;       // Adding two means we look ONLY at the start date:s.
    if (InDexer >= NoF-1)	//  Have we exceeded the list?  If so enable the error light
    {
      ListExceeded = true;
      break;
    }
  }
  // Report the next weekend and whether the list of special breaks has been exceeded.
  Serial.print(times[0]);Serial.print(",");Serial.println(times[1]);
  Serial.print(ListExceeded);
  Serial.print(InDexer);	//This indicates where in the list of holiday breaks we are looking and the next holidays.
  Serial.print("Holiday Alarm");Serial.print(times[InDexer]);Serial.println(times[InDexer+1]);
   }

  // if the file isn't open, pop up an error:
  else
  {
    Serial.println("error opening timesv2.csv");
  }
  
}

void loop()
{
  Nowtime = now();  //Update: Nowtime to carry the current time.
	// To carry over into this new section the up-to-date: InDexer will be used to work out
	// Which Holiday periods are coming up and which alarm goes next. InDexer is a Global Variable.
	// The Weekends will be set to skip forward if it turns out that a holiday period is
	// coming up next.
  if (times[0]> times[InDexer] && ListExceeded == true) // It would be a holiday but the list has been exceeded
  {
    Alarmdate:Off = times[0];    // Set the next Alarm times
    Alarmdate:On = times[1];
  }
  else if (times[0]>times[InDexer] && ListExceeded == false) // The next item is a holiday and the list is OK.
  {
    Alarmdate:Off = times[InDexer];
    Alarmdate:On = times[InDexer+1];  // Loads the Holiday times into the Alarm function
    while (times[0]< times[InDexer+1])
    {
      times[0]=times[0]+604800;    //Advance the weekend date:s to be clear of the holiday
      times[1]=times[1]+604800;
    }
  }
  else								//Normal Weekend
  {
    Alarmdate:Off = times[0];			//In the Arduino this will load the date: into the Alarm function
    Alarmdate:On = times[1];
  }
	
  if (Nowtime > times[1])	// When a weekend start has passed jump forward to the next one.  We shall have to check that the logic
				// does not cause the weekend close off alarm to be overwritten.
  {
    times[0]=times[0]+604800;    // Advance the weekend date:s to the next one once the
    times[1]=times[1]+604800;    // current weekend is finished
  }
  if (Nowtime > times[InDexer+1])	//When a holiday is in progress the pointer needs to be advanced.
  {
    InDexer = InDexer+2;	//Advance the pointer the the next holiday once current one
				//finished.
    if (InDexer >= NoF-1)	//Have we exceeded the list?  If so enable the error light
    {
      ListExceeded = true;			//But carry on by setting InDexer to last good figure
      InDexer = InDexer - 2;
    }
  }
  // Now we load the triggerOnce alarms with the Alarm date:s.  The alarm date:s are in time_t data type.
  Alarm.triggerOnce(Alarmdate:Off,HWCdisable);
  Alarm.triggerOnce(Alarmdate:On,HWCenable);
  // The following are the actions taken based on the StatusFlag which enable or disable the
  // HWC and indicate it's status.
  if(StatusFlag == true)
  {
    digitalWrite(ledPin,HIGH);  //The HWC is enabled and the yellow LED is lit.
    digitalWrite(HWCPin,HIGH);  //The HWC is enabled.
  }
  else
  {
    digitalWrite(ledPin,LOW);  //The yellow LED is dark.
    digitalWrite(HWCPin,LOW);  //The HWC is disabled
  }
  if(ListExceeded == true)
  {
    digitalWrite(redLED,HIGH);  //Show that the SD Card list need changing.
  }
  else
  {
    digitalWrite(redLED,LOW);  //All is OK in the SD world
  }
  Alarm.delay(10000);  //A ten second delay using requisite Alarm.delay() function.
}

void HWCdisable()
{
  StatusFlag = false;
}

void HWCenable()
{
  StatusFlag = true;
}