erratic behavior with alarms

Those mostly identical hourly kalk sections are crying out for a different method - an array of kick off times and a little state machine to handle the on/off with millis perhaps.

However, for your purposes, it's probably easiest to adjust the library to handle more alarms so you'll need to search your machine to find out where it's hiding timealarms.h.

Search the machine...thanks it popped right up.

I changed the allowed alarms from 6 to 255.

Probably better to count how many you need and set it to that (or a little more in case you forget when you add another alarm). It's set to six to conserve memory, so 255 may blow too much RAM away.

Keep an eye on what the compiler tells you about how much memory you're using.

Good call. The sketch wouldn't compile with 255 or 152. It did compile with 76 with a low memory warning.

I think that reducing the number of alarms is possible, and I'm pretty sure it can be brought down to seven with the next alarmOnce setting technique presented in a previous reply.

There are several other adjustments which can be made to simplify the situation.

I think that the A and B return pumps could be placed on one alarm. Turn them both on or off in the same alarm. They would also be a good first candidate for the alarmOnce conversion.

I'm not sure about the overall program, but I would think that some use of delay() would be possible. For example the frozen food start/stop dosing which takes 4 seconds could be done with delay(). The dry food dosing at 10 seconds is the next shortest time, and it would also be a candidate for delay().

Give the alarm design some thought, and see how much you can reduce it. You may be able to get things down to a manageable number and still keep the programming at a level which leaves you comfortable.

As wildbill suggests, the kalk stir/dosing routines can possibly be reduced to a function triggered every hour by one alarm.

As a final thought, I don't think you have an hour 24. Midnight is likely to be 00:00.

No 24th hour? What the heck? 0 hour it is.

I don't want to put both return pumps on a single alarm because that would mean wiring them both to either one relay or one mosfet. (I get monster mosfets from Drazzy that are the best) I don't want a single failure point for both return pumps.

I very much appreciate the help pointing out why the alarms seemed like haywire.

My first thought was to use a single alarm to get things started at the same time every day, then use delays for all else making sure the program used a little less than 24 hours in case the Arduino's clock is running fast.

I'll look into alarmOnce, etc. When I get something working, I'll post. It's not likely to be a beautiful sketch but if it works it will be a beautiful thing. :slight_smile:

Thanks guys!

I don't want to put both return pumps on a single alarm because that would mean wiring them both to either one relay or one mosfet. (I get monster mosfets from Drazzy that are the best) I don't want a single failure point for both return pumps.

I don't understand this. The alarm function will have digitalWrite() calls for two different pins triggering two different mosfets or relays as is currently done in two different alarms one second apart. If you actually need the one second between the two pumps, then delay() could be used between the digitalWrite() calls.

The kalk feed/stir code happens every hour, and can be triggered very simply when the hour() value changes. There is no need use the alarms for this. Here's some example code which triggers on every minute change. The time library syncs to the RTC every five minutes, so the hour() timing will stay accurate.

#include <Time.h>
#include <TimeLib.h>

byte lastMinute;
//byte lastHour;
boolean runKalkCycle = false;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting KalkCycle example");
  setTime(10, 10, 50, 2, 1, 2019);//set time for Time library
  lastMinute = minute();
  //lastHour = hour();
}

void loop() {
 
  if (minute() != lastMinute)
  //if (hour() != lastHour)
  {
    lastMinute = minute();
    //lastHour = hour();
    Serial.println("new minute");
    //Serial.println("new hour");
    runKalkCycle = true;
  }

  if (runKalkCycle == true)
    {
      kalkCycle();
    }
}

void kalkCycle()
{
  //timing values
  const unsigned long kalkStirTime = 55000;//time for new minute example code
  //const unsigned long kalkStirTime = 60000;//1 minute stir
  const unsigned long kalkFeedTime = 12000;//12 second dose
  const unsigned long stirFeedDelay = 7000;//time between stir and dose start
  //logic control variables
  static boolean timing = false;
  static boolean startFeed = false;
  static boolean stopFeed = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(kalkStirPin, On)
    Serial.print("Start KalkStir ");
    Serial.println(second());
  }

  if (millis() - timeStart >= stirFeedDelay and !startFeed)
  {
    Serial.print("Start KalkFeed ");
    Serial.println(second());
    //digitalWrite(kalkFeedPin, On)
    startFeed = true;
  }

  if (millis() - timeStart >= (stirFeedDelay + kalkFeedTime) and !stopFeed)
  {
    Serial.print("Stop KalkFeed ");
    Serial.println(second());
    //digitalWrite(kalkFeedPin, Off)
    stopFeed = true;
  }

  if (millis() - timeStart >= kalkStirTime)
  {
    Serial.print("Stop KalkStir ");
    Serial.println(second());
    timing = false;
    startFeed = false;
    stopFeed = false;
    runKalkCycle = false;
  }
}

