Alarm.alarmRepeat not calling it's function

Hi, I am new to Aduino, and have only little programming experience.

I am using a Nano clone, with a DS1307 RTC.

In my Code below, I have been trying to get the StartWatering function to call with the Alarm.alarmRepeat function. I have been changing the time in the

Alarm.alarmRepeat(15,25,0,StartWatering); 

to a minute or two in the future, and recompiling each time I make a change, but each time the StartWatering is not called when the clock reaches the set alarm time.

/* 
=======================
Watering system control
=======================
- 5 watering zones garden
- 24VAC solenoid valves
- generic 8 channel relay board
- watering suppressed when raining.
- Manual switch to increase watering during heatwave
*/

#include <Wire.h>
#include <TimeLib.h>
#include <TimeAlarms.h>
#include <DS1307RTC.h>

bool HeatWave = LOW;            // initialise for normal weather
const int PlannedHour = 7;      //initialise with normal starts at 7am.
int StartHour = PlannedHour;    // Working variable for start time
const int BonusStartHour = 20;  // If needed a second watering in the evening at 20:00
const int nZones = 5;           // five zone watering system.
const int ZoneTime = 1000;      // default watering time for each zone (seconds)
int ZoneDuration = ZoneTime;
int PumpPin = 7;
int OnPin = 8;
int RainPin = 9;
int HeatWavePin = 10;
int ZonePins[] = {
  2,
  3,
  4,
  5,
  6,
};


void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;  // wait for serial
  delay(200);
  for (int i = 0; i < nZones; i++) {
    pinMode(ZonePins[i], OUTPUT);
    digitalWrite(ZonePins[i], HIGH);
  }
  pinMode(OnPin, INPUT);
  pinMode(RainPin, INPUT);
  pinMode(HeatWavePin, INPUT);
  pinMode(PumpPin, OUTPUT);
  digitalWrite(PumpPin, HIGH);
}

void StartWatering() {
  if (digitalRead(HeatWavePin) == HIGH) {
    ZoneDuration = ZoneTime * 1.5;
  } else {
    ZoneDuration = ZoneTime;
  }
  digitalWrite(PumpPin, LOW);
  for (int i = 0; i < nZones; i++) {
    digitalWrite(ZonePins[i], LOW);
    delay(ZoneDuration);
    digitalWrite(ZonePins[i], HIGH);
  }
  digitalWrite(PumpPin, HIGH);
}

void loop() {
  tmElements_t tm;
  RTC.read(tm);
  while (digitalRead(RainPin) == LOW) {
    Serial.print(tm.Hour);
    Serial.print(":");
    Serial.print(tm.Minute);
    Serial.print(":");
    Serial.println(tm.Second);
    Alarm.alarmRepeat(15,25,0,StartWatering);
    Alarm.delay(0);
    }
}

I have read a number of forum posts on the subject:

  • ChatGPT says the syntax is correct, and StartWatering() should be called.
    -I have tried adding Alarm.delay(0) both before, and after the alarm line.
    -I have tried commenting out all the Serial.Print lines which show me the Aduino's clock time, to avoid it interfering with timing.

I switched out the Alarm for

    if (tm.Hour == StartHour || (tm.Hour == BonusStartHour && digitalRead(HeatWavePin) == HIGH)) {
      StartWatering();
    }

And played with those StartHour variables, and that works, but then I have the problem with the loop starting a second watering cycle, if the total watering time of all zones is less than an hour, hence my desire to use the alarm function.

Thanks in advance for your assistance.
Mark

I am suspicious of your use of Alarm.delay(0) as the compiler may see this call as doing nothing and could have optimised it away

Try Alarm.delay(100); as an experiment

        Alarm.alarmRepeat(15, 25, 0, StartWatering);

Why is this in loop() rather than setup() ?

Does the example TimeAlarms sketch work ?

FWIW I've used the DS1307 and its younger brother DS3231 in many projects over many years.

I have had almost no occasion to use the alarm functions.

I suggest that you just forget about messing around with that function of the RTC and continue with the kind of logic you used in your switch out.

Ultimately it may prove more satisfying.

Getting the watering to follow your wishes is straight ahead simple code; if you've failed somehow at this stage it would probably be better to figure out the logical errors in your attempt.

Please post the code where you did jettison the alarm function and its use of a callback.

Please say more about how this is going to work once it does, like when and whether to water on what information.

General RTC tip - it may not matter, but it is better to see if a time has been reached or passed, rather than an exact march hh:mm:as.

Also, a nice trick is to calculate from hh:mm:as a variable called secondsSincdMidnkght, which is way more convenient and less trouble to code logically for comparing times.

