Switching LEDs on and off according to date and time using if else statements

Hello Friends,
I am trying to make a simple project that acts as a medicine reminder. This isn't my first project, however i am struggling to learn where my error is in my code. I have designed a circuit including red lights, green lights, DHT22 temp/humidity, along with a DS1302 RTC and LCD 20x4 display.

Both the time and date print correctly on the lcd screen and although the temp/ humidity print correctly the do flash every second or so; and by flash i mean that the characters disappear for a millisecond and then reappear as if the code is refreshing just those two lines. I would prefer if it wouldn't do that.

The first two lines of time and date do not flash like the temp and humidity do. However that is only a minor issue compared to the fact that my project doesn't technically work with the existing code I have.

Everything prints on the LCD fine (minus the flashing exception) and I have tested that the LEDs both red and green will in fact light up and switch on and off with different code. So I am trying to write the code so that the red LEDs would be on and the green off EXCEPT on Monday between 3 pm to 9pm and Friday 3 am to 9am when the green LEDs would come on and the red would be off.

When I run my existing code the green LEDs remain lit up and it simply never changes... I have done a fair amount of research but remain perplexed. I would appreciate any advice to help complete this project -

const static char* WeekDays[7] = { "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
const static char* Months[12] = {"January","February","March","April","May","June","July","August","September","October","November","December"};

void setup()
{
 Serial.begin(9600);
  pinMode(9, OUTPUT);
  pinMode(8, OUTPUT);
  dht.begin();
  rtc.init();
    if (rtc.isHalted())
    {
        Serial.println("RTC is halted. Setting time...");

        Ds1302::DateTime dt = {
            .year = 24,
            .month = Ds1302::MONTH_JUL,
            .day = 6,
            .hour = 8,
            .minute = 30,
            .second = 00,
            .dow = Ds1302::DOW_TUE
        };

        rtc.setDateTime(&dt);
    }
}

void loop()
{
    // get the current time
    Ds1302::DateTime now;
    rtc.getDateTime(&now);
    static uint8_t last_second = 0;
    if (last_second != now.second)
    {
        last_second = now.second;
        
  float h = dht.readHumidity();              // Read temperature as Celsius (the default)
  float t = dht.readTemperature();           // Read temperature as Fahrenheit (isFahrenheit = true)
  float f = dht.readTemperature(true);
  float hif = dht.computeHeatIndex(f, h);    // Compute heat index in Celsius (isFahreheit = false)
  float hic = dht.computeHeatIndex(t, h, false);

//  digitalWrite(9, HIGH); // Turn on red lights **it is not time to take your medicine**//
  lcd.init();                      
  lcd.backlight();
  lcd.setCursor(4,0);
  lcd.print(Months[now.month - 1]); //01-12
  lcd.print(" ");
    if (now.day < 10) lcd.print('0');
  lcd.print(now.day);
  lcd.print(" ");
  lcd.print("20");
  lcd.print(now.year);
  lcd.print(" ");
  //lcd.print(".");
  //lcd.print(now.second);
  lcd.setCursor(1,1);
    if (now.hour < 10) lcd.print('0');
  lcd.print(now.hour);
  //lcd.print(":"); 
    if (now.minute < 10) lcd.print('0');
  lcd.print(now.minute);
  lcd.print(" ");
    if(WeekDays[now.dow - 1 == 1 && now.hour >= 300 && now.hour < 900])
 {
      digitalWrite(8, HIGH); // Turn on Green lights **it is time to take your medicine**//
      digitalWrite(9, LOW);  // RED //
    }else{
     digitalWrite(8, LOW);
     digitalWrite(9, HIGH);
 }
//   if(WeekDays[now.dow - 1 == 6 && now.hour >= 15 && now.hour < 21])
//  {
//      digitalWrite(8, HIGH);
//      digitalWrite(9, LOW);
//   }else{
//     digitalWrite(8, LOW);
//     digitalWrite(9, HIGH);
// }
  lcd.print(WeekDays[now.dow - 1]);
  
  lcd.setCursor(0,2);
  lcd.print("Temperature:  ");
  lcd.print(f);
  lcd.setCursor(0,3);
  lcd.print("Humidity:     ");
  lcd.print(h);

type or paste code here

Some suggestions to make helping you easier

  • Split your initial slab of text into paragraphs with blank lines between then to make it easier to read
  • Give details of how the components of your project are connected and powered, preferably by posting a schematic

why? what have humidity common with a medicine reminder?

better you post full code. posted piece not compiles.

this probably belongs in the setup()

flashing is often due to refreshing content with clearing the screen or erasing a line a rewriting the same thing. You should update only what has changed, without clearing the screen.

So I am trying to write the code so that the red LEDs would be on and the green off EXCEPT on Monday between 3 pm to 9pm and Friday 3 am to 9am when the green LEDs would come on and the red would be...

check which day of the week you are and if it's monday or Friday then to see if you are within the time slot calculate the number of minutes since midnight (60 x hours + minutes) and compare that with the same math for 3pm or 9pm or 9am

You are correct, I have moved those two lines to the setup() and that resolved the flashing issue. However I do not understand the midnight calculation, can you provide an example for today, Saturday at 0900 am?
Im not sure I understand the day of the week numbers either, is sunday day 1 or is monday?

I don't know which RTC library you are using but it seems you get the current time with

  // get the current time
  Ds1302::DateTime now;
  rtc.getDateTime(&now);
  • the day of the week is now.dow (you'll have to confirm from the documentation if it 1 to 7 or 0 to 6 and which day is the first day, sometimes it's sunday, sometimes it's monday)
  • the hours are in now.hour
  • the minutes are in now.minute

Let's say the now.dow starts at 1 and it's monday then to check if you are Monday between 3 pm to 9pm, you could do something like (typed here so mind typos)

  const int minutesFor3pm = 15*60+0; // minutes since midnight for 15h00
  const int minutesFor9pm = 21*60+0; // minutes since midnight for 21h00

  // get the current time
  Ds1302::DateTime now;
  rtc.getDateTime(&now);

  // is it monday ?
  if (now.dow == 1) { // 1 is monday
    int minutesSinceMidnight = 60* now.hour + now.minute; // number of minutes elapsed since midnight that day
    if ((minutesSinceMidnight >= minutesFor3pm) && (minutesSinceMidnight <= minutesFor9pm))  { // it's Monday between 3 pm to 9pm
      ...
    }
  } 

I have been able to get the attached code to run, although not successfully. The LCD displays the correct information, but neither color, red nor green, will illuminate when I run this code. I understand the conversion into minutes, I still do not understand why it wont operate? could you explain it to me like I'm 5. LOL...

#include <Ds1302.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include "Arduino.h"

Ds1302 rtc(2,5,4);
LiquidCrystal_I2C lcd(0x27,20,4);

const static char* WeekDays[7] = { "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
//const static char* WeekDays[7] = { "Sunday", "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
const static char* Months[12] = {"January","February","March","April","May","June","July","August","September","October","November","December"};

 const int minutesFor3pm = 15*60+0; // minutes since midnight for 15h00
 const int minutesFor9pm = 21*60+0; // minutes since midnight for 21h00
 
void setup() {
  // put your setup code here, to run once:
  pinMode(9, OUTPUT);
  pinMode(8, OUTPUT);
  lcd.init();                      
  lcd.backlight();
  rtc.init();

}

void loop() {
  Ds1302::DateTime now;
  rtc.getDateTime(&now);
  lcd.setCursor(4,0);
  lcd.print(Months[now.month - 1]); //01-12
  lcd.print(" ");
    if (now.day < 10) lcd.print('0');
  lcd.print(now.day);
  lcd.print(" ");
  lcd.print("20");
  lcd.print(now.year);
  lcd.print(" ");
  //lcd.print(".");
  //lcd.print(now.second);
  lcd.setCursor(1,1);
    if (now.hour < 10) lcd.print('0');
  lcd.print(now.hour);
  //lcd.print(":"); 
    if (now.minute < 10) lcd.print('0');
  lcd.print(now.minute);
  lcd.print(" ");
  lcd.print(WeekDays[now.dow]);
  int minutesSinceMidnight = 60* now.hour + now.minute; // number of minutes elapsed since midnight that day
   if (now.dow == 1) { if ((minutesSinceMidnight >= minutesFor3pm) && (minutesSinceMidnight <= minutesFor9pm))// it's Monday between 3 pm to 9pm
{     digitalWrite(8, HIGH);
      digitalWrite(9, LOW);
   }else{
      digitalWrite(9, HIGH);
      digitalWrite(8, LOW);}
  lcd.setCursor(0,4);
  
   }
}

Verify you have wired the LEDs correctly, including a current-limiting resistor of about 400ohms.

Arduino     Resistor                                 Arduino
    pin ---  (400) --- ANODE --- LED --- CATHODE --- ground 

If you wired the LEDs without a resistor prior to this, they may have burned out. Replace the LEDs and verify wiring.

as stated originally, I have tested that the LEDs do work. Yes, they are correctly wired and not burnt out.
the issue is somewhere in the language of the code. I am still learning, but thanks for covering some of the basics

Thank you. Sorry for redundancy.

This is the droid line not allowing LEDs to light...

((minutesSinceMidnight >= minutesFor3pm) && (minutesSinceMidnight <= minutesFor9pm))

Print out "minutesSinceMidnight", "minutsFor3pm" and "minutesFor9pm" then manually do the math. Verify their "type" can hold the values printed. Verify their logic puts a "true" on both sides... as "minutesSinceMidnight" might be a negative.

[edit] this: ``` minutesSinceMidnight = 60 * now.hour + now.minute; ``` becomes : ``` zero + now.minute // in the midnight hour of "0" ```

How so?

  int minutesSinceMidnight = 60* now.hour + now.minute;

The hour at most is 23, the minute at most 59; 60 * 23 + 59 is 1439, fitting easily into the int variable that is minutesSinceMidnight.

a7

#include <Ds1302.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "Arduino.h"

Ds1302 rtc(2, 5, 4);
LiquidCrystal_I2C lcd(0x27, 20, 4);

const static char* WeekDays[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
//const static char* WeekDays[7] = { "Sunday", "Monday", "Tuesday","Wednesday", "Thursday", "Friday", "Saturday"};
const static char* Months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};

const int minutesFor3pm = 15 * 60 + 0; // minutes since midnight for 15h00
const int minutesFor9pm = 21 * 60 + 0; // minutes since midnight for 21h00

void setup() {
  pinMode(9, OUTPUT);
  pinMode(8, OUTPUT);
  lcd.init();
  lcd.backlight();
  rtc.init();
}

void loop() {
  Ds1302::DateTime now;
  rtc.getDateTime(&now);
  char Text[21];
  sprintf(Text, "%s %02u 20%02u", Months[now.month - 1], now.day, now.year);
  lcd.setCursor(4, 0);
  lcd.print(Text);

  sprintf(Text, "%02u:%02u %s", now.hour, now.minute, WeekDays[now.dow]);
  lcd.setCursor(1, 1);
  lcd.print(Text);

  int minutesSinceMidnight = 60 * now.hour + now.minute; // number of minutes elapsed since midnight that day
 // if (now.dow == 1) {
    if (minutesSinceMidnight %2 )// it's Monday between 3 pm to 9pm
    { 
      digitalWrite(8, HIGH);
      digitalWrite(9, LOW);
    } else {
      digitalWrite(9, HIGH);
      digitalWrite(8, LOW);
    }
 // }
}

If midnight is the "tomorrow" side of noon, rather than the "yesterday" side of noon.

can you explain this further?
I have ran the almost exact same code, the lights do light up , but will not switch - only stay the same once the code us uploaded.

did you waited until monday?
eh, i make every minute change

Hour will be (c-string) printed with two, zero-padded, prefixed digits (00, 01, 02...11), then a colon, then the minute will be printed in the same manner, then the day string (Monday, Tuesday... Sunday) will be printed.

I used int as I knew we would be fine even on 2 bytes.

Did you force the date to see this happen ?
Did you check what Dow is really for Monday ?

shouldn't this be

if (now.hour >= 3 && now.hour < 9)

What? Minutes since midnight is always non-negative, and is at most 1439.

If you are worrying about a time span that crosses midnight, about which @1notsure_2505 has said nothing, the concept works just the same, with all non-negative numbers that fit in an int type variable. You just have to fix the logical expression, viz:

  const int minutesFor10pm = 22 * 60 + 0; // minutes since midnight for 22h00
  const int minutesFor2am = 2* 60 + 0; // minutes since midnight for 2h00

  // get the current time
  Ds1302::DateTime now;
  rtc.getDateTime(&now);


  int minutesSinceMidnight = 60 * now.hour + now.minute;
  if ((minutesSinceMidnight >= minutesFor10pm) || (minutesSinceMidnight <= minutesFor2am))  { // it's between 10pm and 2am

  }

With the wrinkle of doing this only on a time on Monday until a time on Tuesday left as an exercise for the reader.

a7