cattledog:
There is no need use the alarms for this.

Never was. It's frustrating!

Thank you cattledog for the example code. I looked at it on the serial monitor and see it's a continuous loop but the SM did not display time-only the functions.

I haven't had a chance to study it much but I am confused by what's in setTime (10, 10, 50, 2, 1, 2019).

It looks like 50 is in the hours place. Also seems odd that time is written backwards compared to setting time on the serial monitor.

Am I correct that I'd need to set the time in setTime and then upload real quick?

setTime() is a Time Library function, and the format is different from what you entered through the serial monitor. I have been developing the test code without the RTC, and I find it more convenient to just reset the time in code if necessary.You can start at any time of day and the code will follow.

setTime(int hr,int min,int sec,int day, int month, int yr);

My example was not very complete, and I have been working on implementing the complete timing. I was originally skeptical of the approach without the time alarms, and was concerned about some complicated control logic, but it turned out to be not too difficult to develop the code without any use of the time alarms.

I have changed some of the start times of your timing by a few seconds because I had some bugs with events starting at seconds==0. I think the minutes values could be in the process of changing. I centered the frozen food dispense in the middle of the stir cycle. I hope I interpreted our timing schedule correctly.

Please review this code, check and comment on the timings. We'll put the RTC back later after confirming the cycles. I have speeded up the testing by rescaling Timer0 which controls millis() and micros(). One hour is now about a minute of code test time. You will need to implement the digitalWrite() outputs.

Please review and comment.

#include <Time.h>
#include <TimeLib.h>
byte Hour;
byte Minute;
byte Second;
byte lastHour;
boolean runKalkCycle = false;
boolean runDryFoodCycle = false;
boolean runFrozenFoodCycle = false;
boolean runPumpCycle = false;
boolean runNightPumpCycle = false;

void setup()
{
  //accelerate timing for testing purposes
  //change Timer0 prescaler from default 64 to 1
  //comment out next line to go back to default timing
  TCCR0B = B00000001;
  //setTime(int hr,int min,int sec,int day, int month, int yr);
  setTime(8, 15, 00, 2, 1, 2019);//set time for Time library
  Serial.begin(115200);
  Serial.println("Starting Reef Tank Test");
  Hour = hour();
  Minute = minute();
  Second = second();
  printTime();
  lastHour = hour();
}

void loop()
{  
  Hour = hour();
  Minute = minute();
  Second = second();

  if (Hour != lastHour) //Kalk triggers every hour
  {
    lastHour = Hour;
    printTime();
    runKalkCycle = true;
  }

  if (Hour == 8 or Hour == 11 or Hour == 14 or Hour == 17)//dryFood
  {
    if (Minute == 30)
    {
      if (Second == 5) //some problem when triggered at 0 seconds
      {
        runDryFoodCycle = true;
      }
    }
  }

  if (Hour == 10 or Hour == 13 or Hour == 16 or Hour == 19)//frozenFood
  {
    if (Minute == 5)
    {
      if (Second == 5)
      {
        runFrozenFoodCycle = true;
      }
    }
  }

  //pump on times
  if (Hour == 8 and Minute == 40 and Second == 0)  runPumpCycle = true;
  if (Hour == 10 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 11 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 13 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 14 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 16 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 17 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 19 and Minute == 10 and Second == 0) runNightPumpCycle = true;

  if (runPumpCycle == true)
  {
    pumpCycle();
  }

  if (runNightPumpCycle == true)
  {
    nightPumpCycle();
  }

  if (runKalkCycle == true)
  {
    kalkCycle();
  }

  if (runDryFoodCycle == true)
  {
    dryFoodCycle();
  }

  if (runFrozenFoodCycle == true)
  {
    frozenFoodCycle();
  }
}//end loop

