Jahangir Minhas
Jahangir Minhas

Reputation: 19

How to constantly listen for button input in Arduino?

I am trying to create a working physical simulation of a traffic light intersection. I want to listen for button readings (pedestrian buttons to turn pedestrian lights green) continuously throughout the program and want to run a different function that will handle pedestrian lights if the buttons are ever pressed during the program. I have tried it with while loops and if conditions but it just won't work because then it would read the button input at a specific point in time when I want it to read the readings constantly throughout the program and break the loop if the condition is ever untrue (while and do-while loops only check the condition at the end of the loop when I want it to check the condition throughout the loop). I also need to get which button was pressed if that possible. Then depending on which button was pressed, I want to run a function called pedes() or pedes2(). Feel free to ask if you need any clarifications.

I have posted my original code below that I want to run constantly until a button will be pressed. Thanks!

// If at any point in time during this code, a button is pushed, I want to run a function called
    pedes() or pedes2() depending on which button is pressed.

// First set of Trafiic Lights
int redT = 13 ;
int yellowT = 12;
int greenT = 11;

// First set of Pedestrian Lights
int redP = 10;
int greenP = 9;

// Second set of Traffic Lights
int redT2 = 8;
int yellowT2 = 7;
int greenT2 = 6;

// Second set of Pedestrian Lights
int redP2 = 5;
int greenP2 = 4;

// Pedestrian Buttons
int buttonT = 3;
int buttonT2 = 2;
int buttonT3 = 1;
int buttonT4 = 0;

int buttonStateT = 0;
int buttonStateT2 = 0;
int buttonStateT3 = 0;
int buttonStateT4 = 0;

// Booleans which will handle which button was pressed

void setup() {
  // First set of Trafiic Lights
  pinMode(redT, OUTPUT);
  pinMode(yellowT, OUTPUT);
  pinMode(greenT, OUTPUT);

  // First set of Pedestrian Lights
  pinMode(redP, OUTPUT);
  pinMode(greenP, OUTPUT);

  // Second set of Traffic Lights
  pinMode(redT2, OUTPUT);
  pinMode(yellowT2, OUTPUT);
  pinMode(greenT2, OUTPUT);

  // Second set of Pedestrian Lights
  pinMode(redP2, OUTPUT);
  pinMode(greenP2, OUTPUT);

  // Pedestrian Buttons
  pinMode(buttonT, INPUT);
  pinMode(buttonT2, INPUT);
  pinMode(buttonT3, INPUT);
  pinMode(buttonP4, INPUT);
}

void loop() {
    // Resetting all the traffic lights
    digitalWrite(redP, HIGH); // Turns on red pedestrian LED from 1st bunch
    digitalWrite(redP2, HIGH); // Turns on red pedestrian LED from 2nd bunch

    digitalWrite(yellowT, LOW); // Turns off yellow traffic LED from 1st bunch
    digitalWrite(yellowT2, LOW); // Turns off yellow traffic LED from 1st bunch

    digitalWrite(redT, HIGH); // Turns on red traffic LED from 1st bunch
    digitalWrite(redT2, HIGH); // Turns on red traffic LED from 2nd bunch

    delay(2000);

    digitalWrite(redT, LOW); // Turns off red traffic LED from 1st bunch
    digitalWrite(redT2, HIGH); // Turns on red traffic LED from 2nd bunch

    digitalWrite(greenT, HIGH); // Turns on green traffic LED from 1st bunch
    digitalWrite(greenT2, LOW); // Turns off green traffic LED from 2nd bunch

    delay(10000); // Pauses program for 8 seconds

    digitalWrite(greenT, LOW); // Turns off green traffic LED from 1st bunch
    digitalWrite(yellowT, HIGH); // Turns on yellow traffic LED from 1st bunch

    delay(3000); // Pauses program for 3 seconds

    digitalWrite(yellowT, LOW); // Turns off yellow traffic LED from 1st bunch
    digitalWrite(redT, HIGH); // Turns on red traffic LED from 1st bunch

    delay(2000); // Pauses program for 3 seconds

    digitalWrite(redT2, LOW); // Turns off red traffic LED from 2nd bunch
    digitalWrite(greenT2, HIGH); // Turns on green traffic LED from 2nd bunch

    delay(6000); // Pauses program for 8 seconds

    digitalWrite(greenT2, LOW); // Turns off green traffic LED from 2nd bunch
    digitalWrite(yellowT2, HIGH); // Turns on yellow traffic LED from 2nd bunch

    delay(3000); // Pauses program for 3 seconds

    digitalWrite(yellowT2, LOW); // Turns off yellow traffic LED from 2nd bunch
    digitalWrite(redT2, HIGH); // Turns on red traffic LED from 2nd bunch

    delay(2000); // Pauses program for 3 seconds
  }

