Using a button for interrupts...

So I'm making a 2-lane traffic light intersection project, and I need some help with interrupts and how to go ahead with my project.

There are red, yellow, and green lights that will be changing all the time, and I have a button (and when clicked, I want a speaker to buzz, the lights to turn yellow for 1 second, the lights to turn red for 3 seconds, and then the code to continue). I have an interrupt function so that when the button is clicked, my speaker buzzes and my LEDs turn red.

Now my problem is that I can't have a delay in an interrupt, so I can't control how long my yellow lights stay on, and then how long my red lights stay on.

This is my first time using interrupts so I'm not sure what I should do. I'm also relatively new to Arudino, so help is much appreciated. Here's my code:

int red1 = 4; // red1, yellow1, and green1 are for the north-south road LEDs
int yellow1 = 3;
int green1 = 9;
int red2 = 5; // red2, yellow2, and green2 are for the west-east road LEDs
int yellow2 = 6;
int green2 = 7;

int red1state;
int yellow1state;
int green1state;
int red2state;
int yellow2state;
int green2state;

int alarm = 8; // my piezo speaker (buzzer)

int switch1 = 2; // my push button
long lastDebounceTime = 0;
long debounceDelay = 200;

void setup() {

  Serial.begin(9600);

  pinMode(red1, OUTPUT);
  pinMode(yellow1, OUTPUT);
  pinMode(green1, OUTPUT);
  pinMode(red2, OUTPUT);
  pinMode(yellow2, OUTPUT);
  pinMode(green2, OUTPUT);

  pinMode(alarm, OUTPUT);

  pinMode(switch1, INPUT);
  digitalWrite(switch1, LOW);
  attachInterrupt(digitalPinToInterrupt(switch1), buttonPress, RISING);
}

void buttonPress() // this is my interupt function
{
  if ((millis() - lastDebounceTime) > debounceDelay); {  
  Serial.println("Clicked");
  tone(alarm, 800, 1000);
    
  digitalWrite(red1, HIGH);
  red1state = HIGH;
  digitalWrite(yellow1, LOW);
  yellow1state = LOW;
  digitalWrite(green1, LOW);
  green1state = LOW;
  digitalWrite(red2, HIGH);
  red2state = HIGH;
  digitalWrite(yellow2, LOW);
  yellow2state = LOW;
  digitalWrite(green2, LOW);
  green2state = LOW;
  }
  lastDebounceTime = millis();
}

void loop() {

  digitalWrite(red1, LOW); //green and red
  red1state = LOW;
  digitalWrite(yellow1, LOW);
  yellow1state = LOW;
  digitalWrite(green1, HIGH);
  green1state = HIGH;
  digitalWrite(red2, HIGH);
  red2state = HIGH;
  digitalWrite(yellow2, LOW);
  yellow2state = LOW;
  digitalWrite(green2, LOW);
  green2state = LOW;
  delay(5000);

  digitalWrite(red1, LOW); //yellow and red
  red1state = LOW;
  digitalWrite(yellow1, HIGH);
  yellow1state = HIGH;
  digitalWrite(green1, LOW);
  green1state = LOW;
  digitalWrite(red2, HIGH);
  red2state = HIGH;
  digitalWrite(yellow2, LOW);
  yellow2state = LOW;
  digitalWrite(green2, LOW);
  green2state = LOW;
  delay(3000);

  digitalWrite(red1, HIGH); //red and red
  red1state = HIGH;
  digitalWrite(yellow1, LOW);
  yellow1state = LOW;
  digitalWrite(green1, LOW);
  green1state = LOW;
  digitalWrite(red2, HIGH);
  red2state = HIGH;
  digitalWrite(yellow2, LOW);
  yellow2state = LOW;
  digitalWrite(green2, LOW);
  green2state = LOW;
  delay(1000);

  //Midway(ish)

  digitalWrite(red1, HIGH); //red and green
  red1state = HIGH;
  digitalWrite(yellow1, LOW);
  yellow2state = LOW;
  digitalWrite(green1, LOW);
  green2state = LOW;
  digitalWrite(red2, LOW);
  red2state = LOW;
  digitalWrite(yellow2, LOW);
  yellow2state = LOW;
  digitalWrite(green2, HIGH);
  green2state = HIGH;
  delay(5000);

  digitalWrite(red1, HIGH); //red and yellow
  red1state = HIGH;
  digitalWrite(yellow1, LOW);
  yellow1state = LOW;
  digitalWrite(green1, LOW);
  green1state = LOW;
  digitalWrite(red2, LOW);
  red2state = LOW;
  digitalWrite(yellow2, HIGH);
  yellow2state = HIGH;
  digitalWrite(green2, LOW);
  green2state = LOW;
  delay(3000);

  digitalWrite(red1, HIGH); //red and red
  red1state = HIGH;
  digitalWrite(yellow1, LOW);
  yellow1state = LOW;
  digitalWrite(green1, LOW);
  green1state = LOW;
  digitalWrite(red2, HIGH);
  red2state = HIGH;
  digitalWrite(yellow2, LOW);
  yellow2state = LOW;
  digitalWrite(green2, LOW);
  green2state = LOW;
  delay(1000);
}