void dryFoodCycle()
{
  //timing variables
  const unsigned long dryFoodFeedTime = 10000;
  //logic control variables
  static boolean timing = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(dryFoodFeedPin, On)
    Serial.print("Start Dry Food Feed ");
    printTime();
  }

  if (millis() - timeStart >= dryFoodFeedTime)
  {
    //digitalWrite(dryFoodPin, off);
    Serial.print("Stop Dry Food Feed ");
    printTime();
    timing = false;
    runDryFoodCycle = false;
  }
}

void frozenFoodCycle()
{
  //timing values
  const unsigned long frozenFoodStirTime = 20000;//
  const unsigned long frozenFoodFeedTime = 5000;// 5 second dose
  const unsigned long stirFeedDelay = 5000;//time between stir and dose start
  //logic control variables
  static boolean timing = false;
  static boolean startFeed = false;
  static boolean stopFeed = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(frozenFoodStirPin, On)
    Serial.print("Start Frozen Food Stir ");
    printTime();
  }

  if (millis() - timeStart >= stirFeedDelay and !startFeed)
  {
    Serial.print("Start Frozen Food Feed ");
    printTime();
    //digitalWrite(frozenFoodFeedPin, On)
    startFeed = true;
  }

  if (millis() - timeStart >= (stirFeedDelay + frozenFoodFeedTime) and !stopFeed)
  {
    Serial.print("Stop Frozen Food Feed ");
    printTime();
    //digitalWrite(frozenFoodFeedPin, Off)
    stopFeed = true;
  }

  if (millis() - timeStart >= frozenFoodStirTime)
  {
    Serial.print("Stop Frozen Food Stir ");
    printTime();
    timing = false;
    startFeed = false;
    stopFeed = false;
    runFrozenFoodCycle = false;
  }
}

void kalkCycle()
{
  //timing values
  const unsigned long kalkStirTime = 60000;//1 minute stir
  const unsigned long kalkFeedTime = 12000;//12 second dose
  const unsigned long stirFeedDelay = 7000;//time between stir and dose start
  //logic control variables
  static boolean timing = false;
  static boolean startFeed = false;
  static boolean stopFeed = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(kalkStirPin, On)
    Serial.print("Start KalkStir ");
    printTime();
  }

  if (millis() - timeStart >= stirFeedDelay and !startFeed)
  {
    Serial.print("Start KalkFeed ");
    printTime();
    //digitalWrite(kalkFeedPin, On)
    startFeed = true;
  }

  if (millis() - timeStart >= (stirFeedDelay + kalkFeedTime) and !stopFeed)
  {
    Serial.print("Stop KalkFeed ");
    printTime();
    //digitalWrite(kalkFeedPin, Off)
    stopFeed = true;
  }

  if (millis() - timeStart >= kalkStirTime)
  {
    Serial.print("Stop KalkStir ");
    printTime();
    timing = false;
    startFeed = false;
    stopFeed = false;
    runKalkCycle = false;
  }
}

void pumpCycle()
{
  //timing variables
  const unsigned long pumpOnTime = 80 * 60000UL; //1 Hr 20 min = 80 x 60000
  //logic control variables
  static boolean timing = false;
  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(pumpAPin, On)
    //digitalWrite(pumpBPin, On)
    Serial.print("Start Return Pumps ");
    printTime();
  }

  if (millis() - timeStart >= pumpOnTime)
  {
    //digitalWrite(pumpAPin, Off)
    //digitalWrite(pumpBPin, Off)
    Serial.print("Stop Return Pumps ");
    printTime();
    timing = false;
    runPumpCycle = false;
  }
}

void nightPumpCycle()
{
  //timing variables
  const unsigned long pumpOnTime = ((13 * 60) + 20) * 60000UL; //19:10 > 8:30  13 hr 20 min = ((13*60)+20)*60000UL
  //logic control variables
  static boolean timing = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(pumpAPin, On)
    //digitalWrite(pumpBPin, On)
    Serial.print("Start Night Return Pumps ");
    printTime();
  }
  if (millis() - timeStart >= pumpOnTime)
  {
    //digitalWrite(pumpAPin, Off)
    //digitalWrite(pumpBPin, Off)
    Serial.print("Stop Night Return Pumps ");
    printTime();
    timing = false;
    runNightPumpCycle = false;
  }
}