Upvotes: 1

Views: 5556

Answers (3)

Danny_ds
Danny_ds

Reputation: 11406

Instead of constantly polling you could use an interrupt on the button pin and set a flag or call a function from within the interrupt routine.

Also make sure to debounce the button in hardware or software.


Since you seem to be learning, here are some extra improvements you could add as an exercise:

  • To switch the lights you could use timer interrupts instead of delays (you could even put the mcu in sleep mode in between).

  • Another improvement would be to create a TrafficLight class which contains all the logic. Here is an interesting tutorial. This way you could easily create multiple objects: TrafficLight tl1(..), tl2(..); or even arrays of traffic lights (for example with the same timings).

  • You could then even create a class Intersection which contains all the traffic lights and the logic for that intersection.

Upvotes: 2

Mitch Davis
Mitch Davis

Reputation: 55

Avoid using delays. Delays will pause everything until the time is up, which makes everything less responsive. Delays are useful when learning how to code, but they can hinder usability if you need to react to an external sensor or button.

A better habit to get into is using timers instead of delays. This lets your code keep looping without pausing, and you can add code to check for user input more frequently.

Here is an example of what you should NOT do:

void loop() {
  digitalWrite(MY_LED,HIGH);
  delay(1000);
  digitalWrite(MY_LED,LOW);
  delay(1000);

  //This code only gets reached every 2 seconds
  //This means you may need to hold the button for up to
  //2 seconds before it will print a message
  if (digitalRead(MY_BUTTON) == LOW) {
    Serial.println("You pressed the button");
  }
}

You can use millis() to return the number of milliseconds since the arduino started running. Here is a basic example of a timer:

bool ledState = false;
unsigned long toggledTime = 0;  //The last time we toggled the led

void loop() {

  //Calculate how much time has passed since the last time
  //we turned the LED on or off
  unsigned long timeElapsedSinceLastToggle = millis() - toggledTime;

  //If 1000ms (1s) has passed, we'll toggle the LED
  if (timeElapsedSinceLastToggle > 1000) {
    ledState = !ledState;           //Invert the state
    digitalWrite(MY_LED,ledState);  //Perform the action
    toggledTime = millis();         //Reset our timer to the current time
  }

  //This code now checks very frequently since the code above
  //never uses delay()
  if (digitalRead(MY_BUTTON) == LOW) {
    Serial.println("You pressed the button");
  }
}

I would recommend using a different timer for each LED that you want to toggle. This will make your code much more responsive, and your buttons will respond (nearly) instantly.

There is one thing you will want to be careful with regarding timers. millis() is how many milliseconds since boot, but what happens when your code runs for a very long time? millis() resets (starts over at 0) after about 50 days. For lots of people, this doesn't matter much, but it is worth keeping in mind if your code will be running indefinitely for long periods of time.

Upvotes: 0

Piglet
Piglet

Reputation: 28974

Well if you delay for 10 seconds you cannot react to something in the meantime.

You can use millis() to implement a non-blocking delay. https://www.arduino.cc/en/tutorial/BlinkWithoutDelay

Instead of being idle and unable to respond you spend your time with checking and responding to your button states and/or time conditions.

Upvotes: 0

Related Questions