Breaking out of a while loop

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#define password_length 5
char holder[password_length];
char correctPassword[password_length] = "2003";
int redLED = 13;
int greenLED = 12;
int wrong_password = 0;

byte holder_count = 0;
char keyPress;

const int buttonPin = 10;
int buttonState = 0;

const byte ROWS = 4;
const byte COLS = 4;

char kp[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
  
};

//assign Arduino pins 9-2 for rows & columns
byte rowPins[ROWS] = {9,8,7,6};
byte colPins[COLS] = {5,4,3,2};


LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 20 chars and 4 line display
Keypad keypad = Keypad(makeKeymap(kp), rowPins, colPins, ROWS, COLS);



void setup()
{
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}


void loop(){
  buttonState = digitalRead(buttonPin);
  lcd.setCursor(0,0);
  lcd.print("Enter Password:");
  digitalWrite(redLED, HIGH);
  //digitalWrite(greenLED,HIGH);
  char keyPress = keypad.getKey();
  if (keyPress != NO_KEY){
    Serial.println(keyPress);
  }
  if (keyPress){
    holder[holder_count] = keyPress;
    lcd.setCursor(holder_count, 1);
    lcd.print(holder[holder_count]);
    holder_count++;
  }
 
 if (holder_count == password_length -1){
  lcd.clear();

  if (!strcmp(holder, correctPassword) {
    lcd.print("Alarm Deactivated");
    digitalWrite(redLED, LOW);
    //delay(3000);
    Serial.println("Alarm Deactivated");
    digitalWrite(greenLED, HIGH);
    delay(2000);//time for how long the security alarm is deactivated before user alert
    digitalWrite(redLED,HIGH);
    digitalWrite(greenLED,LOW);
    if (wrong_password >0){
      wrong_password = 0;
    }
  }
  else {
    lcd.print("Incorrect Password");
    Serial.println("Incorrect Password");
    wrong_password++;
    wrong_pw();
    delay(2000);
  }

  lcd.clear();
  clearholder();
 }
}
void wrong_pw(){
  buttonState = digitalRead(buttonPin);
  buttonState = LOW;
  while (wrong_password == 2){
    lcd.setCursor(0,3);
    lcd.print("Must Unlock With Key");
    if (buttonState == HIGH){
      break;
    //delay(2000);
    //break;
  }
}}

void clearholder() {
  while (holder_count != 0) {
    holder[holder_count--]= 0;
  }
 }

Hello, Folks. This is my first post and I am fairly new to using the Arduino. I am trying to have the number of wrong keypad entries (which is 2 in this case) need to have a reset that is done by a push button. The reset should return to the top of the loop where it says “Alarm Deactivated.” In the wrong_pw function is where I am trying to break out of the while loop with the buttonState == HIGH. I can break out of the while loop just using break after it without the button, but for some reason it will not work with the push button. I have been at it for hours and would like some guidance if possible. Thanks!

 while (wrong_password == 2)
  {
    lcd.setCursor(0, 3);
    lcd.print("Must Unlock With Key");
    if (buttonState == HIGH)
    {
      break;
      //delay(2000);
      //break;
    }
  }

or some reason it will not work with the push button.

Once in the while loop the value of wrong_password does not change and neither does buttonState, so the exit conditions will never be met

Thanks for the reply, Bob. Any thoughts on how I go about correcting this?

I have not looked at you code in detail, but you could consider using an if instead of a while so that loop() can run free allowing it to read inputs, change the value of variables etc

If I use an if, it immediately gets out of it and goes back to “Enter your password.” If I take the pushbutton out of that wrong_pw function and leave just the break, it works and goes back to the “Enter your password,” but I need it to only break with the pushbutton. Perhaps you have another solution when you get a moment? No worries if you don’t. Thanks.

If you want to break out of the while loop when buttonState equals HIGH then you must read buttonPin inside the while loop, which you don’t at the moment. Currently you do this before the while loop

  buttonState = digitalRead(buttonPin);
  buttonState = LOW;

Even if it were inside the while loop then how would buttonState ever be anything but LOW ?

False alarm…Thought I had it. I see what you’re saying, however. Unfortunately, I am uncertain of what to do on that. I have taken the buttonState = LOW out, but there has been no difference. I will continue to mess around with it.

slugger03,
Welcome.

Not sure if you are ready for this yet but study these 2 tutorials.
Using millis for timing
Demonstration for several things at the same time

Learning to write code this way is, I think, one of the most important steps from beginner to experienced programmer. If you apply the principals in these tutorials then you can do what Bob suggests and use if().

I have taken the buttonState = LOW out, but there has been no difference.

Did you move the digitalRead() inside the while loop ?

Incidentally, I can't help feeling that your sketch could do with a major rewrite to turn it into a state machine

I left the digitalRead in there, but it made no difference when I took it out. I have honestly tried everything to that function. Would you mind telling me how I would go about starting that state machine or a decent reference to point me at? That little bit of code has taken me hours (messing with this function alone has turned my hair grey), but I don't mind redoing it if it will make things more efficient if you believe it is necessary. For the moment, I am using LEDs throughout just to replicated some speakers and sensors that I will eventually implement. An example of a state machine code would be useful if you have one off the top of your head. I will be searching myself in the meantime. At this point, it seems there isn't much I can do to get that push button to work inside that function. I appreciate your help, Bob.

Edit: I just seen Perry's reply (thank you) and I will check those out. Thank you guys!

In general if you need to break out of a WHILE loop you should not be using WHILE. Instead use IF as suggested in Reply #3. And that may require other changes to your program.

One thought is to have a variable that keeps track of the state of your system - whether you are at the start or are at some other stage of the code. That way if you are at (say) stage2 the code for stage1 willl be skipped.

...R

I left the digitalRead in there

In none of your code posted here has the digitalRead() been in the while loop

I apologize, Bob. I misread your post earlier on the digitalRead() inside the while loop. That actually does get me out of the while loop! However, it doesn't do exactly as I need (go to the spot I want, which I'm sure the state method will eliminate), but that is more than a great start and I can probably manage that now. Furthermore, I still intend to take everyone's advice with "cleaning it up." I did actually try the switch case method (Robin has mentioned[thanks]), but I wasn't working well (due to my lack of knowledge). I would like to leave this post open if possible in case I can come up with a better switch case and ask questions if they arise. Again, thanks for the help everyone.

