Problem with checking level sensor during Alarm on period using DS3231

I am trying to design an irrigation system that is triggered by an RTC.
Alarm 1 switches the relay ON (Will eventually control a pump).
However if the tank runs out of water I want to switch the pump OFF.
I am able to check the water level at the start of the Alarm period, but I want it to check it all the time the pump is ON.
How can I get it to check the level constantly i.e. all the time btween the 2 alarm triggers?

// rtc_ds3231_alarm7 (In development)
// during an alarm the INT pin of the RTC is pulled low
//SDA - pin A4
// SCL - pin A5
// this is handy for minimizing power consumption for sensor-like devices, 
// since they can be started up by this pin on given time intervals.

#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 256
// time when to wake up
uint8_t wake1_HOUR = 21;
uint8_t wake1_MINUTE = 38;
uint8_t wake1_SECOND = 00;

uint8_t wake2_HOUR = 21;
uint8_t wake2_MINUTE = 39;

int relaypin = 10;
int SQWpin = 8;
int rainSensePin = A0;
int curCounter = 0;
int sensorValue = 0;

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change :
unsigned long prev = 1000, interval=1000;
void set_alarm(void){
    // flags define what calendar component to be checked against the current time in order
    // to trigger the alarm - see datasheet
    // A1M1 (seconds) (0 to enable, 1 to disable)
    // A1M2 (minutes) (0 to enable, 1 to disable)
    // A1M3 (hour)    (0 to enable, 1 to disable) 
    // A1M4 (day)     (0 to enable, 1 to disable)
    // DY/DT          (dayofweek == 1/dayofmonth == 0)
    uint8_t flags1[5] = { 0, 0, 0, 1, 1 };
    
    // A2M2 (minutes) (0 to enable, 1 to disable)
    // A2M3 (hour)    (0 to enable, 1 to disable) 
    // A2M4 (day)     (0 to enable, 1 to disable) 
    uint8_t flags2[3] = {0, 0, 1};
    
    // set Alarm1
    DS3231_set_a1(wake1_SECOND, wake1_MINUTE, wake1_HOUR, 0, flags1);

    // activate Alarm1
    DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);
    
    // set Alarm2
    DS3231_set_a2(wake2_MINUTE, wake2_HOUR, 0, flags2);

    // activate Alarm2
    DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);
    
}

void setup() {
    Serial.begin(9600);
    Wire.begin();
    pinMode(SQWpin, INPUT);
    pinMode(relaypin, OUTPUT);
    pinMode(rainSensePin, INPUT);
    DS3231_init(DS3231_INTCN);
    DS3231_clear_a1f();
    DS3231_clear_a2f();
    set_alarm();
}

void loop()
{
    char buff[BUFF_MAX];
    unsigned long now = millis();

    struct ts t;

    // once a while show what is going on
    if ((now - prev > interval) && (Serial.available() <= 0)) {        DS3231_get(&t);

        // display current time
        snprintf(buff, BUFF_MAX, "%d.%02d.%02d %02d:%02d:%02d", t.year,
             t.mon, t.mday, t.hour, t.min, t.sec);
        Serial.println(buff);

        // display a1 debug info
      //  DS3231_get_a1(&buff[0], 59);
      //  Serial.println(buff);

        if (DS3231_triggered_a1()) {
            // INT/SQW has been pulled low
            Serial.println(" -> alarm1 has been triggered");
            int rainSenseReading = analogRead(rainSensePin);
Serial.println(rainSenseReading);
  if (rainSenseReading < 500){
    digitalWrite(relaypin, LOW);
  }
  else if (rainSenseReading > 500){
  digitalWrite(relaypin, HIGH);}

 DS3231_clear_a1f();
        }
if (DS3231_triggered_a2()) {
            // INT/SQW has been pulled low
            Serial.println(" -> alarm2 has been triggered");
digitalWrite(relaypin, LOW);
 DS3231_clear_a2f();
        }
prev = now;
    }
}

Your help would be appreciated!

