Delay timer in function without delaying main loop!

OK - out of my depth here. Probably wrong terminology too. It’s the programming, not the pins!

I have successfully got an LCD showing the time, increments of seconds - effectively a digital watch. I want to be able to press and external switch that will turn on the LCD backlight for 5 seconds (or something), but I want the time/watch to continue updating.

At the minute, I am using an interrupt that detects when my switch is on, the backlight lights up for 5 seconds, but this using using delay() or delaymicroseconds() but seems to delay the whole main loop - therefore, the time display stands still…

Should I be looking at a timer and/or delay?
Should I be looking at an interrupt?
Any other pointers?

–config–

attachInterrupt(myButton01, checkButton, RISING);

void checkButton()
{
        myButtonState01 = digitalRead(myButton01);
        if (myButtonState01 == 0) {
            lcd->backlight();
            delay(5000); //this is stopping the main loop!
            return;
        }
        else {
            lcd->noBacklight();
            return;
        }
}

–end of config—

Hey there, and welcome to the community!

Using interrupts for your button is alright, though you definitely do not want to do anything within an interrupt that takes any noticeable amount of time. Use it to set a so-called ‘flag’ for the main loop, and get out of there as quickly as you can. Then, in the main loop check for said flag, and handle accordingly.

As for the delays, delay() will halt the system until it’s over, rendering it useless during that time. You’re probably going to want to look into ‘non-blocking’ delays and/or software timers.
For the former, there’s an example here: Delay(), vs “soft delay” [end of discourse]
For the latter, I suggest you take a look at the docs :smile:
Let us know if that helped :)!

It is usually solved using timer (millis()) checks. Something like:

if (myButtonState == 0) {
    backlightOffTime = millis() + 5000;
}
if (millis() > backlightOffTime) {
    if (backlightOn) {
        lcd->noBacklight();
        backlightOn=false;
    }
} else {
    if (!backlightOn) {
        lcd->backlight();
        backlightOn=true;
    }
}

Thanks for the pointers. I couldn’t get it working with the recommended code, however, took the ideas, tried to understand the logic and came up with my own that (miraculously) seemed to work:

if (myButtonState01 == 0) {
backlightOffTime = millis() + 5000;
lcd->backlight();
}

if (millis() > backlightOffTime) {
        lcd->noBacklight();
}

It is the basis on which is it built :smile:
One downside is it will call lcd->noBacklight() every loop when it should not be on. So I added a boolean with light status (not declared in the code), which should prevent it.

Somewhere in global scope (outside of loop() and setup()):

bool backlightOn = false;

Modified code:

if (myButtonState01 == 0) {
    backlightOffTime = millis() + 5000;
    lcd->backlight();
    backlightOn = true;
}

if (backlightOn && (millis() > backlightOffTime)) {
    lcd->noBacklight();
    backlightOn = false;
}

What it does - when you switch on the backlight, you set a flag the backlight is on. In the second part, you check timestamp only if flag for backlight on is true. If it is, you switch off the backlight and clear the flag. Next loop will not try to switch off backlight again, because the flag is cleared. It gets set again when the backlight turns on again.

It might not be too important if lcd->noBacklight(); just turns off a pin, but if it involves sending some command to display, it is better. And it is a good practice I suppose.

Absolutely brilliant. I was struggling to find the “AND” logic code. I completely understand your explanation. Many thanks for taking the time to explain. I will update the code and put in my “snippets” as I can see me needing this logic for something that is not just turning on a backlight etc!

thanks again!

This is a minor thing, but a good habit to get into. When you compare against mills, always do it like:

if (millis() - lastTime >= TIME_INVERVAL) {
     lastTime = millis();

The reason is that millis() rolls over, or resets to 0, every 49 days. If your code runs for 49 days, the test written the other way will fail, but the test written this way continues to work properly.

The reason why has to do with how unsigned numbers are represented in C, and a topic of an entire post:

3 Likes