Thanks in advance!

It isn't clear to me why you have an interrupt at all

You sure don’t need or want a hardware based interrupt just to detect a pushbutton.

Now we’re into an x-y problem.

AWOL:
It isn't clear to me why you have an interrupt at all

Good question. The reason I have an interrupt is because I need to check if the push button has been clicked or not (I'm checking for that condition all the time).

If the button has been clicked, I want the speaker to buzz, the lights to turn yellow for 1 second, the lights to turn red for 3 seconds, and then the code to continue.

If there are any easy alternatives to this, please let me know. I am a beginner so keep that in mind :slight_smile:

WattsThat:
You sure don’t need or want a hardware based interrupt just to detect a pushbutton.

Now we’re into an x-y problem.

Any good alternatives?

Get back to us with your questions when you've got rid of the calls to delay in your main code.

A well built loop will pick up buttons pressed. Using millis() and "how to do several things at the same time interrupts are not at all needed.

I might not have been clear with my problem, sorry about that.

I have a button and when it's clicked, I want a speaker to buzz, the lights to turn yellow for 1 second, the lights to turn red for 3 seconds, and then the code to continue. Now the problem is that I don't know how to program this, because I can't have delays within functions.

AWOL:
Get back to us with your questions when you've got rid of the calls to delay in your main code.

Not sure exactly what you mean, sorry.

Railroader:
A well built loop will pick up buttons pressed. Using millis() and "how to do several things at the same time interrupts are not at all needed.

Any suggestions on how to do that? I have no clue tbh.

Study material

AWOL:
Study material

Thanks! So I've quickly read over that but there is one problem with making a function for the blinking lights and a function for the button being pressed.

If I wrote a function for the lights to blink, and another function for the button state to be read (and put it in a loop), then the Arduino can't check the state of the button while the lights function is running.

Any ideas?

The lights function will run for, at most (and that's a big "at most"), about a millisecond.

What's the problem?

r2724r16:
Thanks! So I've quickly read over that but there is one problem with making a function for the blinking lights and a function for the button being pressed.

If I wrote a function for the lights to blink, and another function for the button state to be read (and put it in a loop), then the Arduino can't check the state of the button while the lights function is running.

Read it again more slowly and you will see that the whole purpose of the program is to blink lights and check a button so fast that it appears to be at the same time. Try the program.

...R

r2724r16:
Good question. The reason I have an interrupt is because I need to check if the push button has been clicked or not (I'm checking for that condition all the time).

Ho. ho, classical "newbie" mistake, having no idea whatsoever what an "interrupt" is in computer terms. :grinning:


Sorry, I have mislaid my "stock" explanation of what an interrupt is. Had a total disk failure a while back and not quite got everything back on this machine.

Look, you need to read the reference AWOL cited, then start with some of the examples give there and "lose" your first attempt at coding; start from the very beginning.

Or just maybe, you might want to look at my pedestrian crossing code. I never got around to a full intersection.

// Pedestrian crossing lights

const int led13Pin =  13;    // LED pin number
const int PgrnPin =  2;      // Pedestrian
const int PredPin =  3;
const int RgrnPin =  5;      // Road
const int RyelPin =  6;
const int RredPin =  7;
const int button1 =  10;     // Button to ground

int led13State = LOW;        // initialise the LED
int PgrnState = LOW;
int PredState = LOW;
int RgrnState = LOW;
int RyelState = LOW;
int RredState = LOW;
char bstate1 = 0;
boolean press = false;

unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;
unsigned long count4 = 0;
unsigned long count5 = 0;
unsigned long bcount1 = 0; // button debounce timer.  Replicate as necessary.

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

void setout(unsigned long *marker) {
  *marker = millis();             // initialise
}

// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
  switch (*butnstate) {               // Odd states if was pressed, >= 2 if debounce in progress
  case 0: // Button up so far, 
    if (button == HIGH) return false; // Nothing happening!
    else { 
      *butnstate = 2;                 // record that is now pressed
      *marker = millis();             // note when was pressed
      return false;                   // and move on
    }

  case 1: // Button down so far, 
    if (button == LOW) return false; // Nothing happening!
    else { 
      *butnstate = 3;                 // record that is now released
      *marker = millis();             // note when was released
      return false;                   // and move on
    }

  case 2: // Button was up, now down.
    if (button == HIGH) {
      *butnstate = 0;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 1;               // jackpot!  update the state
        return true;                  // because we have the desired event!
      }
      else 
        return false;                 // not done yet; just move on
    }

  case 3: // Button was down, now up.
    if (button == LOW) {
      *butnstate = 1;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 0;               // Debounced; update the state
        return false;                 // but it is not the event we want
      }
      else 
        return false;                 // not done yet; just move on
    }
  default:                            // Error; recover anyway
    {  
      *butnstate = 0;
      return false;                   // Definitely false!
    }
  }
}