I did actually try the switch case method, but I wasn't working well

Switch case is a good way to construct a state machine, if you were trying it then you were on the right track, you just need to refine what you were doing.

All the sample code in my tutorials follow the rules for state machines, even if the example is so simple that a state machine is not necessary.

I did actually try the switch case method (Robin has mentioned[thanks]), but I wasn't working well

Start by listing each of the states that the program can be in. Each of these will be a state in switch/case and each will have a block of code that will deal with what is happening when in that state and as a result of time, user input or other in put move to another state.

The next step is to add details to your list of states of what will cause a change of state and the destination state depending on what happens in the current state

Once that is done you can write the code for a couple of states and test it

Yeah, I tried to put nearly all of my code in the loop into the first case, and then the while loop I was talking about I converted to be the second case. If was the first case that was throwing things off due to needed to have if’s and else’s in it (which from what I’ve read you can do). It seemed to always just go to the else statement. I believe I would only need 2 cases as the push button should just get me back to that main block of code to restart everything. I will do some for research and take your advice. Thanks for taking the time to reply.

I believe I would only need 2 cases

That seems unlikely

Possibly, but not definitively

GETTTING_PASSWORD_FROM USER
VALIDATE_PASSWORD
RESTART_INPUT
TOO_MANY_ATTEMPTS

Don't try to do too much in each state

Here is a 6 state state machine that flashes the built in LED at 3 different speeds. I don't expect you to get it straight away. Study each state individually.

The states:
flashSlow, flashMedium, flashFast
Only differ in the speed at which they flash the LED.

The states:
debounceToMedium, debounceToFast, debounceToSlow
Debounce the button and then move on to the next flashing state when the button is released.

So really there are only 2 states: flashing the LED and debouncing the button.

Ask questions when you get stuck with it.

/* Demonstration of a state machine */
/* Uses built in LED */
/* Requires a push button wired between ground and digital input 2 */
/* Tested on Nano Every */

const uint8_t buttonPin = 2;

void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  stateMachine();
}

void stateMachine() {
  const uint32_t debounceTime = 20;
  const uint32_t slowFlash = 1500;
  const uint32_t mediumFlash = 750;
  const uint32_t fastFlash = 50;
  enum states {flashSlow, debounceToMedium, flashMedium, debounceToFast, flashFast, debounceToSlow};
  static uint8_t state;
  static uint32_t lastMillis;
  uint32_t currentMillis = millis();
  bool buttonState = !digitalRead(buttonPin);     // buttonState true if button pressed
  
  switch (state) {
    
    case flashSlow:
      if (buttonState == true) {
        state = debounceToMedium;
        lastMillis = currentMillis;               // Start debounce timer
      } else {
        if (currentMillis - lastMillis >= slowFlash) {
          lastMillis = currentMillis;
          digitalWrite (LED_BUILTIN, !(digitalRead(LED_BUILTIN)));
        }
      }
      break;
    
    case debounceToMedium:
      if (buttonState == true) {
        lastMillis = currentMillis;
      } else if (currentMillis - lastMillis >= debounceTime) {
        state = flashMedium;
      }
      break;

    case flashMedium:
      if (buttonState == true) {
        state = debounceToFast;
        lastMillis = currentMillis;               // Start debounce timer
      } else {
        if (currentMillis - lastMillis >= mediumFlash) {
          lastMillis = currentMillis;
          digitalWrite (LED_BUILTIN, !(digitalRead(LED_BUILTIN)));
        }
      }
      break;

    case debounceToFast:
      if (buttonState == true) {
        lastMillis = currentMillis;
      } else if (currentMillis - lastMillis >= debounceTime) {
        state = flashFast;
      }
      break;
      
    case flashFast:
      if (buttonState == true) {
        state = debounceToSlow;
        lastMillis = currentMillis;               // Start debounce timer
      } else {
        if (currentMillis - lastMillis >= fastFlash) {
          lastMillis = currentMillis;
          digitalWrite (LED_BUILTIN, !(digitalRead(LED_BUILTIN)));
        }
      }
      break;

    case debounceToSlow:
      if (buttonState == true) {
        lastMillis = currentMillis;
      } else if (currentMillis - lastMillis >= debounceTime) {
        state = flashSlow;
      }
      break;
  }
}

Thanks, Perry. That example is extremely helpful. Quick question: If I want a “Enter Password” messaged to be displayed right at the beginning, would I use a state for that or just have the message at the top of the main loop and then go into the states? The only time I don’t want that message is if I would hit the push button as that would prompt a “Deactivated” message. I guess my question is would it be necessary to have prompts/display messages as states? Another question: is it possible or even necessary to have multiple switches? Example: having one for keypad presses and another for button presses?

I would include it in its own state, you just arrange that you never return to that state if you never want to see it again.

You can have nested switches and separate multiple switches, but understand how to use one effectively first. I don't expect that after an hour or two looking at one example that you would fully understand how to use a state machine. You need to experiment to see what you can do, and just as importantly, what doesn't work.