Setting Relay's to Clock Time

Hello,

I am very new to programming just to start.

I have an Ardiuno UNO WiFi ver2.

I am trying to set relays to turn on or off depending on a time. I learned the ardiuno has terrible time keeping abilities. So I have found this library and added it my repositories: TimeLib.h

I have a bit of code that does work when uploaded to the Aduino. It Automatically hooks up to the wifi network and then starts a time keeping function. I then spits out periodically about network info and the time.

What I am trying to do use the set time and turn a relay on or off depending on the time. Something that would turn the relay on between 8:00 - 20:00 and then turn off. What sort of if then statement would I have with the code as is.

Here is the copied together code:

/*

  This example connects to an unencrypted Wifi network.
  Then it prints the  MAC address of the Wifi module,
  the IP address obtained, and other network details.

  created 13 July 2010
  by dlf (Metodo2 srl)
  modified 31 May 2012
  by Tom Igoe
*/
#include <SPI.h>
#include <WiFiNINA.h>
#include <TimeLib.h>

#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;     // the Wifi radio's status

int in1 = 1;
int in2 = 2;
int in3 = 3;
int in4 = 4;
int in5 = 5;

bool newSecondFireFlag = false;              // rtc new second fire flag
unsigned long pitCounter = 0;                // pit counter

void setup() {
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  pinMode(in5, OUTPUT);
  digitalWrite(in1, HIGH);
  digitalWrite(in2, HIGH);
  digitalWrite(in3, HIGH);
  digitalWrite(in4, HIGH);
  digitalWrite(in5, HIGH);

  InternalRTC.attachClockInterrupt(rtc_irq); // set user IRQ function fired each new second by internal RTC
  // run only on megaAVR-0 series!
  // note : use InternalRTC.detachClockInterrupt() for detach IRQ function

  InternalRTC.attachInterrupt(pit_irq, 16);  // set user IRQ function fired by internal periodic interrupt (PIT)
  // (1Hz by defaut, max 8192Hz, only power of 2 number)
  // run only on megaAVR-0 series!
  // note : use InternalRTC.detachInterrupt() for detach IRQ function

  setTime(4, 16, 00, 30, 07, 2020);          // set the current system timestamp from arbitrary date/time
  // the next second was fire exactly 1 second after the call at this function

  Serial.begin(9600);                        // init serial
  pinMode(LED_BUILTIN, OUTPUT);              // init built-in led


  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }

  // you're connected now, so print out the data:
  Serial.print("You're connected to the network");
  printCurrentNet();
  printWifiData();

}

void pit_irq() { // executed all pediodic interrupt

  pitCounter++; // increment pit counter

}

void rtc_irq() { // executed at each RTC second interrupt

  newSecondFireFlag = true; // fire flag

}


void loop() {

  if ( newSecondFireFlag ) { // make actions if flag is fired:

    newSecondFireFlag = false; // unfire flag

    unsigned long actualTime = now(); // get actual system timestamp

    // display current system date and time:
    Serial.print(year(actualTime));
    Serial.print("-");
    Serial.print(month(actualTime));
    Serial.print("-");
    Serial.print(day(actualTime));
    Serial.print(" ");
    Serial.print(hour(actualTime));
    Serial.print(":");
    Serial.print(minute(actualTime));
    Serial.print(":");
    Serial.print(second(actualTime));

    // display PIT counter at the end of line:
    Serial.print(" -> ");
    Serial.print(pitCounter);
    Serial.println(" PIT cycles.");

    // blink led:
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);

    // Relay One
    
  }
  // check the network connection once every 10 seconds:
  delay(10000);
  printCurrentNet();

}

void printWifiData() {
  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  Serial.println(ip);

  // print your MAC address:
  byte mac[6];
  WiFi.macAddress(mac);
  Serial.print("MAC address: ");
  printMacAddress(mac);
}

void printCurrentNet() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print the MAC address of the router you're attached to:
  byte bssid[6];
  WiFi.BSSID(bssid);
  Serial.print("BSSID: ");
  printMacAddress(bssid);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.println(rssi);

  // print the encryption type:
  byte encryption = WiFi.encryptionType();
  Serial.print("Encryption Type:");
  Serial.println(encryption, HEX);
  Serial.println();
}