void setleds() {
  digitalWrite(led13Pin, led13State);
  digitalWrite(PredPin, PredState);
  digitalWrite(PgrnPin, PgrnState);
  digitalWrite(RredPin, RredState);
  digitalWrite(RyelPin, RyelState);
  digitalWrite(RgrnPin, RgrnState);
}

boolean ispress() { // One-directional read of button - sets but does not clear!
  if (butndown(digitalRead(button1), &bcount1, &bstate1, 10UL )) {
    press = true;
  } 
  return(press);
}

void setup() {
  Serial.begin(9600);
  pinMode(led13Pin, OUTPUT);      
  pinMode(PgrnPin, OUTPUT);      
  pinMode(PredPin, OUTPUT);      
  pinMode(RgrnPin, OUTPUT);      
  pinMode(RyelPin, OUTPUT);      
  pinMode(RredPin, OUTPUT);      
  pinMode(button1, INPUT);      
  digitalWrite(button1,HIGH);        // internal pullup all versions
  press = false;
  Serial.println("Starting ...");
}

void loop() {
  // All red phase
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Red phase");
  setout(&count3);  
  while (!timeout(&count3, 3000UL )) {
    ispress();  // Check on the button
  }

  // Road Green
  RredState = LOW;
  RyelState = LOW; 
  RgrnState = HIGH; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Road green");
  setout(&count3);  
  while (!timeout(&count3, 8000UL )) { // Reasonable time on green
    ispress();  // Check on the button
  }
  Serial.println("Green stale, wait on button");

  while ( press == false )  // Now wait for the button 
  {
    if (timeout(&count2, 300UL )) {
      if (led13State == LOW) {
        led13State = HIGH;
      }
      else {
        led13State = LOW; 
      } 
      digitalWrite(led13Pin, led13State);
    }
    ispress();   
  }
  led13State = LOW; 
  digitalWrite(led13Pin, led13State);

  Serial.println("Button sensed");
  setout(&count3);  
  while (!timeout(&count3, 4000UL )) { // Do not respond immediately!
  }

  // Road Yellow
  RredState = LOW;
  RyelState = HIGH; 
  RgrnState = LOW; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Road yellow");
  setout(&count3);  
  while (!timeout(&count3, 5000UL )) {
  }

  // Road Red
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Road red");
  setout(&count3);  
  while (!timeout(&count3, 3000UL )) {
  }

  // Walk Green
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PredState = LOW;
  PgrnState = HIGH; 
  setleds();
  press = false;  
  Serial.println("Walk");
  setout(&count3);  
  while (!timeout(&count3, 6000UL )) {
  }

  // Flash Don't Walk
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PgrnState = LOW; 
  Serial.println("Flash Don't Walk");
  setout(&count3);  
  while (!timeout(&count3, 7000UL )) {
    if (timeout(&count2, 500UL )) {
      if (PredState == LOW) {
        PredState = HIGH;
      }
      else {
        PredState = LOW; 
      } 
      setleds();
    }
    ispress();  // Check on the button
  }

}

void oldloop() {
  // Toggle LED if button debounced
  if (butndown(digitalRead(button1), &bcount1, &bstate1, 10UL )) {
    if (led13State == LOW) {
      led13State = HIGH;
    }
    else {
      led13State = LOW; 
    } 
    digitalWrite(led13Pin, led13State);
  } 

  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count2, 300UL )) {
    if (PgrnState == LOW) {
      PgrnState = HIGH;
    }
    else {
      PgrnState = LOW; 
    } 
    digitalWrite(PgrnPin, PgrnState);
  } 

  if (timeout(&count3, 600UL )) {
    if (PredState == LOW) {
      PredState = HIGH;
    }
    else {
      PredState = LOW; 
    } 
    digitalWrite(PredPin, PredState);
  } 

  if (timeout(&count4, 400UL )) {
    if (RgrnState == LOW) {
      RgrnState = HIGH;
    }
    else {
      RgrnState = LOW; 
    } 
    digitalWrite(RgrnPin, RgrnState);
  } 

  if (timeout(&count5, 800UL )) {
    if (RredState == LOW) {
      RredState = HIGH;
    }
    else {
      RredState = LOW; 
    } 
    digitalWrite(RredPin, RredState);
  } 
}

