Help with implementing checking an input during delays

Hello, I have searched the internet and am unable to find a solution to my problem. I am building a project and my background is more hardware than software,

I am using an OLED display, and my project is a lamp that has three modes, on, off, or a count down timer.

My loop looks something like this;

void loop()
{
if (toggle == 1) {

if (state == 0) {
lamp_on();
}

else if (state == 1){
lamp_timer();
}
else {
lamp_off();
}
}
toggle = 0;
}

The toggle variable is set by an interrupt on an input button, hence when the button is pressed it knows to jump to a sub routine. The correct subroutine is the next routine in sequence as set by the state variable at the end of the last sub routine.

My problem is, at the start of each subroutine a message is displayed on the OLED asking if the lamp wants to be turned on? /off? /timer? this should pause for a few seconds and then confirm this is the correct choice. However if the button is again pressed i need to jump immediately to the next sub routine, this will allow efficient control of the lamp with only one button. I am unable to advance as the interrupt does not return to the loop, and i cannot seem to use the millis() command outside of loop to check if the button has been pressed. Is there a way to pause the program while the message is displayed on the OLED but still check for a button press and if so cancel the current routine and jump to the next?

example of one of the routines:

void lamp_off(){

display.clearDisplay();
display.setCursor(0, 25);
display.println(F("LAMP:OFF?"));
display.display(); // Show initial text

delay(2000); // THIS IS WHERE THE CHECK NEEDS TO OCCUR

display.clearDisplay();
display.setCursor(0, 25);
display.println(F("LAMP:OFF!"));
display.display(); // Show initial text
display.invertDisplay(true);
delay(600);
display.clearDisplay();
display.setCursor(0, 25);
display.println(F("LAMP:OFF"));
display.display(); // Show initial text
display.invertDisplay(false);
digitalWrite(ledPin, LOW);
state = 0;
}

My programming is limited to old school BASIC, am i going about this the wrong way and the whole program should be inside loop to utilise millis()? any help appreciated

My problem is, at the start of each subroutine a message is displayed on the OLED asking if the lamp wants to be turned on? /off? /timer? this should pause for a few seconds and then confirm this is the correct choice

If you did this in a separate block of code that was called repeatedly from loop() then you could use millis() for timing.

What I have in mind is a state machine. This sounds daunting but really isn't. List all the states that the program can be in and what causes a change of state, be it user input, time passing or maybe both. Write code for each state and the change to another state.

I find it easier to use switch/case based on the current state rather than a series if if/else to control which portion of code is executed each time through loop() and giving each state its own name also makes the code easier to follow.

Incidentally, why is it necessary to use an interrupt to read an input ?

You are going to get into trouble with delay() statements.
I'd also recommend enhancing your state model, maybe something like this:

I would change the state model to include the following states:
lamp_on_requested
lamp_off_requested
lamp_timer_requested
lamp_on_in_verification
lamp_off_in_verification
lamp_timer_in_verification
no_action

and set a timer on entry to each state:

uint32_t lamp_on_reqested_at_ms = millis() ;

etc.

check the states continuously in the loop() and act when a timer has expired.

if ( state == lamp_on_requested  ) {
    // button press indicates we want to switch the lamp on
    lamp_on_in_verification_at_ms = millis() ;
    state = lamp_on_in_verification ;
    // update display
}

if ( state == lamp_on_in_verification && millis() - lamp_on_in_verification_at_ms > 2000 ) {
    // timer expired so switch lamp on
    state = no_action ;
}
. . .

Okay I will try that avenue. I used an interrupt as i was planning to interrupt the delay statement to advance to the next routine, before i understood that this cannot be done, so it is legacy code now and I will look at changing it, thanks for the tips