I'll find an thread where that idea came up and was discussed well past death.

HTH

a7

This one

a7

I don't understand your while and why you would keep calling Alarm.alarmRepeat

the Alarm.delay(0); should be in the loop, not in the while

here is a basic example, it will trigger the alarm at the next full minute (will fail if the current minute is at 59 as I don't test for it, just bluntly add one)

(all libraries are not really available in Wokwi so I added tabs with the code and modified the includes to use "" instead of <> - you can just focus on the .ino sketch and ignore the rest)


the code for reference

#include "TimeLib.h"  // https://github.com/PaulStoffregen/Time
#include <Wire.h>
#include "DS1307RTC.h"  // https://github.com/PaulStoffregen/DS1307RTC
#include "TimeAlarms.h" // https://github.com/PaulStoffregen/TimeAlarms

void print2digits(int v) {
  if (v < 10) Serial.write('0');
  Serial.print(v);
}

void printClock() {
  Serial.print(hour());
  Serial.write(':');
  print2digits(minute());
  Serial.write(':');
  print2digits(second());
  Serial.write('\t');
  Serial.print(day());
  Serial.write('/');
  Serial.print(month());
  Serial.write('/');
  Serial.println(year());
}

void doSomething() {
  Serial.println(F("Alarm: it's time to water the plants"));
}

void setup() {
  Serial.begin(115200);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if (timeStatus() != timeSet) {
    Serial.println("Unable to sync with the RTC");
    while (true) delay(1);
  }

  tmElements_t tm;
  RTC.read(tm);

  Serial.print("Setting the alarm at ");
  Serial.print(tm.Hour);
  Serial.write(':');
  print2digits(tm.Minute+1);
  Serial.write(':');
  print2digits(0);
  Serial.println();

  Alarm.alarmRepeat(tm.Hour, tm.Minute + 1, 0, doSomething);

}

void loop() {
  printClock();
  Alarm.delay(1000);
}


Thanks for your Prompt Response Bob.

I tried Alarm(100) ...

      Alarm.alarmRepeat(16, 17, 0, StartWatering);
      Alarm.delay(100);

Made no difference.

I have the alarm in Loop, as once I get it going, I will replace the hour, with the Variable "StartHour", and use an "If" loop to start an hour later on the weekend, as the pump is annoyingly noisy.

The examples in the TimeAlarms Sketch work fine. Changed the one time alarm in the example to a minute in the future and it returned the alarm.

Copied the relevant bits fro the example into my code, without the Set.Time function and now I notice that clock print out starts at 00:00:00. I suspect that root cause was me assuming that

  RTC.read(tm);

has the same effect as:

setTime(16,24,45,1,1,11)

I added a line:

  setTime(tm.Hour, tm.Minute, tm.Second, tm.Month, tm.Day, tm.Year);

and now it works. Thanks for that.

Thanks guys. I was working through the example and missed your responses.

My idea for the While is that it is the rain inhibit. Whenever the rain sensor is active, the program just becomes "do nothing".

/* 
=======================
Watering system control
=======================
- 5 watering zones garden
- 24VAC solenoid valves
- generic 8 channel relay board
- watering suppressed when raining.
- Manual switch to increase watering during heatwave
*/

#include <Wire.h>
#include <TimeLib.h>
#include <TimeAlarms.h>
#include <DS1307RTC.h>

bool HeatWave = LOW;            // initialise for normal weather
const int PlannedHour = 16;     //initialise with normal starts at 7am.
int StartHour = PlannedHour;    // Working variable for start time
const int BonusStartHour = 20;  // If needed a second watering in the evening at 20:00
const int nZones = 5;           // five zone watering system.
const int ZoneTime = 1000;      // default watering time for each zone (seconds)
int ZoneDuration = ZoneTime;
int PumpPin = 7;
int OnPin = 8;
int RainPin = 9;
int HeatWavePin = 10;
int ZonePins[] = {
  2,
  3,
  4,
  5,
  6,
};

/* 
Need to initialise the serial interface, for use with the Serial monitor for trouble shooting, and also to interface with the RTC.
Otherwise just set up the pins
*/
void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;  // wait for serial
  Alarm.delay(200);
  for (int i = 0; i < nZones; i++) {
    pinMode(ZonePins[i], OUTPUT);
    digitalWrite(ZonePins[i], HIGH);
  }
  pinMode(OnPin, INPUT);
  pinMode(RainPin, INPUT);
  pinMode(HeatWavePin, INPUT);
  pinMode(PumpPin, OUTPUT);
  digitalWrite(PumpPin, HIGH);
}