void printTime()
{
  if (Hour < 10)
    Serial.print('0');
  Serial.print(Hour);
  Serial.print(':');
  if (Minute < 10)
    Serial.print('0');
  Serial.print(Minute);
  Serial.print(':');
  if (Second < 10)
    Serial.print('0');
  Serial.println(Second);
}

Here's a slightly simplified version with the pump cycles combined into one routine.

#include <Time.h>
#include <TimeLib.h>
byte Hour;
byte Minute;
byte Second;
byte lastHour;
boolean runKalkCycle = false;
boolean runDryFoodCycle = false;
boolean runFrozenFoodCycle = false;
boolean runPumpCycle = false;

void setup()
{
  //accelerate timing for testing purposes
  //change Timer0 prescaler from default 64 to 1
  //comment out next line to go back to default timing
  TCCR0B = B00000001;
  //setTime(int hr,int min,int sec,int day, int month, int yr);
  setTime(8, 15, 00, 2, 1, 2019);//set time for Time library
  Serial.begin(115200);
  Serial.println("Starting Reef Tank Test");
  Hour = hour();
  Minute = minute();
  Second = second();
  printTime();
  lastHour = hour();
}

void loop()
{
  Hour = hour();
  Minute = minute();
  Second = second();

  if (Hour != lastHour) //kalk
  {
    lastHour = Hour;
    printTime();
    runKalkCycle = true;
  }

  if (Hour == 8 or Hour == 11 or Hour == 14 or Hour == 17)//dryFood
  {
    if (Minute == 30)
    {
      if (Second == 5) //was 0 don't trigger at 0 repet bug
      {
        runDryFoodCycle = true;
      }
    }
  }

  if (Hour == 10 or Hour == 13 or Hour == 16 or Hour == 19)//frozenFood
  {
    if (Minute == 5)
    {
      if (Second == 5)//was 0
      {
        runFrozenFoodCycle = true;
      }
    }
  }

  //pump on times
  if (Hour == 8 and Minute == 40 and Second == 0)  runPumpCycle = true;
  if (Hour == 10 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 11 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 13 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 14 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 16 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 17 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 19 and Minute == 10 and Second == 0) runPumpCycle = true;
 
  if (runPumpCycle == true)
  {
    pumpCycle();
  }

  if (runKalkCycle == true)
  {
    kalkCycle();
  }

  if (runDryFoodCycle == true)
  {
    dryFoodCycle();
  }

  if (runFrozenFoodCycle == true)
  {
    frozenFoodCycle();
  }
}//end loop

void dryFoodCycle()
{
  //timing variables
  const unsigned long dryFoodFeedTime = 10000;
  //logic control variables
  static boolean timing = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(dryFoodFeedPin, On)
    Serial.print("Start Dry Food Feed ");
    printTime();
  }

  if (millis() - timeStart >= dryFoodFeedTime)
  {
    //digitalWrite(dryFoodPin, off);
    Serial.print("Stop Dry Food Feed ");
    printTime();
    timing = false;
    runDryFoodCycle = false;
  }
}

void frozenFoodCycle()
{
  //timing values
  const unsigned long frozenFoodStirTime = 20000;//
  const unsigned long frozenFoodFeedTime = 5000;// 5 second dose
  const unsigned long stirFeedDelay = 5000;//time between stir and dose start
  //logic control variables
  static boolean timing = false;
  static boolean startFeed = false;
  static boolean stopFeed = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(frozenFoodStirPin, On)
    Serial.print("Start Frozen Food Stir ");
    printTime();
  }

  if (millis() - timeStart >= stirFeedDelay and !startFeed)
  {
    Serial.print("Start Frozen Food Feed ");
    printTime();
    //digitalWrite(frozenFoodFeedPin, On)
    startFeed = true;
  }

  if (millis() - timeStart >= (stirFeedDelay + frozenFoodFeedTime) and !stopFeed)
  {
    Serial.print("Stop Frozen Food Feed ");
    printTime();
    //digitalWrite(frozenFoodFeedPin, Off)
    stopFeed = true;
  }

  if (millis() - timeStart >= frozenFoodStirTime)
  {
    Serial.print("Stop Frozen Food Stir ");
    printTime();
    timing = false;
    startFeed = false;
    stopFeed = false;
    runFrozenFoodCycle = false;
  }
}