I have used a very simple water level detector. Two stainless steel rods parallel to each other and, set 1/4 in. apart. When they're immersed in water, a current flows. If the water drops below the bottom, much less current flows (they're still wet, so some current get across). Use this as a variable resistor, to detect when the water level is below the rods.

Instead of directly setting the relay on detecting an alarm event, set a Boolean status variable say irrigationActive.

On each loop iteration, check the water level and activate the relay accordingly.

if ( irrigationActive == true && waterLevelOk == true ) { digitalWrite(relaypin, HIGH); } else { digitalWrite(relaypin, LOW); }

You have another, similar thread https://forum.arduino.cc/index.php?topic=419729.0 Why not keep it all together?

I change to a different post because I had a different problem!

Regarding the suggestions above, I have tried implementing them in my code, but I am still not getting the correct responce.
When the Alarm1 time is reached it does switch the relay and the state of the alarm changes to 1 (TRUE) at the precise time i.e. it only stays high for 1 sec.
When the Alarm2 time is reached, it does not switch the relay off, but monitoring the state shows that the Alarm2 state does change to 1 and continues infinitum. See below:

1916.08.28 17:50:59
638
0
0
1916.08.28 17:51:00
639
1
0
→ alarm1 has been triggered
1916.08.28 17:51:01

644
0
0
1916.08.28 17:51:02
646
0
0

Then at Alarm2 trigger time:
1916.08.28 17:51:59
553
0
0
1916.08.28 17:52:00
553
0
1
1916.08.28 17:52:01
552
0
1
1916.08.28 17:52:02
553
0
1

I think that the code runs through the Alarm1 if statement, but does not action the Alarm2 if statement.
Note the Alarm2 state is caught on the line after Alarm1 is printed and not in the Alarm2 loop.
I’m afraid I am not clever enough to figure out where I am going wrong.
I’m sure it’s something to do with the way I am nesting the statements. Can anyone help me?
Here is the code:

// rtc_ds3231_alarm7 (Working with sensor)
// during an alarm the INT pin of the RTC is pulled low
//SDA - pin A4
// SCL - pin A5
// this is handy for minimizing power consumption for sensor-like devices, 
// since they can be started up by this pin on given time intervals.

#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 256
// time when to wake up
uint8_t wake1_HOUR = 18;
uint8_t wake1_MINUTE = 10;
uint8_t wake1_SECOND = 00;

uint8_t wake2_HOUR = 18;
uint8_t wake2_MINUTE = 11;
int relaypin = 10;
int SQWpin = 8;
int tankSensePin = A0;
int curCounter = 0;
int sensorValue = 0;
boolean Alarm1On=false;
boolean Alarm2On=false;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change :
unsigned long prev = 1000, interval=1000;
void set_alarm(void){
    // flags define what calendar component to be checked against the current time in order
    // to trigger the alarm - see datasheet
    // A1M1 (seconds) (0 to enable, 1 to disable)
    // A1M2 (minutes) (0 to enable, 1 to disable)
    // A1M3 (hour)    (0 to enable, 1 to disable) 
    // A1M4 (day)     (0 to enable, 1 to disable)
    // DY/DT          (dayofweek == 1/dayofmonth == 0)
    uint8_t flags1[5] = { 0, 0, 0, 1, 1 };
    
    // A2M2 (minutes) (0 to enable, 1 to disable)
    // A2M3 (hour)    (0 to enable, 1 to disable) 
    // A2M4 (day)     (0 to enable, 1 to disable) 
    uint8_t flags2[3] = {0, 0, 1};
    
    // set Alarm1
    DS3231_set_a1(wake1_SECOND, wake1_MINUTE, wake1_HOUR, 0, flags1);

    // activate Alarm1
    DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);
    
    // set Alarm2
    DS3231_set_a2(wake2_MINUTE, wake2_HOUR, 0, flags2);

    // activate Alarm2
    DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);
    
}

void setup() {
    Serial.begin(9600);
    Wire.begin();
    pinMode(SQWpin, INPUT);
    pinMode(relaypin, OUTPUT);
    pinMode(tankSensePin, INPUT);
    DS3231_init(DS3231_INTCN);
    DS3231_clear_a1f();
    DS3231_clear_a2f();
    set_alarm();
}

