Multiple Timer help [using DateTimeAlarm.h]

I am working on a timer for a hydroponic system. Right now, i have it laid out simply on a breadboard with the pump relay as a white LED and the lights relay as a green LED.

I need to have two timers going at the same time, with different on and ff times for each. I found DateTimeAlarm.h, a library that seems to be meant for handling this sort of scenario. I already have it installed.

Would i be able to chain together multiple alarms, eg, have alarm pumpOn call function pumpOn, and at the end of pumpOn start alarm pumpOff, which could call function pumpOff starting alarm pumpOn, etc? Would this get in the way of other parts of the sketch?

Also, is there some way of pseudo-multithreading, maybe through tasking, so that i could have function X check the water/nutrient level in the resovoir and add water, while still keeping the timers going?

I'm not really clear on how to implement DateTimeAlarm, and any pointers would be very helpful.

thanks to all,
eagle

Are the arlarm times fixed or do you want some way to change them. If you say more about the functionality you are looking for I will see if I can come up with some example code to help.

right now they can be fixed, unless there could be some way to change them over serial [a project for another day, when i read up more].

All the flexibility i'm looking for is to be able to change a variable at the top and have that change the timings. I just want to avoid using a series of customized delay statements, etc. which is why i turned to alarms.

thanks mem for the help,
eagle

Sure

Its getting late here but I will see if I can post something for you tomorrow. I have been meaning to do some more examples for this library.

i would really, really appreciate that. Also, putting it into the Arduino Playground would be great. If there any way i could help, just tell me [As we speak, i am trying to tear apart the library into bite size chunks that a guy with 2 years of computer science can understand. Wish me luck!]

Thanks again,
eagle

Here is a simple example sketch that sets two alarms, one at 8:30 am, the other at 5:45 pm.

/*
 * DateTimeAlarmSimple.pde
 *
 * Set the time by sending 'T' followed by hh:mm:ss -> T08:29:55 sets the time for 8:29:55 am 
 * Alarms are set in code by  changing parameters to createAlarm() in setup 
 * This example calls onAlarmA() at 8:30 am and onAlarmB() at 5:45 pm (17:45)
*/

#include <DateTime.h>
#include <DateTimeAlarms.h>

#define TIME_MSG_LEN  9  // Number of characters needed to decode a time message
#define TIME_HEADER  'T'  // Header for serial messages

AlarmID_t alarmA;
AlarmID_t alarmB;


void setup(){
  Serial.begin(9600);  
  pinMode(13,OUTPUT); // we flash the LED each second
  Serial.println("Set clock by sending Thh:mm:ss");
  alarmA = dtAlarms.createAlarm(AlarmHMS(8,30,0), &onAlarmA, false);  // 8:30am
  alarmB = dtAlarms.createAlarm(AlarmHMS(17,45,0),&onAlarmB, false);  // 5:45pm 
}

void onAlarmA(AlarmID_t Sender){
  // do something here on alarm A
  Serial.print("Alarm A ");      
  timeDisplay();    
}

void onAlarmB(AlarmID_t Sender){
  // do something here on alarm B
  Serial.print("Alarm B ");      
  timeDisplay();     
}

void  loop(){  
  processSerial(); // process a message to set the time           
  if(DateTime.available()) { // update clocks if time has been synced
    dtAlarms.delay(1000);
    timeDisplay();
  }
}

void processSerial() 
{
  // Process a message available on serial port
  // This version just sets the time of day

  char tag;
  int Hr,Min,Sec;
  // time message = Thh:mm:ss
  while(Serial.available() >=  TIME_MSG_LEN )  // process messages when all characters are received 
  { 
    if(Serial.read() == TIME_HEADER )
    { 
      Hr = getNumber();
      Serial.read();  // ignore the colon
      Min = getNumber();
      Serial.read();  // ignore the colon
      Sec = getNumber();    
     // time_t time = DateTime.makeTime(Sec, Min, Hr, 0, 0, 0 );
      // because the actual day is never set, the current day is retained when adjusting time 
      time_t time = previousMidnight(DateTime.now()) + AlarmHMS(Hr, Min, Sec);  
      DateTime.sync(time); 
      Serial.print("Clock set at ");      
      timeDisplay();
      
      dtAlarms.enable(alarmA);   
      dtAlarms.enable(alarmB);  
    }
  }
}