void printMacAddress(byte mac[]) {
  for (int i = 5; i >= 0; i--) {
    if (mac[i] < 16) {
      Serial.print("0");
    }
    Serial.print(mac[i], HEX);
    if (i > 0) {
      Serial.print(":");
    }
  }
  Serial.println();
}

Also, I am totally new to programming so if anybody has any suggestion for a good launching point for learning C++

Thank You Very Much For Your Time

You know the current hour. Test the value and take appropriate actions.

Where are you stuck ?

Ok, you found the RTC library. Did you you also purchase and attach a RTC module?
My bad, ignore me - looks like the UNO WiFi 2 has a built in RTC.

Maintain a state of your relays as a global variable and check time of the day against set times. A simple way to compare moments in the day is to count the number of seconds since midnight and compare those numbers

bool relayActivated = false;
const unsigned long startOfActivation = 3600ul*8; // 8h00:00
const unsigned long endOfActivation = 3600ul*20; // 20h00:00
...

unsigned long actualTime = now();
unsigned long secondsInDay = hour(actualTime)*3600ul+ minute(actualTime)*60ul+ second(actualTime);
if (relayActivated && secondsInDay >= endOfActivation) {
  // Time to switch off
  digitalWrite(relayPin, LOW );
  relayActivated = false;
} else
if (!relayActivated && secondsInDay >= startOfActivation) {
  // Time to switch on
  digitalWrite(relayPin, HIGH );
  relayActivated = true;
}

Typed here so fully untested

Thank you very much. I was stumped on the declaration of the correct variable and how to implement into the an if else statement.

You are awesome J-M-L.

Everything is working snazzy. I can turn the relays on/off based on time which is exactly what I wanted.

A couple things for questioning:

With this code:

const unsigned long startOfActivation = 3600ul * 8; // 8h00:00
const unsigned long endOfActivation = 3600ul * 20; // 20h00:00

Is their a way I can change it to function at night? Say turn on at 20h(10PM) and shutoff at 6h(6AM).

Also, it is not a big deal but I wanted to display information while I was troubleshooting so I popped this in there to check the Activated Relay status:

      if (relayActivated1 = true)
      {
        Serial.print("Relay 1 Engaged!");
      }
      else
      {
        Serial.print("Relay 1 Disengaged!");
      }

But no matter whether it is engaged or not is still prints 'Relay 1 Engaged!'
How do properly print the status of a boolean variable?

Thanks Again for All the Help.

Also I have attached my complete mostly working code so far.

Final-WiFi-Clock-Relays.ino (8.09 KB)

I can’t read code attached from my mobile. Post directly inline using code tags.

If you don’t see anything in the console may be you have the wrong baud rate or you did not call Serial.begin() ?

Is their a way I can change it to function at night? Say turn on at 20h(10PM) and shutoff at 6h(6AM).

thats more tricky as you are comparing dates across days’ boundaries so the formula secondsInDay = hour(actualTime)*3600ul+ minute(actualTime)*60ul+ second(actualTime);is no longer enough

If you are still within a 24h boundary, and only need to handle night activation, you could count seconds since noon (adding 12h if you are before noon that day).

If you want full flexibility then you need a more dynamic approach, may be instead of defining start and stop time, you define start time and duration and when comes the moment to start you calculate the moment when to stop using duration (adding duration in seconds to now which gives you a date in unixtime) and then you compare now to that end time to trigger the cutoff

But no matter whether it is engaged or not is still prints 'Relay 1 Engaged!'

if (relayActivated1 = true)

= is for assigning a value
== is for testing equality

If your alarms span multiple days, the easiest way to calculate times is by using Epoch times (time_t values).

I can't read code attached from my mobile. Post directly inline using code tags.

unfortunately I bypassed how many characters I can post so that is why it was an attachment. I believe I may cheat a little bit and just set my times 12 hours off from actual time to achieve the same affect.

Thank you UKHeliBob for pointing out that I needed a double == to test equality. I will bes testing next time my Arduino is done with its cycle.