AWOL:
The lights function will run for, at most (and that's a big "at most"), about a millisecond.

What's the problem?

Robin2:
Read it again more slowly and you will see that the whole purpose of the program is to blink lights and check a button so fast that it appears to be at the same time. Try the program.

...R

No, the lights function will run for around 18 seconds. Here's the "stages" of the function:

  1. Green light one way, red light the other way (for 5 seconds)
  2. Yellow light one way, red light the other way (for 3 seconds)
  3. Red light one way, red light the other way (for 1 second)
  4. Red light one way, green light the other way (for 5 seconds)
  5. Red light one way, yellow light the other way (for 3 seconds)
  6. Red light one way, red light the other way (for 1 second)

My problem is checking the state of the button while the lights function is running (which I'm doing with an interrupt), and making it so that when the button is pressed, the speaker buzzes, all lights turn red, stay red for 1 second, and then the loop continues.

No, the lights function will run for around 18 seconds.

No, it won't.

The sequence may take 18 seconds, but a single function call and state change will take well under a millisecond.

r2724r16:
My problem is checking the state of the button while the lights function is running (which I'm doing with an interrupt), and making it so that when the button is pressed, the speaker buzzes, all lights turn red, stay red for 1 second, and then the loop continues.

Your problem is the word "interrupt".

Until you lose that and start again from the very beginning, you are getting nowhere! :astonished:

AWOL:
No, it won't.

The sequence may take 18 seconds, but a single function call and state change will take well under a millisecond.

Okay sure, that makes sense. I'm using an interrupt to check the state of the button all the time (in my original code).

But my question is: how can I make it so that when the button is pressed, the speaker buzzes, all lights turn red, stay red for 1 second, and then the loop continues. The problem is that I can't have a delay in an interrupt, so the lights stay red forever until they turn off in the normal loop.

Thanks in advance!

Paul__B:
Your problem is the word "interrupt".

Until you lose that and start again from the very beginning, you are getting nowhere! :astonished:

Not sure what to do though. I am a beginner, and I need to finish this project within two days lol.

r2724r16:
No, the lights function will run for around 18 seconds. Here's the "stages" of the function:

The sequence may take 18 seconds but the Arduino can do that in 18,000 small pieces, and check the button in between every one of those pieces.

It sounds like you still have not carefully studied my example program.

...R

Robin2:
The sequence may take 18 seconds but the Arduino can do that in 18,000 small pieces, and check the button in between every one of those pieces.

It sounds like you still have not carefully studied my example program.

...R

Are you suggesting that I do something like a "for loop", where I keep updating the LEDs and checking the state of the button?

It would be REALLY helpful if you tell me how I should program it. Thanks!

No, a for loop would not be the way forward.
A state machine would.

I need to finish this project within two days lol.

The sooner you forget/ignore interrupts, the better.

r2724r16:
Not sure what to do though. I am a beginner, and I need to finish this project within two days lol.

Oh, that is the problem. :astonished:

Which tech course is this?

The persistent misunderstanding is that an "interrupt" is to "interrupt" the running of the normal "loop". That is exactly what it is not for, the "loop" needs to make its own decisions.

You are seeing the loop as a sequence of actions which takes 18 - or whatever - seconds. This is completely wrong. The loop is a processor of a "state machine" which make decisions according to the condition of a number of "state variables" which include the condition of your traffic lights, whether red, yellow or green, the time since the last action and whether your button was pressed. These can be handled using "case" statements (which are a variant of "if ... else").

So your button action needs to take into account what the current state is. Also, you need to decide where in the original cycle you actually wish to return after the button-caused action. You do not for example, want to "jump back" to a point where there is only one second left on the current "green" cycle before it goes yellow - that makes no sense.

Your problem is the initial design - you have not thought it out to start with. By the way, the "XY problem" - you have failed to explain what the purpose of the button interruption is and if we understood that, we could offer more complete suggestions of how things should happen. :grinning:


I now note AWOL has mentioned the state machine. You have of course, misunderstood the complexity of the problem. It will actually take a lot of work. The code I provided is apparently quite complex but it contains all the required elements. It would be worth the time it takes to study it ...

OTOH, you really should in all fairness, do the code yourself for your assessment. :roll_eyes: