Using millis() for timing relays

Hello everyone. I'm new with arduino and with program language C, but I know electronics and automatics. I'm fast learner :slight_smile: .

For project of mine I'm trying tom make relay HIGH for 5 seconds, than LOW for 10 minutes from 09 to 17 every day while displaying current time, temperature and humidity.
I'm using DS3231 module for keeping track of the time and DHT11 module for temperature and humidity.

First I tried delay() function and soon realized that its "blocking" my other code from running. So I googled a bit and mixed few codes and this is what I got (all code in attachment):

#include <dht.h>
#include <Time.h>
#include <TimeLib.h>
#include <DS3231.h>
DS3231 rtc(SDA, SCL); // Init the DS3231 using the hardware interface
Time t; // Init a Time-data structure
#define dataPin 13 // Defines pin number to which the sensor is connected
dht DHT; // Creats a DHT object

//TWO INDEPENDANT TIMED EVENTS
const unsigned long relay_ON = 10000; //intervals in ms
const unsigned long relay_OFF = 600000; //intervals in ms

unsigned long eventTime_ON = 0;
unsigned long eventTime_OFF = 0;

    
// RELAYS

int Sprinkle1Relay = 3; //prskalica1
int Sprinkle2Relay = 4; //prskalica2
int LightsRelay = 5; //svijetlo
int HeaterRelay =6;     //grijač

/*//BUTTONS

const int FeedingButton = 7;
const int HeaterButton = 18;*/

//SENSORS

const int IRSensor = 7;
const int TempSensor = 18;

//LEDS

const int LightsLED = 8;
const int Sprinkle1LED = 9; //sigPrskalica1
const int Sprinkle2LED = 10; //sigPrskalica2
//const int FeedingLED = 11; //
const int HeaterLED = 12; //sigGrijač

void setup() {
  // put your setup code here, to run once:
  
  Serial.begin(115200);  // initialize serial communications:
  //CLOCK SETTING
  rtc.begin(); // Initialize the rtc object
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(SATURDAY);  // Set Day-of-Week to SUNDAY
  //rtc.setTime(17, 59, 0);  // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(28, 12, 2019); // Set the date to DD/MM/YYYY
  t = rtc.getTime(); // Get data from the DS3231

  //INPUTS, OUTPUTS
  //pinMode(IRSensor, INPUT);
  //pinMode(TempSensor, INPUT);   
  pinMode(Sprinkle1Relay, OUTPUT);
  pinMode(Sprinkle1LED, OUTPUT);
  pinMode(Sprinkle2Relay, OUTPUT);
  pinMode(Sprinkle2LED, OUTPUT);
  pinMode(LightsRelay, OUTPUT);
  pinMode(LightsLED, OUTPUT); 
  pinMode(HeaterRelay, OUTPUT);
  pinMode(HeaterLED, OUTPUT);  
  //pinMode(FeedingLED, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  PrintData();
  //Updateds frequently
  unsigned long currentTime = millis();
   
  if (t.hour > 9 && t.hour < 18) {
    digitalWrite(Sprinkle1Relay, HIGH);
    }else{
      if(millis() > eventTime_ON + relay_OFF){
        eventTime_ON = millis();
        SprinkleON();
        PrintData();
          if(millis() > eventTime_OFF + relay_ON){
            eventTime_OFF = millis();
            SprinkleOFF();
            PrintData();
            }
            }
         }
 }

void SprinkleON() {
  digitalWrite(Sprinkle1Relay, LOW); // Set Sprinkle Relay On
  digitalWrite(Sprinkle1LED, HIGH); // Set Sprinkle LED On
  Serial.println("Navodnjavanje");
  Serial.println("--------------------------------");
  delay(5000);
}

void SprinkleOFF() {
  digitalWrite(Sprinkle1Relay, HIGH); // Set Sprinkle Relay On
  digitalWrite(Sprinkle1LED, LOW); // Set Sprinkle LED On
  Serial.println("Navodnjavanje završeno");
  Serial.println("--------------------------------");
  delay(10000);
}