// return the value given by the next two characters in the serial buffer
int getNumber()
{
  int val = Serial.read() - '0';
  val = (val * 10) + Serial.read() - '0';
  return val;
}

void timeDisplay()
{
  DateTime.available();
  Serial.print(DateTime.Hour,DEC);  
  printDigits(DateTime.Minute);  
  printDigits(DateTime.Second); 
  Serial.println();
}

void printDigits(byte digits)
{
  // utility function for digital clock display: prints preceding colon and leading 0 
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits,DEC);   
}

The time is set by sending a serial message, the format of this message is documented in the code.

Bear in mind when testing that although in this example sketch only the time of day is set (hours, mins and seconds) the underlying system keeps track of elapsed days. If an alarm for a particular time has triggered and the clock is then set back to what appears to be a time before the alarm, the alarm will not trigger again until the next day. This is a side affect of simplifying the time message to only include hours, mins and secs.

When I designed the library, I assumed that the full time would be set (yr, month, day etc) But because it is not here, there are two small changes needed in the Library source code. These are in the function updateNextTrigger() and I have posted the revised version of this below:

void AlarmClass::updateNextTrigger()
{
  if( (value != 0) && (Mode.isEnabled != 0) )
  {
    time_t time = DateTime.now();
    if(Mode.isAlarm && nextTrigger <= time )   // update alarm if next trigger is not yet in the future
    {
      if( value > [glow]SECS_PER_WEEK [/glow]) { // is the value a specific data and time in the future (upd: July 2009 
        nextTrigger = value;  // yes, trigger on this value      
      }
      else if ( value <= SECS_PER_DAY) {
        if( value + previousMidnight(DateTime.now()) [glow]<=[/glow] time) // upd: July 2009 changed < to <=
            {
          nextTrigger = value + nextMidnight(time); // if time has passed then set for tomorrow 
            }
            else
            {
          nextTrigger = value + previousMidnight(time);  // set the date to today and add the time given in value      
            }
      }
      else if ( value <= SECS_PER_WEEK) {
        nextTrigger = value + previousMidnight(time); // set the date to today and add the time given in value
      }
      else {
        Mode.isEnabled = 0; // values more than a year but less than today have expired so the alarm is disabled 
      }
    }
    if(Mode.isAlarm == false){
      // its a timer
      nextTrigger = time + value;  // add the value to previous time (this ensures delay always at least Value seconds)
    }
  }
  else {
    Mode.isEnabled = 0;  // Disable if the value is 0
  }
}

I have an enhanced sketch that sets the alarm time from messages on the serial port but will wait and see how you get on with this before posting.

have fun!

mem, thanks again for your help. I am trying that code as we speak.
I guess what i was really looking for was some way to set multiple, low interval timers.

Think
http://www.petco.com/Assets/product_images/0/047431502994B.jpg
But with the intervals between 15 minutes on and 30 minutes off.
For any who are familiar with hydroponics, this is functioning as a pump timer. Quick summary; Hydroponics is soil-less growing in which the grower must supply the nutrients. Best known for it's application of growing marijuana, in the world of non-stoners it results in much larger and healthier fruits and vegetables, as well a year-round indoor growing season. The pump in my system drives the nutrients in my system to sprayers, which water the plants, and then turns off, allowing better access to CO2 in the air. If the pump stays off for too long, the roots of the plants will dry up and die. This timer is preventing that, as well as controlling the lights, which can be on in cycles from 12/12 on/off to 18/6 to 24/0.

In my system, i would wire the arduino's pins to relays, and have it take control of the lighting and pump timing. Integration of an LED display is included in my feature creep, but that's a long while down the road.

On the coding side, this demands a timing sequence, and after x time pin y goes high or low.

Sorry if i wasn't clear on my intent before, and thanks again mem for your help,
eagle

There are two ways that can be done using the library.

One is to have two pairs of alarms. Each pair uses one of the alarms to turn a pin on and the other to turn the pin off.
alarmAon – turn on a pin at a given time each day
alarmAoff – turn off a pin at a given time each day

