Millis Code still freezing the LCD display

When operating the relay and buttons the LCD timer and Serial Print cease working.

I previously used delays and caused the same issue. I thought the solution was in millis and still believe it is. However, clearly my application is not work the way I would like.
I am using Wokwi to help design which has been mostly helpful.

Could you please review the code and provide advice on how to resolve the clock ceasing?

https://wokwi.com/projects/350939434314629716


#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);


unsigned long previousMillis;
const unsigned long interval = 1000;   // 1 second

int hours, minutes, seconds;

// Relay Controls
int rt = 1000;
const int RELAY_PIN = 3;

// ON & OFF Time 1

int OnRelayHour = 05;
int OnRelayMin = 00;

int OffRelayHour = 05;
int OffRelayMin = 01;

int x = OffRelayMin - OnRelayMin;
int y = 0;

// Button Int
const int pushbtn1 = 9; // LCD Backlight
const int pushbtn2 = 5; // Manual Relay activation

int btnstate1; // LCD Backlight
int btnstate2; // Manual Relay activation


void setup()
{
  Wire.begin();
  pinMode(pushbtn1, INPUT); // Green LCD Back light
  pinMode(pushbtn2, INPUT); // Blue Manual Relay activation
  pinMode(3, OUTPUT); // Manual Relay activation

  Serial.begin( 9600);
  Serial.setTimeout( 100);    // 100ms timeout for serial input
  Serial.println( "Set the new time in hh:mm:ss format");

// LCD Code 26 Dec 2022 
  lcd.init();
  lcd.backlight();
  lcd.setCursor(1, 0);

}

void loop()
{

    // LCD Backlight Button

    if(digitalRead(9)==HIGH)
    {
      lcd.backlight();
    }
    if(digitalRead(9)==LOW)
    {
      lcd.noBacklight();
    }

    // Manual Relay Activation
    digitalWrite(3,digitalRead(5));
   

    // Relay on and off time
      if(hours == OnRelayHour && minutes == OnRelayMin)
        for (y= 0; y <x*60/7; y++)
          { 
            digitalWrite(RELAY_PIN, HIGH);
            delay(rt);
            digitalWrite(RELAY_PIN, LOW);
            delay(rt);
          }
          {
              delay(rt);
              digitalWrite(RELAY_PIN, LOW);
          } 


  unsigned long currentMillis = millis();
  
  if( Serial.available() > 0)
  {
    delay( 100);               // wait 100ms to get whole line
    int h, a, m, b, s;
    h = Serial.parseInt();    // get integer with timeout
    a = Serial.read();        // get character, no timeout
    m = Serial.parseInt();    // get integer with timeout
    b = Serial.read();        // get character, no timeout
    s = Serial.parseInt();    // get integer with timeout

    if( a == ':' && b == ':') // check for right format
    {
      hours   = constrain( h, 0, 23);
      minutes = constrain( m, 0, 59);
      seconds = constrain( s, 0, 59);
    }
    else
    {
      Serial.println( "New time not accepted");
    }
    // Clear remaining characters from the serial input buffer.
    while( Serial.available() > 0)
    {
      Serial.read();
    }
  }

  if( currentMillis - previousMillis >= interval)
  {
    // When previousMillis would be set to currentMillis,
    // then a delay in the code would delay the clock.
    // When previousMillis is incremented with 1 second,
    // then it stays synchronized.
    previousMillis += interval;   // increment with 1 second

    seconds++;
    if( seconds >= 60)
    {
      seconds = 0;
      minutes++;
      if( minutes >= 60)
      {
        minutes = 0;
        hours++;
        if( hours >= 24)
        {
          hours = 0;
        }
      }
    }

    // Update the time to the serial monitor.
    // The format is  hh:mm:ss.
    // For example    09:30:55
    if( hours < 10)
      Serial.print( "0");
    Serial.print( hours);
    Serial.print( ":");
    if( minutes < 10)
      Serial.print( "0");
    Serial.print( minutes);
    Serial.print( ":");
    if( seconds < 10)
      Serial.print( "0");
    Serial.print( seconds);
    Serial.println();

    // LCD code 
    lcd.setCursor(1,1);
    if( hours < 10)
    lcd.print("0");
    lcd.print(hours);
    lcd.print(":");
    if( minutes < 10)
      lcd.print( "0");
    lcd.print( minutes);
    lcd.print( ":");
    if( seconds < 10)
      lcd.print( "0");
    lcd.print( seconds);
    lcd.println();


     // Attempt of On Off Code

  



  } 
}

  {
    delay(rt);
    digitalWrite(RELAY_PIN, LOW);
  }

Could this unconditional 1 second delay() in loop() be a problem, to say nothing of the multiple 1 second delays in the for loop just before it and the multiple parseInt() calls each with a 100 milisecond timeout ?

Yep. Probably is the issue here. However I want to isolate the 'clock function' from the delay.
Do you have a recommendation on further learning regarding this matter?

I learnt from this example but it wasn't enough for my particular application.

Thoughts?

See Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

https://docs.arduino.cc/built-in-examples/digital/BlinkWithoutDelay