void loop()
{
    char buff[BUFF_MAX];
    unsigned long now = millis();

    struct ts t;

    // once a while show what is going on
    if ((now - prev > interval) && (Serial.available() <= 0)) {        
      DS3231_get(&t);
// display current time
        snprintf(buff, BUFF_MAX, "%d.%02d.%02d %02d:%02d:%02d", t.year,
             t.mon, t.mday, t.hour, t.min, t.sec);
        Serial.println(buff);
boolean Alarm1On=DS3231_triggered_a1();
boolean Alarm2On=DS3231_triggered_a2();
int tankSenseReading = analogRead(tankSensePin);
Serial.println(tankSenseReading);
Serial.println(Alarm1On); 
            Serial.println(Alarm2On); 
if (Alarm1On == true && tankSenseReading >500) {
            // INT/SQW has been pulled low
Serial.println(" -> alarm1 has been triggered");            
digitalWrite(relaypin, HIGH);
if (Alarm2On == true) {
            // INT/SQW has been pulled low
            Serial.println(Alarm2On); 
            Serial.println(" -> alarm2 has been triggered");
digitalWrite(relaypin, LOW);
}

 DS3231_clear_a2f();
 DS3231_clear_a1f();
}
prev = now;
    }
}

OK. I’ve had a look. There may be a better way of doing it, but this is what I have done.
It keeps the status of the alarms in 2 global variables myAlarm1On and myAlarm2On. It is these global variables which control the relay. I assume that alarm1 cancels alarm2 and alarm2 cancels alarm1.

I haven’t got your ds3231 library installed so I can’t check it.

// rtc_ds3231_alarm7 (Working with sensor)
// during an alarm the INT pin of the RTC is pulled low
//SDA - pin A4
// SCL - pin A5
// this is handy for minimizing power consumption for sensor-like devices,
// since they can be started up by this pin on given time intervals.

#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 256
// time when to wake up
uint8_t wake1_HOUR = 18;
uint8_t wake1_MINUTE = 10;
uint8_t wake1_SECOND = 00;

uint8_t wake2_HOUR = 18;
uint8_t wake2_MINUTE = 11;
int relaypin = 10;
int SQWpin = 8;
int tankSensePin = A0;
int curCounter = 0;
int sensorValue = 0;
boolean Alarm1On = false;
boolean Alarm2On = false;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change :
unsigned long prev = 1000, interval = 1000;
void set_alarm(void) {
  // flags define what calendar component to be checked against the current time in order
  // to trigger the alarm - see datasheet
  // A1M1 (seconds) (0 to enable, 1 to disable)
  // A1M2 (minutes) (0 to enable, 1 to disable)
  // A1M3 (hour)    (0 to enable, 1 to disable)
  // A1M4 (day)     (0 to enable, 1 to disable)
  // DY/DT          (dayofweek == 1/dayofmonth == 0)
  uint8_t flags1[5] = { 0, 0, 0, 1, 1 };

  // A2M2 (minutes) (0 to enable, 1 to disable)
  // A2M3 (hour)    (0 to enable, 1 to disable)
  // A2M4 (day)     (0 to enable, 1 to disable)
  uint8_t flags2[3] = {0, 0, 1};

  // set Alarm1
  DS3231_set_a1(wake1_SECOND, wake1_MINUTE, wake1_HOUR, 0, flags1);

  // activate Alarm1
  DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);

  // set Alarm2
  DS3231_set_a2(wake2_MINUTE, wake2_HOUR, 0, flags2);

  // activate Alarm2
  DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);

  // alarm status - persistent
  boolean myAlarm1On = false ;
  boolean myAlarm2On = false ;
}

void setup() {
  Serial.begin(9600);
  Wire.begin();
  pinMode(SQWpin, INPUT);
  pinMode(relaypin, OUTPUT);
  pinMode(tankSensePin, INPUT);
  DS3231_init(DS3231_INTCN);
  DS3231_clear_a1f();
  DS3231_clear_a2f();
  set_alarm();
}

