Code not reacting to button event

Hi There,

I'm trying to make a laser tripwire, it will have an LCD display and 2 buttons. It will have 4 separate "modes"

1 - Standby
2 - Arming Sequence
3 - Armed
4 - Alarm

The buttons are intended to Arm/Disarm and reset the alarm, but currently I am having trouble getting it to register the inputs. I have tested the buttons with an example program and everything appears to be functioning correctly. Here is my code so far, it would be great if somebody could read and give me any input/feedback you may have.

Thanks for reading...

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int armButton = 10;
const int analogInPin = A0;
const int resetButton = 9;
const int laserPin = 6;
const int buzzPin = 7;
int armButtonState;
int alarmState = 0;
int sensorValue = 0;
int threshold;

void setup()  { 
  
  pinMode(resetButton, INPUT);
  pinMode(armButton, INPUT);
  pinMode(laserPin, OUTPUT);
  pinMode(buzzPin, OUTPUT);
  threshold = 750;
  armButtonState = digitalRead(armButton);
  resetButtonState = digitalRead(resetButton);
} 

void loop()  {
 standbymode(); 
}
void standbymode()  {
  
  digitalWrite(laserPin, LOW);
    lcd.begin(16,2);
    lcd.print("System Ready");
    lcd.blink();
     if (armButtonState == HIGH) {   
    armsequence();
     }
     delay(100);
}