void LightsOn() {
 digitalWrite(LightsRelay, LOW); // Set Lights On
 digitalWrite(LightsLED, HIGH); // Set LED On
 Serial.println("Svijetlo Uključeno");
}

void LightsOff() {
 digitalWrite(LightsRelay, HIGH); // Set Lights Off
 digitalWrite(LightsLED, LOW); // Set LED Off
 Serial.println("Svijetlo Isključeno");
}

void PrintData() {
  int readData = DHT.read11(dataPin); // Reads the data from the sensor
  float tmp = DHT.temperature; // Gets the values of the temperature
  float hmd = DHT.humidity; // Gets the values of the humidity
  t = rtc.getTime(); // Get data from the DS3231
 
   // Printing the results on the serial monitor
  Serial.print("Temperatura = ");
  Serial.print(tmp);
  Serial.print(" *C ");
  Serial.println();
  Serial.print("Vlaznost vazduha = ");
  Serial.print(hmd);
  Serial.println(" % ");

  // Send date over serial connection
  Serial.print("Datum: ");
  Serial.print(t.date, DEC);
  Serial.print("/");
  Serial.print(t.mon, DEC);
  Serial.print("/");
  Serial.print(t.year, DEC);
  Serial.println();
  // Send Day-of-Week and time
  Serial.print("Dan u nedjelji: ");
  Serial.print(t.dow, DEC);
  Serial.println();
  Serial.print("Vrijeme: ");
  Serial.print(t.hour, DEC);
  Serial.print(":");
  Serial.print(t.min, DEC);
  Serial.print(":");
  Serial.print(t.sec, DEC);
  Serial.println();
  Serial.println("--------------------------------");
  delay(2000); // Delays 2 secods, as the DHT11 sampling rate is 0.5Hz
}

Can I make this simper or different? I wanna add some code for heater and for LCD instead of Serial.print everything.
Thanks in advice!

Nikola1219.ino (4.27 KB)

Nikola1219.ino (4.27 KB)

Please Read this before posting a programming question then revise you post and follow the instructions for posting formatted code using code tags to make it easier for all of us

i think you would be better off having sub-functions to deal with the 5 sec and 10 min states. keep track of which state your in.

using millis() to measure 5 sec is ok, but why not use the clock module to measure 10 mins. each sub-function deals with time differently

UKHeliBob:
Please Read this before posting a programming question then revise you post and follow the instructions for posting formatted code using code tags to make it easier for all of us

Sorry now I see I posted wrong format. How can I change and add ... tags? Dont see edit button sorry.

gciurpita:
i think you would be better off having sub-functions to deal with the 5 sec and 10 min states. keep track of which state your in.

using millis() to measure 5 sec is ok, but why not use the clock module to measure 10 mins. each sub-function deals with time differently

Thanks for fast replay. Well I dont know how to do that. I'll search for that and if you can give an example. Thanks!

this should be code: (Set as code in post) Read this before posting a programming question ... - Programming Questions - Arduino Forum part 6

toxic_el:

//TWO INDEPENDANT TIMED EVENTS

const unsigned long relay_ON = 10000; //intervals in ms
const unsigned long relay_OFF = 600000; //intervals in ms

unsigned long eventTime_ON = 0;
unsigned long eventTime_OFF = 0;

void loop() {
// put your main code here, to run repeatedly:
PrintData();
//Updateds frequently
unsigned long currentTime = millis();
 
if (t.hour > 9 && t.hour < 18) {
  digitalWrite(Sprinkle1Relay, HIGH);
  }else{
    if(millis() > eventTime_ON + relay_OFF){
      eventTime_ON = millis();
      SprinkleON();
      PrintData();
        if(millis() > eventTime_OFF + relay_ON){
          eventTime_OFF = millis();
          SprinkleOFF();
          PrintData();
          }
          }
       }
}

gciurpita:
i think you would be better off having sub-functions to deal with the 5 sec and 10 min states. keep track of which state your in.

using millis() to measure 5 sec is ok, but why not use the clock module to measure 10 mins. each sub-function deals with time differently

How do I do that?

Dont see edit button sorry.

Bottom/right of your post next to "Quote"

Thanks!

Thanks for adding the code tags. As you can see it makes the code easier to deal with here

I tought when I attach .ino file it will be shown like with code tags. Now I know thanks.
Anyone can help? Just give me an example how should I make this code less confusing...

For project of mine I'm trying tom make relay HIGH for 5 seconds, than LOW for 10 minutes from 09 to 17 every day while displaying current time, temperature and humidity.

The system can be in one of several states which makes it perfect for implementing as a "state machine". That sounds scary but all it means in practice is that by knowing what the current state is the program can determine which section of code to execute.

First decide on your states and give them meaningful names. Maybe

enum states
{
  NO_ACTION,
  RELAY_ON,
  RELAY_OFF  
};

Using an enum allows you to give the states names which you can use later

Then comes your code to declare other variables, #include libraries, set pinModes() etc. In setup() use the time from the RTC to determine the state to start in. If it is between 17:00 and 09:00 then set a variable named currentState to NO_ACTION and turn the relay off, otherwise set it to RELAY_ON, turn the relay on and save the value of millis() as startTime.

Now it is time for the loop() function. You could use if/else to test the current state and execute code but I find that switch/case makes for code that is easier to read and maintain. This is the general form of switch/case

  switch (currentState)
  {
    case NO_ACTION:
      //no code to execute
      break;
    case RELAY_ON:
      //code here to test whether the relay should still be on
      break;
    case RELAY_OFF:
      //code here to test whether the relay should still be off
      break;
  }

Only the code block for the current state will be executed so what goes in those code blocks ? Well, before entering the RELAY_ON state the value of millis() was saved in currentTime so you can use

if (currentTime - startTime >=5000 )
  {
    //do something
  }

What to do ?
Set currentState to RELAY_OFF, turn off the relay and save millis() as currentTime and BINGO the program will execute the code for the RELAY_OFF state which will be remarkably similar to that for the RELAY_ON state

Now, what about the 0900 to 1700 timing ?
Nothing in the switch/case blocks loop() so every time through loop() you can get the time from the RTC. When it becomes time for nothing is to happen turn off the relay and set currentState to NO_ACTION, then when it becomes time to act set currentState to RELAY_ON and save millis() to startTime again and off you go again.

Add whatever other code you need to loop() but leave it running freely

Thank you so much for so detailed answer. I will do that and repost final result. This helps me to better understand the way the language works. I need to learn C
All the best wishes in 2020 to you all!

UKHeliBob:
The system can be in one of several states which makes it perfect for implementing as a "state machine". That sounds scary but all it means in practice is that by knowing what the current state is the program can determine which section of code to execute.

First decide on your states and give them meaningful names. Maybe

enum states

{
  NO_ACTION,
  RELAY_ON,
  RELAY_OFF 
};



Using an enum allows you to give the states names which you can use later

Then comes your code to declare other variables, #include libraries, set pinModes() etc. In setup() use the time from the RTC to determine the state to start in. If it is between 17:00 and 09:00 then set a variable named currentState to NO_ACTION and turn the relay off, otherwise set it to RELAY_ON, turn the relay on and save the value of millis() as startTime.

Now it is time for the loop() function. You could use if/else to test the current state and execute code but I find that switch/case makes for code that is easier to read and maintain. This is the general form of switch/case


switch (currentState)
  {
    case NO_ACTION:
      //no code to execute
      break;
    case RELAY_ON:
      //code here to test whether the relay should still be on
      break;
    case RELAY_OFF:
      //code here to test whether the relay should still be off
      break;
  }



Only the code block for the current state will be executed so what goes in those code blocks ? Well, before entering the RELAY_ON state the value of millis() was saved in currentTime so you can use


if (currentTime - startTime >=5000 )
  {
    //do something
  }



What to do ?
Set currentState to RELAY_OFF, turn off the relay and save millis() as currentTime and BINGO the program will execute the code for the RELAY_OFF state which will be remarkably similar to that for the RELAY_ON state