In the Wokwi "Library Manager" tab, you have three libraries for the display. Please use just one. A good library is the hd44780 library (https://github.com/duinoWitchery/hd44780). You don't have to upload the "hd44780" library, it is part of the known libraries, search for the name "hd44780". You also don't have to add the "Wire" library "Library Manager" tab, because the "Wire" library is already included by the Arduino software.

I suggest to print a message to the Serial Monitor in setup(), so you know that the sketch has started.

You don't have to use the 'enum' to make a variable, when you already have created a type of that kind.

Your code:

enum myOwnEnumType
{
  OFF, ON
};
enum myOwnEnumType myEnum = OFF;

More straightforward:

enum myOwnEnumType
{
  OFF, ON
};
myOwnEnumType myEnum = OFF;

It is possible to create a variable without the type:

enum 
{
  OFF, ON
} myEnum = OFF;

This is an extensive example - thank you.
However after reading - I find it difficult to apply to my application setting.
Do you have anything that will help with this?

I uploaded your code into Wokwi as a practical example also.

https://wokwi.com/projects/352112150654427137

Do you have an example of a more extensive project whereby there are two 'requests'/functions occurring at the same time using the millis() function?

Hi Koepel,

Thanks for your input.
I have deleted the other libraries as per recommendation however have this notification.

I have attempted to look through to GitHub library for the new referencing of code.

Thoughts?

Do you mean 2 functions triggered at different times by mills() timing ?

I have managed to get it working again but had to keep library

LiquidCrystal I2C

in the library manager.

I have also added the start up display as per your recommendation.

However the problem remains when activating the the relay there is a delay and then the shutoff timer does not register within the expect timeframe. Thoughts?

Lastly, I have just realised there is a different code between the Wokwi and the code I have posted. Apologies - I will also fix this.

I guess so.

I am wanting the Arduino to count as a clock (1) and then pulse the relay on and off (2).

At the moment it pulses the relay but does not continue to function as a counting clock.

Thoughts?

(V4 LCD Relay 26 Dec 2022 copy - Wokwi Arduino and ESP32 Simulator)


#include <Wire.h>
#include <hd44780.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);


unsigned long previousMillis;
const unsigned long interval = 1000;   // 1 second

int hours, minutes, seconds;

// Relay Controls
int rt = 1000;
const int RELAY_PIN = 3;

// ON & OFF Time 1

int OnRelayHour = 05;
int OnRelayMin = 00;

int OffRelayHour = 05;
int OffRelayMin = 01;

int x = OffRelayMin - OnRelayMin;
int y = 0;

// Button Int
const int pushbtn1 = 9; // LCD Backlight
const int pushbtn2 = 5; // Manual Relay activation

int btnstate1; // LCD Backlight
int btnstate2; // Manual Relay activation


void setup()
{
   

  Wire.begin();
  pinMode(pushbtn1, INPUT); // Green LCD Back light
  pinMode(pushbtn2, INPUT); // Blue Manual Relay activation
  pinMode(3, OUTPUT); // Manual Relay activation

  Serial.begin( 9600);
  Serial.setTimeout( 100);    // 100ms timeout for serial input
  Serial.println( "Starting...");

// LCD Code 26 Dec 2022 
  lcd.init();
  lcd.backlight();
  lcd.setCursor(1, 0);
  lcd.print( "Starting...");
  delay(1000);
  lcd.clear();

}

void loop()
{

    // LCD Backlight Button

    if(digitalRead(9)==HIGH)
    {
      lcd.backlight();
    }
    if(digitalRead(9)==LOW)
    {
      lcd.noBacklight();
    }

    // Manual Relay Activation
    digitalWrite(3,digitalRead(5));
   

    // Relay on and off time
      if(hours == OnRelayHour && minutes == OnRelayMin)
        for (y= 0; y <x*60/7; y++)
          { 
            digitalWrite(RELAY_PIN, HIGH);
            delay(rt);
            digitalWrite(RELAY_PIN, LOW);
            delay(rt);
          }
          {
              delay(rt);
              digitalWrite(RELAY_PIN, LOW);
          } 


  unsigned long currentMillis = millis();
  
  if( Serial.available() > 0)
  {
    delay( 100);               // wait 100ms to get whole line
    int h, a, m, b, s;
    h = Serial.parseInt();    // get integer with timeout
    a = Serial.read();        // get character, no timeout
    m = Serial.parseInt();    // get integer with timeout
    b = Serial.read();        // get character, no timeout
    s = Serial.parseInt();    // get integer with timeout

    if( a == ':' && b == ':') // check for right format
    {
      hours   = constrain( h, 0, 23);
      minutes = constrain( m, 0, 59);
      seconds = constrain( s, 0, 59);
    }
    else
    {
      Serial.println( "New time not accepted");
    }
    // Clear remaining characters from the serial input buffer.
    while( Serial.available() > 0)
    {
      Serial.read();
    }
  }

  if( currentMillis - previousMillis >= interval)
  {
    // When previousMillis would be set to currentMillis,
    // then a delay in the code would delay the clock.
    // When previousMillis is incremented with 1 second,
    // then it stays synchronized.
    previousMillis += interval;   // increment with 1 second

    seconds++;
    if( seconds >= 60)
    {
      seconds = 0;
      minutes++;
      if( minutes >= 60)
      {
        minutes = 0;
        hours++;
        if( hours >= 24)
        {
          hours = 0;
        }
      }
    }

    // Update the time to the serial monitor.
    // The format is  hh:mm:ss.
    // For example    09:30:55
    if( hours < 10)
      Serial.print( "0");
    Serial.print( hours);
    Serial.print( ":");
    if( minutes < 10)
      Serial.print( "0");
    Serial.print( minutes);
    Serial.print( ":");
    if( seconds < 10)
      Serial.print( "0");
    Serial.print( seconds);
    Serial.println();

    // LCD code 
    lcd.setCursor(1,1);
    if( hours < 10)
    lcd.print("0");
    lcd.print(hours);
    lcd.print(":");
    if( minutes < 10)
      lcd.print( "0");
    lcd.print( minutes);
    lcd.print( ":");
    if( seconds < 10)
      lcd.print( "0");
    lcd.print( seconds);
    lcd.println();


     // Attempt of On Off Code

  



  } 
}

I was able to press the button 1095 times in 5 seconds.

A better test of whatever that tests is to turn off the bounce attribute of the simulated switch.

Click on the switch, a pop up has a checkbox for bounce/no bounce.

a7

unsigned long clockStartTime;
unsigned long relayStartTime;
unsigned long clockPeriod = 5000;  //10 second period
unsigned long relayPeriod = 500;    //shorter period
unsigned long currentTime;

byte relayCounter;

boolean relayRunning = false;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  currentTime = millis();
  if (currentTime - clockStartTime >= clockPeriod)
  {
    if (!relayRunning)
    {
      Serial.println("starting relay");
      relayRunning = true;
      relayStartTime = currentTime;
      relayCounter = 0;
    }
    clockStartTime = currentTime;
  }
  if (relayRunning)
  {
    if (currentTime - relayStartTime >= relayPeriod)
    {
      Serial.println("relay click");
      relayStartTime = currentTime;
      relayCounter++;
      if (relayCounter == 5)
      {
        relayRunning = false;
        Serial.println("stopping relay");
      }
    }
  }
}

I don't want to check your new sketch when there are two competing libraries for the display.

At this moment, you seem to use the "LiquidCrystal_I2C", so please remove the other.

The "hd44780" is better than the "LiquidCrystal_I2C" and the "LiquidCrystal_I2C" is not even maintained anymore.

Hi UKHeliBob,

This is helpful and I have been able to generate the outcome of this in Wokwi.
However, this does not resolve my issue about having this running and the 'clock' continuing to function at the same time so the Arduino can recognise when to power on and off the relay.

Do you have any suggestions how you might intergrate this into your code?

I also notice your code is much more refined.

Thanks again.

Hi Koepel,

Thats fine.
I am just not sure how to incorporate your library into the code.

Any advice would be helpful on how to resolve the previous error that occurred when I deleted "LiquidCrystal_I2C".

Thanks.

Try this version

The "clock" runs all the time and changes the state of the LED every second. Every 10 seconds the relays clicks 5 times, which takes longer than one second, but note that the "clock" keeps running while this happens

unsigned long clockTickTime;
unsigned long relayStartTime;
unsigned long clockPeriod = 1000;  //1 second clock period
unsigned long relayPeriod = 500;    //relay click period
unsigned long currentTime;
unsigned int secondsCounter;
byte relayCounter;

boolean relayRunning = false;

void setup()
{
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  currentTime = millis();
  if (currentTime - clockTickTime >= clockPeriod)
  {
    secondsCounter++;
    clockTickTime = currentTime;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
  if ((secondsCounter % 10) == 0) //start relay every 10 seconds
  {
    if (!relayRunning)
    {
      Serial.println("starting relay");
      relayRunning = true;
      relayStartTime = currentTime;
      relayCounter = 0;
    }
  }
  if (relayRunning)
  {
    if (currentTime - relayStartTime >= relayPeriod)
    {
      Serial.println("relay click");
      relayStartTime = currentTime;
      relayCounter++;
      if (relayCounter == 5)
      {
        relayRunning = false;
        Serial.println("stopping relay");
      }
    }
  }
}

Here is a simple example with the LiquidCrystal_I2C library: Hundred percent LiquidCrystal_I2C - Wokwi Arduino and ESP32 Simulator
Here is a simple example with the hd44780 library: Hundred percent hd44780 - Wokwi Arduino and ESP32 Simulator
Check also the "Library Manager" tab. I like to keep it clean and do not include libraries that are not used.

The hd44780 is not my library, it is bperrybap his library.

The best way to start with that library is to read about the functions: https://github.com/duinoWitchery/hd44780#api-summary
The examples are here: https://github.com/duinoWitchery/hd44780/tree/master/examples/ioClass/hd44780_I2Cexp

Thanks Koepel,

I didn't know about the "functions" section. This was really helpful.