void kalkCycle()
{
  //timing values
  const unsigned long kalkStirTime = 60000;//1 minute stir
  const unsigned long kalkFeedTime = 12000;//12 second dose
  const unsigned long stirFeedDelay = 7000;//time between stir and dose start
  //logic control variables
  static boolean timing = false;
  static boolean startFeed = false;
  static boolean stopFeed = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(kalkStirPin, On)
    Serial.print("Start KalkStir ");
    printTime();
  }

  if (millis() - timeStart >= stirFeedDelay and !startFeed)
  {
    Serial.print("Start KalkFeed ");
    printTime();
    //digitalWrite(kalkFeedPin, On)
    startFeed = true;
  }

  if (millis() - timeStart >= (stirFeedDelay + kalkFeedTime) and !stopFeed)
  {
    Serial.print("Stop KalkFeed ");
    printTime();
    //digitalWrite(kalkFeedPin, Off)
    stopFeed = true;
  }

  if (millis() - timeStart >= kalkStirTime)
  {
    Serial.print("Stop KalkStir ");
    printTime();
    timing = false;
    startFeed = false;
    stopFeed = false;
    runKalkCycle = false;
  }
}

void pumpCycle()
{
  //timing variables
  static unsigned long pumpOnTime = 0;
  const unsigned long shortPumpOnTime = 80 * 60000UL; //1 Hr 20 min = 80 x 60000
  const unsigned long longPumpOnTime = ((13 * 60) + 20) * 60000UL; //19:10 > 8:30  13 hr 20 min = ((13*60)+20)*60000UL
  //logic control variables
  static boolean timing = false;
  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(pumpAPin, On)
    //digitalWrite(pumpBPin, On)
    Serial.print("Start Return Pumps ");
    printTime();
    if (Hour == 19)
      pumpOnTime = longPumpOnTime;
    else
      pumpOnTime = shortPumpOnTime;
  }

  if (millis() - timeStart >= pumpOnTime)
  {
    //digitalWrite(pumpAPin, Off)
    //digitalWrite(pumpBPin, Off)
    Serial.print("Stop Return Pumps ");
    printTime();
    timing = false;
    runPumpCycle = false;
  }
}

void printTime()
{
  if (Hour < 10)
    Serial.print('0');
  Serial.print(Hour);
  Serial.print(':');
  if (Minute < 10)
    Serial.print('0');
  Serial.print(Minute);
  Serial.print(':');
  if (Second < 10)
    Serial.print('0');
  Serial.println(Second);
}

Thank you cattledog, that's a brilliant sketch! The accelerated time is the best ever for checking it out on the serial monitor.

But there does seem to be a small glitch with turning the return pumps off. When the frozen food cycle runs at 10,13,16 and 19, often the return pumps turn off an hour early. But not always. That's according to the serial monitor anyway. I didn't see it happen with the dry food cycle.

I also have a request. During those four frozen food cycles, the kalk runs before the frozen food. Could you reverse that? The reason is, I need to use the kalkwasser to push the frozen food out of the dosing tube. Otherwise, the mixture of raw seafood will sit in the dosing tube for three hours getting rancid.

I also have a request. During those four frozen food cycles, the kalk runs before the frozen food. Could you reverse that? The reason is, I need to use the kalkwasser to push the frozen food out of the dosing tube. Otherwise, the mixture of raw seafood will sit in the dosing tube for three hours getting rancid.

The kalk runs on an hour value change and consequently the coding is very simple. Rather than complicate that, I have chosen to move the four frozen food dispense cycles 10 minutes earlier, so as to run 5 minutes before the hour instead of 5 minutes after the hour. Let me know if this works OK for the tank management. I'm thinking the fish will just start hanging out under the dispense tube 10 minutes earlier. :slight_smile:

there does seem to be a small glitch with turning the return pumps off. When the frozen food cycle runs at 10,13,16 and 19, often the return pumps turn off an hour early. But not always.

I just saw this in my serial monitor after moving the Frozen food ahead by the 10 minutes.