/*
Once the start time for watering is called, based on Heat, decide whether to perform longer duration watering.
The Pump has to be turned on for the full duration, and the Zones get cycled through.
*/
void StartWatering() {
  if (digitalRead(HeatWavePin) == HIGH) {
    ZoneDuration = ZoneTime * 1.5;
  } else {
    ZoneDuration = ZoneTime;
  }
  digitalWrite(PumpPin, LOW);
  for (int i = 0; i < nZones; i++) {
    digitalWrite(ZonePins[i], LOW);
    delay(ZoneDuration);
    digitalWrite(ZonePins[i], HIGH);
  }
  digitalWrite(PumpPin, HIGH);
}

/*
As the watering system will run for years, the RTC doesn't only get interogated one time on setup, but drift in the Aduino's oscillator is avoided by resyncing to the RTC constantly.
The during heatwaves, the system should perform a second watering in the evening.
The morning start time is shifted back an hour on the weekend.
The While is there to make the program go into "Do nothing" mode when the rain sensor is active.
*/

void loop() {
  tmElements_t tm;
  RTC.read(tm);
  setTime(tm.Hour, tm.Minute, tm.Second, tm.Month, tm.Day, tm.Year);
  while (digitalRead(RainPin) == LOW) {
    if (tm.Wday == 0 || tm.Wday == 6) {
      StartHour = PlannedHour + 1;
    } else {
      StartHour = PlannedHour;
    }
    Alarm.alarmRepeat(StartHour, 0, 0, StartWatering);
    if (digitalRead(HeatWavePin) == HIGH) {
      Alarm.alarmRepeat(BonusStartHour, 0, 0, StartWatering);
    }
  }
}

This now works when I use Alarm.alarmRepeat (16, 42, 0, StartWatering);, but if I put my variable StartHour instead of the 16, it doesn't.

I still don't get the logic.

you have a planned watering say at 7am
what should the system do if it has been raining that day (before the watering time) ?

It is an inhibit. If the sensor is wet, the RainSensor pin will be high, and the rest of the loop is skipped. So it just sits in the Do nothing loop, until that sensor dries back out. Could be inhibited for days.

I'll give you an example:

  • assume your startHour is 7am
  • say it has been raining for 3 days
  • say rain stops at 6:45am

➜ by 7am the sensor is dry. Do you trigger watering (the soil is probably soaked from the 3 days of rain)?

  • now at 7:01 it starts raining again.

➜ do you keep watering under the rain?

Rain sensors dont dry out that fast. I have a well, and live on a river flood plain, so my watering is moving water up about 7M vertically. If the extreme case occurs and the watering happens when the ground is already moist, or if it starts raining during the cycle, that water will just run back down through the soil to where it came from.
Not a problem. This does not need to be perfect. I am not going to get into soil moisture sensors etc.

Ok makes sense

Oh, well then it must be true! :rofl:

Yeah, I was really happy to find out that AI decided my non-functional code was in fact correct :smirk:

trust but verify is still the norm... :slight_smile:

It is pretty impressive how well it can interpret the code and comments though.

There's a big gap between a syntactically correct program, and a program that does what you want.

Ask me how I know. :expressionless:

A compiler makes a better syntax checker than chatGPT. So far as we've seen, humans make code that does what we want better than chatGPT.

So if you aren't using chatGPT to write code for you, using it is without a point.

Someone who knows how to program and is willing to meet chatGPT way more than halfway might be able to leverage AI as a productivity tool. At least until it starts to hallucinate. For everyone else, it is a waste of time. For the person using it to write code, if that's what they do, and for the ppl,who have to fix its output or reject it entirely.

a7

I was curious, as I'd never used it before. I figured I would try it and see if anything useful came out. It didn't provide any help in this case, except that Serial.print() can give you timing issues,

well it's not purely a syntax checker.

for example I just asked chatGPT this question


what's wrong with my bublesort function?

void bubbleSort(int array[], int size) {

  // loop to access each array element
  for (int step = 0; step < (size - 1); ++step) {

    // check if swapping occurs
    int swapped = 0;

    // loop to compare two elements
    for (int i = 0; i < (size - step - 1); ++i) {

      // compare two array elements
      // change > to < to sort in descending order
      if (array[i] > array[i + 1]) {

        // swapping occurs if elements
        // are not in intended order
        array[i] = array[i + 1];
        int temp = array[i];
        array[i + 1] = temp;

        swapped = 1;
      }
    }

    // no swapping means the array is already sorted
    // so no need of further comparison
    if (swapped == 0)
      break;
  }
}

the compiler would happily compile that as the syntax is correct

but chatGPT told me