alarmBon – turn on a pin at some other given time each day
alarmBoff – turn off a pin at some other given time each day

The other way is to use a time delay triggered in an alarm. The alarm would turn a pin on and also create a timer for some adjustable period of time that would turn the pin off.
alarmA – turn on a pin at a given time each day
timerA - turn off the pin after a given number of hours and minutes have elapsed from the time the alarm was triggered

The second option would be better if the start times need to be changed but the durations are constant (or the durations change but the start times are constant)

Both approaches are easy, let me know which one is closer to what you want.

perhaps a worked example will help.

Here is the sketch posted earlier with timers added to 'turn off' pins at a preset interval after the alarm is triggered - this is the second option mentioned above.

/*
 * DateTimeAlarmSimple.pde
 *
 * Set the time by sending 'T' followed by hh:mm:ss -> T08:29:55 sets the time for 8:29:55 am 
 * Alarms are set in code by  changing paramaegers to createAlarm() in setup 
 * This example calls onAlarmA() at 8:30 am and onAlarmB() at 5:45 pm (17:45)
*/

#include <DateTime.h>
#include <DateTimeAlarms.h>

#define TIME_MSG_LEN  9  // Number of characters needed to decode a time message
#define TIME_HEADER  'T'  // Header for serial messages

AlarmID_t alarmA, timerB;
AlarmID_t alarmC, timerD;


void setup(){
  Serial.begin(9600);  
  Serial.println("Set clock by sending Thh:mm:ss");
  alarmA = dtAlarms.createAlarm(AlarmHMS(8,30,0), &onAlarmA, false);  // 8:30am
  timerB = dtAlarms.createTimer(AlarmHMS(1,30,0), &onAlarmA, false); // timer for 1 hour 30 minutes

  alarmC = dtAlarms.createAlarm(AlarmHMS(17,45,0),&onAlarmC, false);  // 5:45pm 
   timerD = dtAlarms.createTimer(AlarmHMS(0,0,10), &onAlarmC, false); // timer for 10 seconds

}

void onAlarmA(AlarmID_t Sender){
  if(Sender == alarmA)
  {
     Serial.print("Alarm A On ");      
     // turn on the pin for alarmA
     dtAlarms.enable(timerB); // enable timer
  }
  else  // if its not alarmA it must be timerB so turn the pin off
  {
    Serial.print("Alarm A Off ");  
    // turn off the pin 
    dtAlarms.disable(timerB); // disable timer
  }     
  timeDisplay();    
}

void onAlarmC(AlarmID_t Sender){
  if(Sender == alarmC)
  {
     Serial.print("Alarm C On ");      
     // turn on the pin for alarmC
     dtAlarms.enable(timerD); // enable timer
  }
  else  // if its not alarmC it must be timerD so turn the pin off
  {
    Serial.print("Alarm C Off ");  
    // turn off the pin 
    dtAlarms.disable(timerD); // disable timer
  }     
  timeDisplay();    
}

void  loop(){  
  processSerial(); // process a message to set the time           
  if(DateTime.available()) { // update clocks if time has been synced
    dtAlarms.delay(1000);
    timeDisplay();
  }
}

void processSerial() 
{
  // Process a message available on serial port
  // This version just sets the time of day

  char tag;
  int Hr,Min,Sec;
  // time message = Thh:mm:ss
  while(Serial.available() >=  TIME_MSG_LEN )  // process messages when all characters are received 
  { 
    if(Serial.read() == TIME_HEADER )
    { 
      Hr = getNumber();
      Serial.read();  // ignore the colon
      Min = getNumber();
      Serial.read();  // ignore the colon
      Sec = getNumber();    
     // time_t time = DateTime.makeTime(Sec, Min, Hr, 0, 0, 0 );
      // because the actual day is never set, the current day is retained when adjusting time 
      time_t time = previousMidnight(DateTime.now()) + AlarmHMS(Hr, Min, Sec);  
      DateTime.sync(time); 
      Serial.print("Clock set at ");      
      timeDisplay();
      
      dtAlarms.enable(alarmA);   
      dtAlarms.enable(alarmC);  
    }
  }
}

// return the value given by the next two characters in the serial buffer
int getNumber()
{
  int val = Serial.read() - '0';
  val = (val * 10) + Serial.read() - '0';
  return val;
}

void timeDisplay()
{
  DateTime.available();
  Serial.print(DateTime.Hour,DEC);  
  printDigits(DateTime.Minute);  
  printDigits(DateTime.Second); 
  Serial.println();
}

void printDigits(byte digits)
{
  // utility function for digital clock display: prints preceding colon and leading 0 
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits,DEC);   
}

The first alarm will trigger at 8:30am and will turn off one hour 30 minutes later. The code that sets that is this:
alarmA = dtAlarms.createAlarm(AlarmHMS(8,30,0), &onAlarmA, false); // 8:30am
the timer code that will activate 1hr 30 minutes later is this:
timerB = dtAlarms.createTimer(AlarmHMS(1,30,0), &onAlarmA, false); // timer for 1 hour 30 minutes

The handler for these is the function:
void onAlarmA(AlarmID_t Sender)
this code checks to see if it was trigger by the alarm (at the turn on time) or by the timer (for the turn off time)

The triggers for the second start and stop times are similar:
alarmC = dtAlarms.createAlarm(AlarmHMS(17,45,0),&onAlarmC, false); // 5:45pm
timerD = dtAlarms.createTimer(AlarmHMS(0,0,10), &onAlarmC, false); // timer for 10 seconds

This is set to turn of in 10 seconds. Sending T17:44:55 will set the time to 5 seconds before the alarm and you should see what happens.

Again, i am trying out the new code. The only thing is that these cycles are continuous. So, 15 minutes pump on, 30 minutes off, 15 on, 30 off and so on forever into the foreseeable future.

Thanks again!
eagle

If your application is interested in durations rather than time of day then you can use the createTimer function instead of createAlarm. The difference is that createAlarm works at a given time of day and createTimer works at a given time interval. The latest sketch I posted shows the use of both.

dtAlarms.createAlarm(AlarmHMS(8,30,0), &onAlarmA);
will call the onAlarmA function at 8:30 every day

dtAlarms.createTimer(AlarmHMS(8,30,0), &onAlarmA);
would call the onAlarmA function every 8 hours and 30 minutes

Perhaps if you could outline the functionality you want I can help you tailor the sketch.

You could also combine the EventFuse and MSTimer2 libraries for this.

EventFuse is essentially a set of countdown timers. Each time you call the burn() function it subtracts 1 from the timer. When the timer reaches zero it will call the associated callback function. Each timer has a repeat count which can be set to a specific value or to INF_REPEAT for an infinitely repeating event.

MSTimer2 uses the Timer2 interrupt to generate an event after a given number of milliseconds. By calling the event fuse burn() function the fuses can be set up as timers running at the given speed.

Here is an example using your pump control scenario:

#include <EventFuse.h>
#include <MsTimer2.h>

int pumpCtrlPin = 13;      // pump control pin
#define PumpOnMinutes 15   // duration in minutes for pump on cycle
#define PumpOffMinutes 30  // curation in minutes for pump off cycle

// event fuse handler for PumpOn event
void PumpOn(eventFuse_h fuse){
  // do whatever is required to turn pump on
  digitalWrite( pumpCtrlPin, HIGH );
  
  // Set up a new event to turn it off
  Fuses.setFuse( PumpOnMinutes, 1, PumpOff );
}

// event fuse handler for PumpOn event
void PumpOff(eventFuse_h fuse){
  // do whatever is required to turn pump off
  digitalWrite( pumpCtrlPin, LOW );
  
  // Set up a new event to turn it on
  Fuses.setFuse( PumpOffMinutes, 1, PumpOn );
}

void timerTick(){
  // reduce all enabled fuses by 1 unit. In this example
  // there is only one fuse running at any given time, but
  // it is easy to add more in order to control multiple
  // overlapping events.
  Fuses.burn(1);
}

void setup() {
  pinMode(pumpCtrlPin, OUTPUT);
  
  // initialize the system by turning on the pump directly
  // This will also create the first event fuse which will
  // turn the pump back off in PumpOffMinutes minutes. 
  PumpOn(0);
  
//  MsTimer2::set(60000, timerTick );  // One minute intervals, 60,000mS
  MsTimer2::set(1000, timerTick );   // One second intervals, for testing
  MsTimer2::start();
}