Aarg thank you for pointing out Epoch times. I did some skimming and I can see that it is something I will be using in future projects.

The last thing I wanted for this project was the ability to change these relay values through a simple webserver UI.

Something along the lines of:

void showWebPage(WiFiClient client) {
  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
  // and a content-type so the client knows what's coming, then a blank line:
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();

  // the content of the HTTP response follows the header:
  client.println("<h1>Arduino Remote Control</h1>");
  client.println("<table border=1 style='text-align:center'>");
  client.println("<tr><th>Component</th><th>Status</th><th>Control</th></tr>");

  // LED's
  client.print("<tr><td>LED's</td><td>");
  if (digitalRead(in1)) {
    client.print("<font style='color:green;'>ON</font>");
  } else {
    client.print("<font style='color:red;'>OFF</font>");
  }
  client.println("</td><td><a href='/LED's/on'>ON</a> / <a href='/LED's/off'>OFF</a></td></tr>");

  // Fan1
  client.print("<tr><td>FAN1</td><td>");
  if (digitalRead(in2)) {
    client.print("<font style='color:green;'>ON</font>");
  } else {
    client.print("<font style='color:red;'>OFF</font>");
  }
  client.println("</td><td><a href='/FAN1/on'>ON</a> / <a href='/FAN1/off'>OFF</a></td></tr>");

  // FAN2
  client.print("<tr><td>FAN2</td><td>");
  if (digitalRead(in3)) {
    client.print("<font style='color:green;'>ON</font>");
  } else {
    client.print("<font style='color:red;'>OFF</font>");
  }
  client.println("</td><td><a href='/FAN2/on'>ON</a> / <a href='/FAN2/off'>OFF</a></td></tr>");

  client.println("</table>");

  // The HTTP response ends with another blank line
  client.println();
}

I have it coded into the already compiled script I have been working on with no compiling errors however selecting any of the hyperlinks in the WebUI does not change the state of the relay. So then I started thinking, if I have options for enabling and disabling a certain variable. How do I give one command precedence over another.

So lets say that the relay is activated normally through its time function, then on the webUI I disable one of the relays before it reaches the 'endOfActivation' time?

Is this possible with C++?

It’s all possible with the right code... imagine you had a button that could also turn off the relay before the end of the active duration, it would be the same thing as a web interface. The relay state would become OFF so testing against end time would no longer apply. The easiest way to code this is a finite state machine. Look it up there are many examples.

Note on testing equality you don’t need to say

if (itIsRaining == true) {
  getMyUmbrella();
  ...
}

as a Boolean is already a truth value, you just write

if (itIsRaining) {
  getMyUmbrella();
  ...
}

(In English you don’t say if it is raining is true I’ll take my umbrella you just say if it is raining I’ll take my umbrella. Same goes in programming with Boolean.)

Okay so after running the program for a full day cycle. It seems I have an issue.

Before 10 when it needs to be disengaged, everything works fine. When it hits 10 it starts the relays.

However, when is surpasses the the 220 hour mark and when it is supposed to stay off, every time the code loops, the relays turn on for a loop and then turn back off for the next loop.

#include <SPI.h>
#include <WiFiNINA.h>
#include <TimeLib.h>

#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;     // the Wifi radio's status

bool relayActivated1 = false;
bool relayActivated2 = false;
bool relayActivated3 = false;
bool relayActivated4 = false;
bool relayActivated5 = false;
bool relayActivated6 = false;
bool relayActivated7 = false;
bool relayActivated8 = false;

const unsigned long startOfActivation = 3600ul * 10; // 10h00:00
const unsigned long endOfActivation = 3600ul * 20; // 20h00:00

bool newSecondFireFlag = false;              // rtc new second fire flag
unsigned long pitCounter = 0;                // pit counter

int in1 = 1;
int in2 = 2;
int in3 = 3;
int in4 = 4;
int in5 = 5;
int in6 = 6;
int in7 = 7;
int in8 = 8;

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  pinMode(in5, OUTPUT);
  pinMode(in6, OUTPUT);
  pinMode(in7, OUTPUT);
  pinMode(in8, OUTPUT);
  digitalWrite(in1, HIGH);
  digitalWrite(in2, HIGH);
  digitalWrite(in3, HIGH);
  digitalWrite(in4, HIGH);
  digitalWrite(in5, HIGH);
  digitalWrite(in6, HIGH);
  digitalWrite(in7, HIGH);
  digitalWrite(in8, HIGH);

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }

  // you're connected now, so print out the data:
  Serial.print("You're connected to the network");
  printCurrentNet();
  printWifiData();

  InternalRTC.attachClockInterrupt(rtc_irq); // set user IRQ function fired each new second by internal RTC
  // run only on megaAVR-0 series!
  // note : use InternalRTC.detachClockInterrupt() for detach IRQ function

  InternalRTC.attachInterrupt(pit_irq, 16);  // set user IRQ function fired by internal periodic interrupt (PIT)
  // (1Hz by defaut, max 8192Hz, only power of 2 number)
  // run only on megaAVR-0 series!
  // note : use InternalRTC.detachInterrupt() for detach IRQ function

  setTime(19, 59, 50, 9, 8, 2020);          // set the current system timestamp from arbitrary date/time
  // the next second was fire exactly 1 second after the call at this function

  Serial.begin(9600);                        // init serial
  pinMode(LED_BUILTIN, OUTPUT);              // init built-in led

}

void pit_irq() { // executed all pediodic interrupt

  pitCounter++; // increment pit counter

}

void rtc_irq() { // executed at each RTC second interrupt

  newSecondFireFlag = true; // fire flag

}

void loop() {
  if ( newSecondFireFlag ) { // make actions if flag is fired:

    newSecondFireFlag = false; // unfire flag

    unsigned long actualTime = now(); // get actual system timestamp
    unsigned long secondsInDay = hour(actualTime) * 3600ul + minute(actualTime) * 60ul + second(actualTime);

    // Relay 1 Timer
    if (relayActivated1 && secondsInDay >= endOfActivation) {
      // Time to switch off
      digitalWrite(in1, HIGH );
      relayActivated1 = false;
    } else if (!relayActivated1 && secondsInDay >= startOfActivation) {
      // Time to switch on
      digitalWrite(in1, LOW );
      relayActivated1 = true;
    }
    
    // Relay 2 Timer
    if (relayActivated2 && secondsInDay >= endOfActivation) {
      // Time to switch off
      digitalWrite(in2, HIGH );
      relayActivated2 = false;
    } else if (!relayActivated2 && secondsInDay >= startOfActivation) {
      // Time to switch on
      digitalWrite(in2, LOW );
      relayActivated2 = true;
    }

You check to see if it's later than off time and if so, turn off.
Similarly, if it's later than on time to turn on.

However, at off time, both things are true. To turn on, you need to check that the time is between on and off.

this flag needs to be volatile

bool newSecondFireFlag = false;              // rtc new second fire flag

isn't that pin the TX of your Serial port ?

int in1 = 1;

might not be a good idea to use it

Okay got rid of in1 and switched with in7. I have declared the boolean volatile:

volatile bool newSecondFireFlag = false;      // rtc new second fire flag

Unfortunately it still turns off and on after every loop.

But I figured it out:

    // Relay 2 Timer
    if (relayActivated2 == true && secondsInDay >= endOfActivation) {
      // Time to switch off
      digitalWrite(in2, HIGH );
      relayActivated2 = false;
    } else if (!relayActivated2 == false && secondsInDay >= startOfActivation && secondsInDay <= endOfActivation) {
      // Time to switch on
      digitalWrite(in2, LOW );
      relayActivated2 = true;
    }

This seems to keep the relay off if past 20h.

EDIT: I was wrong, this function doesn't even turn the relay on. I'll have to look at it some more when I get back.

Oops

    } else if (!relayActivated2 == false && secondsInDay >= startOfActivation && secondsInDay <= endOfActivation) {

That not (!) should probably be removed.

That not (!) should probably be removed.

Absolutely correct. Thank you for pointing that out and helping me repair the problem.

My code is working perfectly now. You all are great people and I hope you have a wonderful day.

Thank You
-Matt