Your bubble sort implementation seems mostly correct, but there is a small mistake in the swapping step. You are updating the values incorrectly during the swap. To fix this, you need to swap the elements correctly by using a temporary variable to hold one of the values during the swap process. Here's the corrected version of your code:

void bubbleSort(int array[], int size) {
    // loop to access each array element
    for (int step = 0; step < (size - 1); ++step) {
        // check if swapping occurs
        int swapped = 0;

        // loop to compare two elements
        for (int i = 0; i < (size - step - 1); ++i) {
            // compare two array elements
            // change > to < to sort in descending order
            if (array[i] > array[i + 1]) {
                // swapping occurs if elements
                // are not in intended order
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;

                swapped = 1;
            }
        }

        // no swapping means the array is already sorted
        // so no need for further comparison
        if (swapped == 0)
            break;
    }
}


Now, the swapping should work correctly, and your bubble sort algorithm should sort the array in ascending order. If you want to sort the array in descending order, as mentioned in the comment, you can change the condition in the inner loop to

array[i] < array[i + 1].


So it did find the bug I had (knowingly) introduced by swapping two lines and offered an advice if I wanted to sort the other way around.

My compiler does not do that.

Thanks everyone for their assistance. This is the final version of the software. I have tested it on the bench with the system bread boarded. This will be replacing an existing watering computor which had a battery leak, so all of the pump and zone valves are already installed and functioning.

/* 
=======================
Watering system control
=======================
- 5 watering zones garden
- 24VAC solenoid valves
- generic 8 channel relay board
- watering suppressed when raining.
- Manual switch to increase watering during heatwave
*/

#include <TimeLib.h>
#include <TimeAlarms.h>
#include <DS1307RTC.h>

const int PLANNEDHOUR = 7;     //initialise with normal starts at 7am.
int StartHour = PLANNEDHOUR;    // Working variable for start time
const int BONUSSTARTHOUR = 13;  // If needed a second watering in the evening at 20:00
const int NUMBEROFZONES = 5;    // five zone watering system.
const int ZONETIME = 600;      // default watering time for each zone (seconds)
int ZoneDuration = ZONETIME;
int PumpPin = 7;
int OnPin = 8;
int RainPin = 9;
int HeatWavePin = 10;
int ZonePins[] = {
  2,
  3,
  4,
  5,
  6,
};

/* 
Need to initialise the serial interface, for use with the serial monitor for trouble shooting, and also to interface with the RTC.
Otherwise just set up the pins
*/
void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;  // wait for serial
  Alarm.delay(200);
  for (int i = 0; i < NUMBEROFZONES; i++) {
    pinMode(ZonePins[i], OUTPUT);
    digitalWrite(ZonePins[i], HIGH);
  }
  pinMode(OnPin, INPUT);
  pinMode(RainPin, INPUT);
  pinMode(HeatWavePin, INPUT);
  pinMode(PumpPin, OUTPUT);
  digitalWrite(PumpPin, HIGH);
}

/*
Once the start time for watering is called, based on a manually set heat wave switch position, decide whether to perform longer duration watering.
The pump has to be turned on for the full duration, and the zones get cycled through.
*/
void StartWatering() {
  if (digitalRead(HeatWavePin) == HIGH) {
    ZoneDuration = ZONETIME * 1.5;
  } else {
    ZoneDuration = ZONETIME;
  }
  digitalWrite(PumpPin, LOW);
  for (int i = 0; i < NUMBEROFZONES; i++) {
    digitalWrite(ZonePins[i], LOW);
    delay(ZoneDuration * 1000);
    digitalWrite(ZonePins[i], HIGH);
  }
  digitalWrite(PumpPin, HIGH);
}




/*
As the watering system will run for years, the RTC doesn't only get interogated one time on setup, but drift in the Aduino's oscillator is avoided by resyncing to the RTC constantly.
The "While" is there to make the program go into "Do nothing" mode when the rain sensor is active.
The during heatwaves, the system should perform a second watering in the evening.
The morning start time is shifted back an hour on the weekend, as the pump is annoyingly noisy.

*/

void loop() {
  tmElements_t tm;
  RTC.read(tm);
  setTime(tm.Hour, tm.Minute, tm.Second, tm.Month, tm.Day, tm.Year);
  while (digitalRead(RainPin) == LOW) {
    if (tm.Wday == 0 || tm.Wday == 6) {
      StartHour = PLANNEDHOUR + 1;
    } else {
      StartHour = PLANNEDHOUR;
    }
    Alarm.alarmRepeat(StartHour, 0, 0, StartWatering);
    if (digitalRead(HeatWavePin) == HIGH) {
      Alarm.alarmRepeat(BONUSSTARTHOUR, 0, 0, StartWatering);
    }
  }
}