Stopping a while loop with a button

Hi everybody,

I’m new to the Arduino thing and had a question regarding a while loop and a button. I am attempting to make a RGB LED do certain things every time that the button is pressed. Pressing the button the first time makes all RGB values 255 making it white, which is what I want. Pressing the button for a second time causes the LED to enter a repeating pattern of fading colors, which is also what I want. However, it seems to be stuck within the while loop and pressing the button a third time does not change from the pattern in the while loop, and I want the LED to shut off when the button is pressed the third time. What do I need to change in my sketch to make it stop the pattern whenever the button is pressed again?

const int switchPin = 2;
const int red = 11;
const int green = 9;
const int blue = 10;
int buttonState;
int lightState = 0;
int i = 0;
int wait = 8;
#define delayTime 8

void setup(){
  pinMode(switchPin, INPUT);
  pinMode(red, OUTPUT);
  pinMode(green, OUTPUT);
  pinMode(blue, OUTPUT);
  Serial.begin(9600);
  buttonState = digitalRead(switchPin);
}

void loop() {
  int val = digitalRead(switchPin);
  delay(10);
  int val2 = digitalRead(switchPin);
  if (val == val2) {
  if (val != buttonState) {
    if (val == LOW) {
      if (lightState == 0) {
        lightState = 1;
        digitalWrite(red, HIGH);
        digitalWrite(blue, HIGH);
        digitalWrite(green, HIGH);
        analogWrite(red, 255);
        analogWrite(green, 255);
        analogWrite(blue, 255);
        Serial.println("Button just pressed, light on");
      }
      else if(lightState == 1){
        lightState = 2; 
          int redVal = 255;
          int blueVal = 0;
          int greenVal = 0;
              while(lightState == 2) {
  for( int i = 0 ; i < 255 ; i += 1 ){
    greenVal -= 1;
    redVal += 1;
    analogWrite(green, 255 - greenVal);
    analogWrite(red, 255 - redVal);
    analogWrite(blue, 0);
    delay(delayTime);
  }
  for( int i = 0 ; i < 255 ; i += 1 ){
    greenVal += 1;
    blueVal -= 1;
    analogWrite(green, 255 - greenVal);
    analogWrite(red, 0);
    analogWrite(blue, 255 - blueVal);
    delay(delayTime);
  }
  for( int i = 0 ; i < 255 ; i += 1 ){
    blueVal += 1;
    redVal -= 1;
    analogWrite(green, 0);
    analogWrite(red, 255 - redVal);
    analogWrite(blue, 255 - blueVal);
    delay(delayTime);
  }
          }
        Serial.println("Button just pressed, pattern begin");
      }
      else if(lightState == 2) {
        lightState = 0;
        digitalWrite(red, LOW);
        digitalWrite(blue, LOW);
        digitalWrite(green, LOW);
        Serial.println("Button just pressed, light off");
      }      
      }
      
     }
    }
  
  buttonState = val;
  }

You seem to have a combination of Finite State Machine (for example)

      if (lightState == 0) {
        lightState = 1;

and non-FSM code. Any place you use delay(...) is suspect. True FSM code would have little use for it. Once your code gets into the fading colors, there is no checking for the button presses. There is little wonder that the code appears to be stuck.

I am not going to rewrite your code for you, but you MUST learn to code without using delay(...).

Your FSM is almost there. Keep going!

a while is like a small loop once its entered it will not exit until it has finished. For is also the same and delay just stops the arduino dead so it will not monitor for a button pressed. The only way to exit is to read the button inside the “for” loops then give it a way to exit.

you really don’t need to use a “for” loop to fade the led as a simple counter will work that will not block the button being read

I played with your code and can not understand how you are using the button pushes to advance the program.

you have while (lightState == 2) but no way to exit then else if (lightState == 2)

lightstate is only changing because you told it to like a cascade lightState = 1; then lightState = 2; the button does not advance lightState so im not sure what you are trying to do

also the maths on the fade looks odd as you are minus on one color and plus on another but both start at 255

I don't think it does what you are saying it does

As I was messing around in the code I will post what I did. This is not a fix for your program but it will give you a idea (probably wont work i was just playing with removing the while, for and delay and the fade maths still looks wrong)

const int switchPin = 2;
const int red = 11;
const int green = 9;
const int blue = 10;
int buttonState;
int lightState = 0;
byte fade = 0;
byte counter = 0;
int redVal;
int greenVal;
int blueVal;
unsigned long delayTime = 8;
unsigned long  previousMillis = 0;

void setup() {
  pinMode(switchPin, INPUT);
  pinMode(red, OUTPUT);
  pinMode(green, OUTPUT);
  pinMode(blue, OUTPUT);
  Serial.begin(9600);
  buttonState = digitalRead(switchPin);
}

void loop() {
  unsigned long currentMillis = millis();
  int val = digitalRead(switchPin);
  delay(10);
  int val2 = digitalRead(switchPin);
  if (val == val2) {
    if (val != buttonState) {
      if (val == LOW ) {
        lightState++;
      }
    }
  }


  if (lightState >= 4) {
    lightState = 0;
  }
  //lightState = 2; // test
  switch (lightState) {

    case 0:
      digitalWrite(red, HIGH);
      digitalWrite(blue, HIGH);
      digitalWrite(green, HIGH);
      analogWrite(red, 255);
      analogWrite(green, 255);
      analogWrite(blue, 255);
      Serial.println("Button just pressed, light on");
      break;

    case 1:
      redVal = 255;
      blueVal = 0;
      greenVal = 0;
      break;

    case 2:

      if (currentMillis - previousMillis >= delayTime) {

        switch (fade) {
          case 0:
            counter++;
            if (counter == 255) {
              counter = 0;
              fade = 1;
            }
            greenVal -= 1;
            redVal += 1;
            analogWrite(green, 255 - greenVal);
            analogWrite(red, 255 - redVal);
            analogWrite(blue, 0);
            //delay(delayTime);
            break;

          case 1:
            counter++;
            if (counter == 255) {
              counter = 0;
              fade = 2;
            }
            greenVal += 1;
            blueVal -= 1;
            analogWrite(green, 255 - greenVal);
            analogWrite(red, 0);
            analogWrite(blue, 255 - blueVal);
            //delay(delayTime);
            break;

          case 2:
            counter++;
            if (counter == 255) {
              counter = 0;
              fade = 0;
            }
            blueVal += 1;
            redVal -= 1;
            analogWrite(green, 0);
            analogWrite(red, 255 - redVal);
            analogWrite(blue, 255 - blueVal);
            //delay(delayTime);
            break;
        }
        previousMillis = currentMillis;
      }
      break;

    case 3:
      digitalWrite(red, LOW);
      digitalWrite(blue, LOW);
      digitalWrite(green, LOW);
      Serial.println("Button just pressed, light off");
  }
  // Serial.print ("green ");
  // Serial.print (greenVal);
  // Serial.print (" blue ");
  // Serial.print (blueVal);
  //Serial.print (" red ");
  //Serial.println (redVal);
  buttonState = val;
}

I was going to point out that lightstate gets increased from zero to one to two, and then wraps around to zero again... but then I saw that the posted code has changed. That is rude. I give up.

you should be able to use an interrupt for the button state change. this will stop code execution, run the interrupt function and return to the code. otherwise you need to keep checking for the button state inside your loops.

but you will need to debounce the button press properly otherwise the INT will fire several times. and you can't use delay() inside an interrupt to do a quasi debounce.