Now, what about the 0900 to 1700 timing ? 
Nothing in the switch/case blocks loop() so every time through loop() you can get the time from the RTC. When it becomes time for nothing is to happen turn off the relay and set currentState to NO_ACTION, then when it becomes time to act set currentState to RELAY_ON and save millis() to startTime again and off you go again.

Add whatever other code you need to loop() but leave it running freely

Sorry for still asking im doing something wrong...

enum states
{
  NO_ACTION,
  RELAY_ON,
  RELAY_OFF  
};

#include <dht.h>
#include <Time.h>
#include <TimeLib.h>
#include <DS3231.h>
DS3231 rtc(SDA, SCL); // Init the DS3231 using the hardware interface
Time t; // Init a Time-data structure
#define dataPin 13 // Defines pin number to which the sensor is connected
dht DHT; // Creats a DHT object

 int currentState;

void setup() {   
  // put your setup code here, to run once:
  Serial.begin(115200);  // initialize serial communications:
  //CLOCK SETTING
  rtc.begin(); // Initialize the rtc object
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(SATURDAY);  // Set Day-of-Week to SUNDAY
  //rtc.setTime(17, 59, 0);  // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(28, 12, 2019); // Set the date to DD/MM/YYYY
  t = rtc.getTime(); // Get data from the DS3231
  
  if (t.hour < 9 && t.hour > 17) {
    currentState = NO_ACTION;
    }else{ RELAY_ON;
    unsigned long startTime = millis();
    }


}

void loop() {
  // put your main code here, to run repeatedly:
   
  switch (currentState)
  {
    case NO_ACTION:
      //no code to execute
      break;
    case RELAY_ON:
      //code here to test whether the relay should still be on
      if (currentTime - startTime >= 5000 )
  {
    //do something
    currentState = RELAY_OFF:
    unsigned long currentTime = millis();
  }
      break;
    case RELAY_OFF:
      //code here to test whether the relay should still be off
      break;
  }

}

, and get this error:"exit status 1
'currentTime' was not declared in this scope"

where do I declare currentTime? Thanks again

where do I declare currentTime?

You can declare it at the start of loop() and give it the current value of millis() at the same time

}else{ RELAY_ON;

should be
}else{ currentState = RELAY_ON; and don;t forget to actually turn the relay on at the same place

void setup() {   
  // put your setup code here, to run once:
  Serial.begin(115200);  // initialize serial communications:
  //CLOCK SETTING
  rtc.begin(); // Initialize the rtc object
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(SATURDAY);  // Set Day-of-Week to SUNDAY
  //rtc.setTime(17, 59, 0);  // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(28, 12, 2019); // Set the date to DD/MM/YYYY
  t = rtc.getTime(); // Get data from the DS3231
  
  if (t.hour < 9 && t.hour > 17) {
    currentState = NO_ACTION;
    }else{ currentState = RELAY_ON;
    unsigned long startTime = millis();
    }


}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentTime = millis();
   
  switch (currentState)
  {
    case NO_ACTION:
      //no code to execute
      break;
    case RELAY_ON:
      //code here to test whether the relay should still be on
      if (currentTime - startTime >= 5000 )
  {
    //do something
    currentState = RELAY_OFF:
    unsigned long currentTime = millis();
  }
      break;
    case RELAY_OFF:
      //code here to test whether the relay should still be off
      break;
  }

}

Now it gives me "exit status 1
'startTime' was not declared in this scope" , I declared it in void setup();
Should I declare it in loop too?
Please excuse me for bothering you.

My mistake. I had forgotten what I had said previously

Let's keep it simple. Declare it as a global and initialise it in setup()

Im confused startTime, or currentTime?

Sorry, I am in the middle of something and got confused between what I am doing and replying to you.

Make both startTime and currentTime global. Set currentTime to millis() at the start of loop() and startTime to currentTime whenever you turn the relay on or off.

Ok, sorry about that and thanks for helping me. I will try to complete the code and will post what I get tommorow. Thank you, one more time!