void armsequence()  {
  
  lcd.begin(16,2);
    lcd.print("System Arming in");
    lcd.setCursor(0,1);
    lcd.print("10 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("09 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("08 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("07 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("06 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("05 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("04 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("03 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("02 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("01 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("00 Seconds");
    lcd.blink();
    digitalWrite(laserPin, HIGH);
    delay(1000);
    armed();
     }


void armed()  {
  
  lcd.begin(16,2);
    lcd.print("System Armed");
    lcd.blink();
    lcd.setCursor(0,1);
    lcd.print("Disarm");
    delay(100);
    digitalWrite(laserPin, HIGH);
    if(sensorValue < threshold) {
      alarm();
    }
     if (armButton == HIGH) {   
    standbymode();
     }
}

void alarm()  {
  
  lcd.begin(16,2);
    lcd.print("ALARM");
    lcd.setCursor(0,1);
    lcd.print("LASER TRIPPED");
    //lcd.blink();
    delay(100);
    if (armButton == HIGH) {   
    standbymode();
     }
     if (resetButtonState == HIGH) {   
    armsequence();
     }
}

"armButton" is a variable which stores the pin number. It doesn't relate to the state of the pin in any way.

Which means that
if (armButton == HIGH) {
does nothing.

To read a button, you need to use digitalRead();
if (digitalRead(armButton) == HIGH) {

He did do a digitalRead for a variable called ArmButtonState at the very beginning of the code. But it won't work out there in global space. Can't call functions out there. And then it will forever stay the same until you read it again. So it never changes.

That armsequence is atrocious. You should look up and read about for loops.

Also, while it's doing all those delays, nothing will work. So you will have to be pressing the button after that is all done. Any press during the delays will not be seen.

Thanks for getting back to me.

I used

armButtonState = digitalRead(armButton);

in the setup, before using

if (armButtonState == HIGH) {   
    armsequence();

This worked fine in the test program, and I was under the impression that it amounted to the same thing. I will try swap it out and see if it improves things.

Delta_G:
That armsequence is atrocious. You should look up and read about for loops.

Ha, I know. I was ashamed, wasn't going to include it but thought I may as well be honest.

I am a total noob incase you haven't guessed, just trying to get something that works for now!

This works:

armButtonState = digitalRead(armButton);

because you have read in the state to a boolean variable using digital read. Which means this:

if (armButtonState == HIGH) {

Is checking the state of the variable, which matches the state of button as you just read it in with digitalRead().

Thanks for the replies.

I changed the references to armbuttonState and used digitalRead to get all button events. Now the code will react and move on from standbymode on button press, but will just cycle through the other modes and return there.

This is what I have now, any ideas?

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int armButton = 10;
const int analogInPin = A0;
const int resetButton = 9;
const int laserPin = 6;
const int buzzPin = 7;
int alarmState = 0;
int sensorValue = 0;
int threshold;

void setup()  { 
  
  pinMode(resetButton, INPUT);
  pinMode(armButton, INPUT);
  pinMode(laserPin, OUTPUT);
  pinMode(buzzPin, OUTPUT);
  threshold = 750;
  standbymode();
} 

void loop()  {
 standbymode(); 
}
void standbymode()  {
  
  digitalWrite(laserPin, LOW);
    lcd.begin(16,2);
    lcd.print("System Ready");
    lcd.blink();
     if (digitalRead(armButton) == HIGH) {   
    armsequence();
     }
     delay(100);
}

void armsequence()  {
  
  lcd.begin(16,2);
    lcd.print("System Arming in");
    lcd.setCursor(0,1);
    lcd.print("10 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("09 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("08 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("07 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("06 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("05 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("04 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("03 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("02 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("01 Seconds");
    lcd.blink();
    delay(1000);
    lcd.setCursor(0,1);
    lcd.print("00 Seconds");
    lcd.blink();
    digitalWrite(laserPin, HIGH);
    delay(1000);
    armed();
     }


void armed()  {
  
  lcd.begin(16,2);
    lcd.print("System Armed");
    lcd.blink();
    lcd.setCursor(0,1);
    lcd.print("Disarm");
    delay(100);
    digitalWrite(laserPin, HIGH);
    if(sensorValue < threshold) {
      alarm();
    }
     if (digitalRead(armButton) == HIGH) {   
    standbymode();
     }
}

void alarm()  {
  
  lcd.begin(16,2);
    lcd.print("ALARM");
    lcd.setCursor(0,1);
    lcd.print("LASER TRIPPED");
    //lcd.blink();
    delay(100);
    if (digitalRead(armButton) == HIGH) {   
    standbymode();
     }
     //if (resetButtonState == HIGH) {   
    //armsequence();
     //}
}

Is your button wired up correctly?

One side of the button to +5v, the other to the arduino pin. Then a resistor (anywhere from 1k to 100k) for the arduino pin to GND?

Yep, the button is working fine and wired as you describe.

Problem is with my shoddy code somewhere!

This may be part of the issue:

    if(sensorValue < threshold) {
      alarm();
    }

Nowhere in the code do you call analogRead(), which means that sensorValue is always 0, and thus always less than the threshold.

Try:

    sensorValue = analogRead(analogInPin );
    if(sensorValue < threshold) {
      alarm();
    }

Thanks for the help. Sorry, I should have mentioned that I was aware of that. Any ideas why it would jump past the alarm stage (it displays briefly on-screen) and go back to the standby?

Another thing:

void armed()  {
  
  lcd.begin(16,2);
    lcd.print("System Armed");
    lcd.blink();
    lcd.setCursor(0,1);
    lcd.print("Disarm");
    delay(100);
    digitalWrite(laserPin, HIGH);
    if(sensorValue < threshold) {
      alarm();
    }
     if (digitalRead(armButton) == HIGH) {   
    standbymode();
     }
}

You call this function from armsequence();, but as soon as it is done, it returns and the whole process repeats.

Perhaps you meant to use a while loop.

Before you put that in a while loop, I am going to point out one thing...

To begin with you have this sequence:
standbymode -> armsequence -> armed (loop in armed)
From the armed() function, you can then go to either call the functions alarm() or standbymode().
armed -> alarm -> standbymode
or
armed -> standbymode

Put it all together you get the following

standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode -> armsequence -> armed -> alarm -> standbymode ......... etc.
Notice the problem?

Hint it is called Recursion.

I think you are getting confused with how C works. The loop() function once it gets to the end, repeats from the beginning again. This is because it is defined secretly as:
while(1){
loop();
}

However your functions once they reach the end exit and go back to where they were in the last function.

Here is some pseudo code to try and illustrate

loop:
call standbymode()
   if button high
       call armsequence()
              call armed()
                    if sensor tripped
                          call alarm()
                               if button high
                                   call standbymode() //notice here if you call standbymode() the whole sequence repeats?
                                         if button high
                                                call armsequence()
                                                       call armed()
                                                             if sensor tripped
                                                                   call alarm()
                                                                        if button high
                                                                              call standbymode()
                                                                                     //notice here if you call standbymode() the whole sequence repeats?
                                                                                     return to armed();
                                                                        return to alarm()
         
                                                             if button high
                                                                   call standbymode() //notice here if you call standbymode() the whole sequence repeats?
                                                                          //notice here if you call standbymode() the whole sequence repeats?
                                                                         return to armed();
                                                             return to armsequence();
                                                       return to standbymode();
                                         return to armed()

                               return to alarm()

                    if button high
                          call standbymode() //notice here if you call standbymode() the whole sequence repeats?
                                if button high
                                       call armsequence()
                                              call armed()
                                                    if sensor tripped
                                                          call alarm()
                                                               if button high
                                                                     call standbymode() 
                                                                            //notice here if you call standbymode() the whole sequence repeats?
                                                                           return to armed();
                                                               return to alarm()

                                                    if button high
                                                           call standbymode() 
                                                                 //notice here if you call standbymode() the whole sequence repeats?
                                                                 return to armed();
                                                    return to armsequence();
                                              return to standbymode();
                                
                             return to armed()

                     return to armsequence()
              return to standbymode()
        return to loop()
jump to loop

Thanks. I see the problem now.

So do I need to break each mode or function into a seperate while loop?

What you need to do is let all the functions return to the loop. Once possibility is to have a variable which says which mode you are in.

Here is something to get you started. Shout if you need more help.

byte mode = 0;
void loop()  {
  switch (mode){
    case 0:
      standbymode(); 
      break;
    case 1:
      armsequence(); 
      break;
    case 2:
      armed(); 
      break;
    case 3:
      alarm(); 
      break;
    default:
      case = 0; //switch to standbymode if mode not defined
      break;
  }    
}
void standbymode()  {
  digitalWrite(laserPin, LOW);
  lcd.begin(16,2);
  lcd.print("System Ready");
  lcd.blink();
  if (digitalRead(armButton) == HIGH) {   
    mode = 1; //arm sequence mode
    return; //next time the loop runs, it will detect that mode =1 and call the armsequence() function. 
                //But as it is done from the main loop, you avoid recursion.
  }
  delay(100);
}

Excellent, thanks.

I had been reading a bit about switch case previously, and was unsure exactly how to implement it. This is a great help, thanks for your time!

brenryan:

    case 0:

case 1:
   case 2:
      etc

This will be much easier to read and any mistakes would be far more apparent if you use an enumerated type for your state values.

This will be much easier to read and any mistakes would be far more apparent if you use an enumerated type for your state values.

Perhaps something like this you mean?

typedef enum{
  standbyMode = 0,
  armsequenceMode = 1,
  armedMode = 2, 
  alarmMode = 3
} MODE;

MODE mode = standbyMode;
void loop()  {
  switch (mode){
    case standbyMode:
      standbymode(); 
      break;
    case armsequenceMode :
      armsequence(); 
      break;
    case armedMode :
      armed(); 
      break;
    case alarmMode :
      alarm(); 
      break;
    //default: not needed with Enum, as it can only take the four values above.
  }    
}
void standbymode()  {
  digitalWrite(laserPin, LOW);
  lcd.begin(16,2);
  lcd.print("System Ready");
  lcd.blink();
  if (digitalRead(armButton) == HIGH) {   
    mode = armsequenceMode; //arm sequence mode
    return;
  }
  delay(100);
}

Spot on, and the only change I'd suggest is to change standbymode() to standby(). Both for consistency with the other function names, and to avoid a situation where you have two identifiers which only differ in capitalisation.

Hi everybody,

and thanks a million for all the helpful replies, sorry I haven't had internet access to respond in a while.

I was wondering if somebody could please help me clean up that appalling arming sequence I have used (see above), by using a for loop.

There's not much point in showing you what I've come up with, because it dosen't work.

Basically I want the arming sequence to countdown from 10 before entering the armed mode, and to be interruptible by button input.

The LCD should display "Arming in XX Seconds..." and count down from there.

I would really appreciate any help...