void loop()
{
  char buff[BUFF_MAX];
  unsigned long now = millis();

  struct ts t;

  // once a while show what is going on
  if ((now - prev > interval) && (Serial.available() <= 0)) {
    DS3231_get(&t);
    // display current time
    snprintf(buff, BUFF_MAX, "%d.%02d.%02d %02d:%02d:%02d", t.year,
             t.mon, t.mday, t.hour, t.min, t.sec);
    Serial.println(buff);
    boolean Alarm1On = DS3231_triggered_a1();
    boolean Alarm2On = DS3231_triggered_a2();
    int tankSenseReading = analogRead(tankSensePin);
    Serial.println(tankSenseReading);
    Serial.println(Alarm1On);
    Serial.println(Alarm2On);
    if (Alarm1On == true ) {
      // INT/SQW has been pulled low
      Serial.println(" -> alarm1 has been triggered");
      myAlarm1On = true ;
      myAlarm2On = false ;
      DS3231_clear_a1f();
    }

    if (Alarm2On == true) {
      // INT/SQW has been pulled low
      Serial.println(Alarm2On);
      Serial.println(" -> alarm2 has been triggered");
      myAlarm2On = true ;
      myAlarm1On = false ;
      DS3231_clear_a2f();
    }

    prev = now;

    // set relay
    if ( myAlarm1On && tankSenseReading > 500) ) {
      Serial.println(" -> relay on");
      digitalWrite(relaypin, HIGH);
    }
    else {
      Serial.println(" -> relay off");
      digitalWrite(relaypin, LOW);
    }

  }

}

Hi 6v6gt Tried that, but at Alarm 1 it again only switched the relay on for 1 sec (see below). Alarm 2 is triggered, but as the relay was already off could not see if that was working correctly.

To make the code work I had to move the lines from just before void setup(), to just after void loop. Oh! there was a superfluous ) at the end of the line after Set relay.

What I need is like a electronic flip-flop, to change the logic level on the first pulse (Alarm1) and to the opposite state when Alarm 2 is triggered. Can this behaviour be mimic'ed in C++?

1916.08.29 11:00:58 620 -> relay off 1916.08.29 11:00:59 620 -> relay off 1916.08.29 11:01:00 619 -> alarm1 has been triggered -> relay on 1916.08.29 11:01:01 622 -> relay off 1916.08.29 11:01:02 617 -> relay off

-> relay off 1916.08.29 11:01:58 580 -> relay off 1916.08.29 11:01:59 579 -> relay off 1916.08.29 11:02:00 578 1 -> alarm2 has been triggered -> relay off 1916.08.29 11:02:01 578

Hi 6v6gt
Right! Started thinking about this as a logic problem, rather than trying to put it into code.
I came up with this dediction: I needed to keep the if statement to be true, during the time period I set.
For purposes of these experiments 1 min or 60000mS.
Thge logic I used:
Is TankSensorReading > 500 && millis() > StartTime && millis < EndTime
I had to create 2 new variables> StartTime and Endtime.
To find StartTime I had to read the millis() when the alarm was triggered
For the EndTime I added 60000 to the StartTime.
To make sure that this was calculated at the right time, I found thgat this had to go right after the start of void loop(). It also had to only do this when the Alarm 1 was triggered. Here is the statement I wrote
if(DS3231_triggered_a1() == true){
StartTime=millis();
EndTime=StartTime+60000;
That seemed to work perfectly, using both Alarms, Alarm1 to turn ON Alarm2 to turn OFF.
You could say that the problem is solved, but I think even this could be improved!

  • Could the EndTime be used to switch OFF whilst still in the Alarm1 loop?
  • If the above works, then I could set 2 alarm periods
  • I have a problem, that when I am setting the time in the minutes and it happens to be 08, it comes up with ‘invalid digit “8” in octal constant’, Why?
  • Lastly, its been bugging me, but its not important. Why does the time display the year as 1916?

Any further comments about the above points, would be helpful!
Here is the code so far. I have commented out the bits that are no longer used.

// rtc_ds3231_alarm7 (Working with sensor)
// during an alarm the INT pin of the RTC is pulled low
//SDA - pin A4
// SCL - pin A5
// this is handy for minimizing power consumption for sensor-like devices,
// since they can be started up by this pin on given time intervals.

#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 256
// time when to wake up
uint8_t wake1_HOUR = 15;
uint8_t wake1_MINUTE = 08;
uint8_t wake1_SECOND = 00;

uint8_t wake2_HOUR = 15;
uint8_t wake2_MINUTE = 59;
int relaypin = 10;
int SQWpin = 8;
int tankSensePin = A0;
int curCounter = 0;
int sensorValue = 0;
boolean Alarm1On = false;
boolean Alarm2On = false;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change :
unsigned long prev = 1000, interval = 1000;
void set_alarm(void) {
  // flags define what calendar component to be checked against the current time in order
  // to trigger the alarm - see datasheet
  // A1M1 (seconds) (0 to enable, 1 to disable)
  // A1M2 (minutes) (0 to enable, 1 to disable)
  // A1M3 (hour)    (0 to enable, 1 to disable)
  // A1M4 (day)     (0 to enable, 1 to disable)
  // DY/DT          (dayofweek == 1/dayofmonth == 0)
  uint8_t flags1[5] = { 0, 0, 0, 1, 1 };

  // A2M2 (minutes) (0 to enable, 1 to disable)
  // A2M3 (hour)    (0 to enable, 1 to disable)
  // A2M4 (day)     (0 to enable, 1 to disable)
  uint8_t flags2[3] = {0, 0, 1};

  // set Alarm1
  DS3231_set_a1(wake1_SECOND, wake1_MINUTE, wake1_HOUR, 0, flags1);

  // activate Alarm1
  DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);

  // set Alarm2
  DS3231_set_a2(wake2_MINUTE, wake2_HOUR, 0, flags2);

  // activate Alarm2
  DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);

  // alarm status - persistent
//  boolean myAlarm1On = false ;
//  boolean myAlarm2On = false ;
}

void setup() {
  Serial.begin(9600);
  Wire.begin();
  pinMode(SQWpin, INPUT);
  pinMode(relaypin, OUTPUT);
  pinMode(tankSensePin, INPUT);
  DS3231_init(DS3231_INTCN);
  DS3231_clear_a1f();
  DS3231_clear_a2f();
  set_alarm();
}

void loop()
{
    unsigned long StartTime;
    unsigned long EndTime;
if(DS3231_triggered_a1()== true){    
   StartTime=millis();
   EndTime=StartTime+60000;
   Serial.println(StartTime);
   Serial.println(EndTime); 
} 
//boolean myAlarm1On = false ;
//boolean myAlarm2On = false ;  
  char buff[BUFF_MAX];
  unsigned long now = millis();

   boolean Alarm2On = false;
  struct ts t;

  // once a while show what is going on
  if ((now - prev > interval) && (Serial.available() <= 0)) {
    DS3231_get(&t);
    // display current time
    snprintf(buff, BUFF_MAX, "%d.%02d.%02d %02d:%02d:%02d", t.year,
             t.mon, t.mday, t.hour, t.min, t.sec);
             Serial.println(buff);   
//boolean Alarm1On = DS3231_triggered_a1();
boolean Alarm2On = DS3231_triggered_a2();

    int tankSenseReading = analogRead(tankSensePin);
    Serial.println(tankSenseReading);
   
    if (tankSenseReading>500 && millis() > StartTime && millis() < EndTime) {
      // INT/SQW has been pulled low
      Serial.println(" -> alarm1 has been triggered");
      digitalWrite(relaypin, HIGH);
//      myAlarm1On = true ;
//      myAlarm2On = false ;
       Serial.println(StartTime);
       Serial.println(EndTime);
      DS3231_clear_a1f();
    }

    if (Alarm2On == true) {
      // INT/SQW has been pulled low
      Serial.println(Alarm2On);
      Serial.println(" -> alarm2 has been triggered");
      digitalWrite(relaypin, LOW);
//      myAlarm2On = true ;
//      myAlarm1On = false ;
      DS3231_clear_a2f();
    }

    prev = now;
/*
    // set relay
  
    if ( myAlarm1On && tankSenseReading > 500) {
      Serial.println(" -> relay on");
      digitalWrite(relaypin, HIGH);
    }
    else {
      Serial.println(" -> relay off");
      digitalWrite(relaypin, LOW);
    }
*/
  }

}