Start Return Pumps 14:40:00
15:00:00
Start KalkStir 15:00:00
Start KalkFeed 15:00:07
Stop KalkFeed 15:00:19
Stop KalkStir 15:01:00
Start Frozen Food Stir 15:55:05
Start Frozen Food Feed 15:55:10
Stop Frozen Food Feed 15:55:15
Stop Frozen Food Stir 15:55:25
Stop Return Pumps 15:00:00 >>reporting an hour early?
16:00:00
Start KalkStir 16:00:00
Start KalkFeed 16:00:07
Stop KalkFeed 16:00:19
Stop KalkStir 16:01:00
Start Return Pumps 16:10:00
17:00:00

I think the pump is actually turning off at 16:00:00, and the pump cycle which is based on a millis() timer is turning off while the hours in the time library are changing from 15 to 16, and the seconds and minutes have already rolled to 00. To test this, lets shorten the short pump cycles from 1:20:00 to 1:19:59 (subtract 1000 from the duration) and lets see how this is reported.

//V2 single point timing for loop, speeds up loop
//fix double action FrozenFood when seconds match to == 0 changed to 5
#include <Time.h>
#include <TimeLib.h>
byte Hour;
byte Minute;
byte Second;
byte lastHour;
boolean runKalkCycle = false;
boolean runDryFoodCycle = false;
boolean runFrozenFoodCycle = false;
boolean runPumpCycle = false;

void setup()
{
  //accelerate timing for testing purposes
  //change Timer0 prescaler from default 64 to 1
  //comment out next line to go back to default timing
  TCCR0B = B00000001;
  //setTime(int hr,int min,int sec,int day, int month, int yr);
  setTime(8, 15, 00, 2, 1, 2019);//set time for Time library
  Serial.begin(115200);
  Serial.println("Starting Reef Tank Test");
  Hour = hour();
  Minute = minute();
  Second = second();
  printTime();
  lastHour = hour();
}

void loop()
{ 
  Hour = hour();
  Minute = minute();
  Second = second();

  if (Hour != lastHour) //kalk
  {
    lastHour = Hour;
    printTime();
    runKalkCycle = true;
  }

  if (Hour == 8 or Hour == 11 or Hour == 14 or Hour == 17)//dryFood
  {
    if (Minute == 30)
    {
      if (Second == 5) //was 0 don't trigger at 0 repeat bug
      {
        runDryFoodCycle = true;
      }
    }
  }

  //if (Hour == 10 or Hour == 13 or Hour == 16 or Hour == 19)//frozenFood
   if (Hour == 9 or Hour == 12 or Hour == 15 or Hour == 18)//frozenFood
  {
    //if (Minute == 5)
    if (Minute == 55)
    {
      if (Second == 5)//was 0
      {
        runFrozenFoodCycle = true;
      }
    }
  }

  //pump on times
  if (Hour == 8 and Minute == 40 and Second == 0)  runPumpCycle = true;
  if (Hour == 10 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 11 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 13 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 14 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 16 and Minute == 10 and Second == 0) runPumpCycle = true;
  if (Hour == 17 and Minute == 40 and Second == 0) runPumpCycle = true;
  if (Hour == 19 and Minute == 10 and Second == 0) runPumpCycle = true;
 
  if (runPumpCycle == true)
  {
    pumpCycle();
  }

  if (runKalkCycle == true)
  {
    kalkCycle();
  }

  if (runDryFoodCycle == true)
  {
    dryFoodCycle();
  }

  if (runFrozenFoodCycle == true)
  {
    frozenFoodCycle();
  }
}//end loop

void dryFoodCycle()
{
  //timing variables
  const unsigned long dryFoodFeedTime = 10000;
  //logic control variables
  static boolean timing = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(dryFoodFeedPin, On)
    Serial.print("Start Dry Food Feed ");
    printTime();
  }

  if (millis() - timeStart >= dryFoodFeedTime)
  {
    //digitalWrite(dryFoodPin, off);
    Serial.print("Stop Dry Food Feed ");
    printTime();
    timing = false;
    runDryFoodCycle = false;
  }
}

void frozenFoodCycle()
{
  //timing values
  const unsigned long frozenFoodStirTime = 20000;
  const unsigned long frozenFoodFeedTime = 5000;// 5 second dose
  const unsigned long stirFeedDelay = 5000;//time between stir and dose start
  //logic control variables
  static boolean timing = false;
  static boolean startFeed = false;
  static boolean stopFeed = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(frozenFoodStirPin, On)
    Serial.print("Start Frozen Food Stir ");
    printTime();
  }

  if (millis() - timeStart >= stirFeedDelay and !startFeed)
  {
    Serial.print("Start Frozen Food Feed ");
    printTime();
    //digitalWrite(frozenFoodFeedPin, On)
    startFeed = true;
  }

  if (millis() - timeStart >= (stirFeedDelay + frozenFoodFeedTime) and !stopFeed)
  {
    Serial.print("Stop Frozen Food Feed ");
    printTime();
    //digitalWrite(frozenFoodFeedPin, Off)
    stopFeed = true;
  }

  if (millis() - timeStart >= frozenFoodStirTime)
  {
    Serial.print("Stop Frozen Food Stir ");
    printTime();
    timing = false;
    startFeed = false;
    stopFeed = false;
    runFrozenFoodCycle = false;
  }
}

void kalkCycle()
{
  //timing values
  const unsigned long kalkStirTime = 60000;//1 minute stir
  const unsigned long kalkFeedTime = 12000;//12 second dose
  const unsigned long stirFeedDelay = 7000;//time between stir and dose start
  //logic control variables
  static boolean timing = false;
  static boolean startFeed = false;
  static boolean stopFeed = false;

  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(kalkStirPin, On)
    Serial.print("Start KalkStir ");
    printTime();
  }

  if (millis() - timeStart >= stirFeedDelay and !startFeed)
  {
    Serial.print("Start KalkFeed ");
    printTime();
    //digitalWrite(kalkFeedPin, On)
    startFeed = true;
  }

  if (millis() - timeStart >= (stirFeedDelay + kalkFeedTime) and !stopFeed)
  {
    Serial.print("Stop KalkFeed ");
    printTime();
    //digitalWrite(kalkFeedPin, Off)
    stopFeed = true;
  }

  if (millis() - timeStart >= kalkStirTime)
  {
    Serial.print("Stop KalkStir ");
    printTime();
    timing = false;
    startFeed = false;
    stopFeed = false;
    runKalkCycle = false;
  }
}

void pumpCycle()
{
  //timing variables
  static unsigned long pumpOnTime = 0;
  //subtract 1 second from shortPumpOnTime to fix serial monitor hour value reporting glitch
  const unsigned long shortPumpOnTime = (80 * 60000UL) - 1000; //1 Hr 20 min = 80 x 60000 
  const unsigned long longPumpOnTime = ((13 * 60) + 20) * 60000UL; //19:10 > 8:30  13 hr 20 min = ((13*60)+20)*60000UL
  //logic control variables
  static boolean timing = false;
  static unsigned long timeStart;

  if (!timing)
  {
    timing = true;
    timeStart = millis();
    //digitalWrite(pumpAPin, On)
    //digitalWrite(pumpBPin, On)
    Serial.print("Start Return Pumps ");
    printTime();
    if (Hour == 19)
      pumpOnTime = longPumpOnTime;
    else
      pumpOnTime = shortPumpOnTime;
  }

  if (millis() - timeStart >= pumpOnTime)
  {
    //digitalWrite(pumpAPin, Off)
    //digitalWrite(pumpBPin, Off)
    Serial.print("Stop Return Pumps ");
    printTime();
    timing = false;
    runPumpCycle = false;
  }
}

void printTime()
{
  if (Hour < 10)
    Serial.print('0');
  Serial.print(Hour);
  Serial.print(':');
  if (Minute < 10)
    Serial.print('0');
  Serial.print(Minute);
  Serial.print(':');
  if (Second < 10)
    Serial.print('0');
  Serial.println(Second);
}

That's a really nice way to make the kalkwasser push the frozen food out of the dosing tube. Could the frozen food cycle be pushed ahead about four minutes? In the summer the dosing tube is exposed to very warm temperature and that seafood will ripen in a hurry.

Also, the return pumps on and off times seem stable, thanks. But they could use a tweak in the frozen food cycle. Can the return pumps be made to stop a few seconds before the frozen food cycle and start back up about 10 minutes later? The idea is to prevent food from going straight to filtration.

At some point you will maintain this program by yourself, so now is a good time to begin. Making changes will also help you develop an understanding of how the code works. I'll be available to help if you don't get things right, but with no live fish yet, I don't see the down side of any experimentation or mistakes.

From the previous code changes, you have a commented example for making changes to the frozen food timing, so that should be easy to modify so that the frozen food cycles start at xx:59:05 instead of xx:55:05.

Regarding the pumps and the frozen food cycles. There are 4 of the 7 short pump cycles which interact with the frozen food. With the frozen food back closer to the hour, it might be most simple to shorten the short pump cycles by two minutes so they run for 1:18:00 instead of 1:20:00, and will stop before the frozen food. This change actually stops the pumps right before the dry food as well, which looks correct for not pulling food away.

const unsigned long shortPumpOnTime = (78 * 60000UL); //1 Hr 18 min

If you want to keep some of the short pump cycles at 1:20:00, you can define a third pump interval following the model of the long and short interval.

I have found the cause of some of the anomalies where the change in hour could lag the roll over of seconds and minutes to 00. This is what made the pump off time appear to be an hour early, and it was the cause of not being able to start the food cycles on the :00 seconds without some repeating cycles. This change will synchronize all the hour/second/minute values to the same exact second in the time library. At the start of loop() make this change

  time_t t = now();//unix time; single time point for all values replaces the 3 separate function calls.
  Hour = hour(t);
  Minute = minute(t);
  Second = second(t);
  
  //Hour = hour();
  //Minute = minute();
  //Second = second();

With the above change, you can bring any of the food cycles to start on the even minute (second ==0) instead of the second ==5 if you think it makes things more intuitive.

Thank you so much!

That's a great sketch, I got the timing tuned. The comments were extremely helpful. :slight_smile:

But I'm having a little trouble getting the sketch to recognize the RTC. I commented out these two lines-

TCCR0B = B00000001;

setTime(8, 15, 00, 2, 1, 2019);//set time for Time library

Then I added this, #include <DS3232RTC.h> and when that didn't seem to make a difference I also added this, #include <Wire.h>

I copied and pasted both libraries from another sketch, could that be the problem?

After uploading the sketch, the serial monitor says Starting Reef Tank Test 00:00:00. When I send a new time to the RTC, nothing happens.

Here's how I recommend putting the RTC back into the sketch.

include the library

#include <Time.h>
#include <TimeLib.h>
#include <DS3232RTC.h>

Then change setup as follows. For convenience you will set the time for the time library as you currently do. Then you will set the RTC with the time you have entered, and then use the RTC as the synch provider for the time library.

void setup()
{
  //accelerate timing for testing purposes
  //change Timer0 prescaler from default 64 to 1
  //comment out next line to go back to default timing
  //TCCR0B = B00000001;
  //setTime(int hr,int min,int sec,int day, int month, int yr);
  Serial.begin(115200);
  Serial.println("Starting Reef Tank Test");
  setTime(11, 15, 00, 2, 1, 2019);//set time for Time library
  time_t t = now();//unix time for the set time
  RTC.set(t); //set the RTC to the entered time
  setSyncProvider(RTC.get);   // the function to sync the time library to the RTC
    if(timeStatus() != timeSet)
        Serial.println("Unable to sync with the RTC");
    else
        Serial.println("RTC has set the system time");
  t = now();//unix time
  Hour = hour(t);
  Minute = minute(t);
  Second = second(t);
  printTime();
  lastHour = Hour;
}

When the time is set, you can comment out the two lines which set the time and reload the sketch to prevent the time being set if there is a power loss or other reset.

void setup()
{
  //accelerate timing for testing purposes
  //change Timer0 prescaler from default 64 to 1
  //comment out next line to go back to default timing
  //TCCR0B = B00000001;
  //setTime(int hr,int min,int sec,int day, int month, int yr);
  Serial.begin(115200);
  Serial.println("Starting Reef Tank Test");
 // setTime(11, 15, 00, 2, 1, 2019);//set time for Time library
  time_t t = now();//unix time
 // RTC.set(t);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
    if(timeStatus() != timeSet)
        Serial.println("Unable to sync with the RTC");
    else
        Serial.println("RTC has set the system time");
  t = now();//unix time
  Hour = hour(t);
  Minute = minute(t);
  Second = second(t);
  printTime();
  lastHour = Hour;
}

Thanks a ton! I'll get after it in the morning.