void loop(){
}

I was just updating EventFuse and I needed another example, so I used this idea of a lamp timer style output controller as an example.

This code could be used as-is to control several pumps with varying on-off cycle times.

/*
 *
 * Description:
 * EventFuse example demonstrating control of 
 * multiple independent switched outputs. Each
 * output can be configured with independent
 * on and off durations with a minimum of 1 second
 * and a maximum of about 1100 hours (2^32 mS).
 *
 */
 
#include <EventFuse.h>
#include <MsTimer2.h>

#define OutputCount 4
// These would be better handled as enums, 
// but that requires a seperate .h file.
#define OffTime 0
#define OnTime 1
#define OutputPin 2

// The outputs array defines how long each output will
// be turned off, on, and what pin to use for that output.
// The off and on values are in units of 'ticks'. The length
// of a tick is controlled by the setup of MsTimer2. 
                             // off   on  pin
byte outputs[OutputCount][3] ={{  5,  10,  13},   // Output A
                               { 15,  20,  12},   // Output B
                               {  2,  12,  11},   // Output C
                               { 10,   2,  10},}; // Output D
                    
void OutputHandler(FuseID fuseID, int outputID){
  // look up the pin associated with this output
  byte pin = outputs[outputID][OutputPin];

  // get and invert the current pin state and write
  // it back to the port to invert the current pin state.
  int state = 1&~digitalRead(pin);
  digitalWrite( pin, state );
  
  // Reset the fuse length with a new interval. The current state
  // of the pin is used to determine which interval should be used.
  eventFuse[fuseID].fuseLen = outputs[outputID][state];
}

void timerTick(){
  eventFuse.burn(1);
}

void setup() {
  // Set up and init all outputs to off
  for(byte i = 0; i<OutputCount; i++){
    pinMode( outputs[i][OutputPin], OUTPUT);
    digitalWrite( outputs[i][OutputPin], LOW );

    // Set up an event fuse for this output.
    eventFuse.newFuse( i, outputs[i][OffTime], INF_REPEAT, OutputHandler );
  }
  
  // Set MsTimer2 for one second per tick.
  MsTimer2::set(1000, timerTick );
  MsTimer2::start();
}

void loop(){
}

Here is the code for DateTimeAlarms that does the same thing as DaveK's sketch above. I have chosen to expand the test for pin state to make it less cryptic but the form in DaveK's can be used if preferred.

#include <DateTime.h>
#include <DateTimeAlarms.h>

#define OutputCount 4

#define OffTime 0
#define OnTime 1
#define OutputPin 2

byte outputs[OutputCount][3] =                   
    // off   on  pin
    {{  5,  10,  13},   // Output A
     { 15,  20,  12},   // Output B
     {  2,  12,  11},   // Output C
     { 10,   2,  10},}; // Output D

// declare four alarms
AlarmID_t alarm[OutputCount];  // the array of alarms 

void setup()
{
  DateTime.sync(0); // Start the clock, but here we don't use time of day
   // Set up and init all outputs to off
  for(byte i = 0; i<OutputCount; i++){
    pinMode( outputs[i][OutputPin], OUTPUT);
    digitalWrite( outputs[i][OutputPin], LOW );
    alarm[i] = dtAlarms.createTimer(outputs[i][OffTime], &onAlarm);
  } 
}

void onAlarm(AlarmID_t Sender){
  boolean state = digitalRead(outputs[Sender][OutputPin]); // determine if the pin is currenty on or off
  if(state == LOW) // is the output currently off?
  {  
     digitalWrite( outputs[Sender][OutputPin], HIGH ); 
     dtAlarms.setValue(Sender, outputs[Sender][OnTime] );
  }
  else  // here if the output was on
  {  
     digitalWrite( outputs[Sender][OutputPin], LOW );
     dtAlarms.setValue(Sender, outputs[Sender][OffTime] );    
  }    
}

void  loop()
{  
    dtAlarms.delay(1000);  // service the alarms handler, this can be any value greater or equal to 0
}

For delaying for given periods of time either version would do. If the pins need to be turned on or off at particular times of day then I would think that DateTimeAlarms would be the preferred choice.