I have a problem, that when I am setting the time in the minutes and it happens to be 08, it comes up with 'invalid digit "8" in octal constant', Why?

Because the leading 0 tells the compiler that the value is in base 8. There are no 10s in base 10, and there are no 8s in base 8.

Do NOT use leading 0s in values unless you know what you are doing.

Thanks for that! Now I know I wont do that.
Been working on the problem point 1.

I thought that this if statement would work:
if(millis()( > EndTime){
digitalWrite(relaypin, LOW)}
but it did not.
I managed to get the Endtime to print and found that each cycle it updates by 1000, so consequently millis > Endtime is never reached. Also makes the millis() < Endtime irrelivant in the if statement at the start of Alarm1.
How can I catch the value for EndTime and preserve it, at least for the duration that the period I’ve set?

How can I catch the value for EndTime and preserve it

Based on the snippets of code that can't possibly compile, you can't. Post some real code that will compile if you want help.

I'm just looking at posts #5 and #6 at the moment to try to see what the problem might be. Strange is the following. Although the status of both Alarm1On and Alarm2On should appear, each on a separate line as either a "0" or as a "1" (Boolean) , I see only the value of the tankSenseReading (620, 619 etc.)

int tankSenseReading = analogRead(tankSensePin);
Serial.println(tankSenseReading);
Serial.println(Alarm1On);
Serial.println(Alarm2On);

This is what you have said you have seen:

1916.08.29 11:00:58
620
 -> relay off
1916.08.29 11:00:59
620
 -> relay off
1916.08.29 11:01:00
619
 -> alarm1 has been triggered
 -> relay on
1916.08.29 11:01:01
622
 -> relay off
1916.08.29 11:01:02
617
 -> relay off

I don't understand what has happened if the results you have posted are from the code I supplied.

My recommendation... take some time to learn about state machines and then use this library to rewrite your project. In the end it will be far easier to code and maintain.

SM library

Thanks 6v6gt I have moved on from there, as the code did not work. I have made post #7 work, so your comments about posts #5/6 are irrelivant. Post#7 works, but uses both alarms. Post#9 I explained what had tried and had not worked.

PaulS As stated above post#7 IS a working copy that has compiled and runs on my Arduino UNO.

George Matthews I will certainly look into State Machines. The link gives the library and some examples, but are there any explainitary articles that I can read?

PaulS As stated above post#7 IS a working copy that has compiled and runs on my Arduino UNO.

The code in reply #7 contains:

void loop()
{
    unsigned long StartTime;
    unsigned long EndTime;
if(DS3231_triggered_a1()== true){   
   StartTime=millis();
   EndTime=StartTime+60000;

You should NEVER declare local variables without IMMEDIATELY assigning them a value. What will EndTime contain if the alarm has not been triggered? NO ONE knows.

EndTime should probably NOT be local to loop() (or should be static if it is to be local).

thudson: The link gives the library and some examples, but are there any explainitary articles that I can read?

Google is your friend... Here is a good introduction to state machines.

Draw the circles (states) and the lines between states (triggers) and define what triggers a state change is the first step.

PaulS I see what you mean! when I changed as you said, the StartTime and EndTime figures were consistant with what I expected. However, I am trying to used the calculated EndTime to switch off the relay in the Alarm1 loop, but I cant figure out what to use? Another if, while, do while, I've tried them all but no success. If I figure this out I can use 1 setup (UNO board, RTC, Sensor etc).

George Matthews I am having problems with FSM, so will start new post on this subject.