I'm using a state machine, but the states are not switching properly

I'm trying to make a Simon says style Arduino game, but I am having a lot of trouble switching between the different states. The code definitely hits these statements, but nothing happens when it does. Specifically, this is happening at state = error in the user input section of the switch statement. Here is the code:

typedef enum { output, userInput, idleReset, finish, error} SimonState;
SimonState state = output;
bool boot = true;
int greenButton = 5;
int greenLed = 6;
int yellowButton = 7;
int yellowLed = 8;
int redButton = 9;
int redLed = 10;
int blueButton = 11;
int blueLed = 12;
int counter = 0;
int memory[10];
int userMem[10] = {4};
int buzzer = 3;
int roundsNumber = 1;
int blinkDelay = 500;
int totalRounds = 3;


int buttonValueg;
int buttonStateg = LOW;
int lastKnowng = LOW;
int lastTimeg = 0;
void debounceGreen(int inputCheck) {
  buttonValueg = digitalRead(greenButton);
  if (buttonValueg != lastKnowng) {
    lastTimeg = millis();
  }
  if ((millis() - lastTimeg) > 50) {
    if (buttonValueg != buttonStateg) {
      buttonStateg = buttonValueg;
      if (buttonStateg == HIGH) {
        userMem[inputCheck] = 0;
        green();
      }
    }
  }
  lastKnowng = buttonValueg;
}
int buttonValuer;
int buttonStater = LOW;
int lastKnownr = LOW;
int lastTimer = 0;
void debounceRed(int inputCheck) {
  buttonValuer = digitalRead(redButton);
  if (buttonValuer != lastKnownr) {
    lastTimer = millis();
  }
  if ((millis() - lastTimer) > 50) {
    if (buttonValuer != buttonStater) {
      buttonStater = buttonValuer;
      if (buttonStater == HIGH) {
        userMem[inputCheck] = 2;
        red();
      }
    }
  }
  lastKnownr = buttonValuer;
}
int buttonValueb;
int buttonStateb = LOW;
int lastKnownb = LOW;
int lastTimeb = 0;
void debounceBlue(int inputCheck) {
  buttonValueb = digitalRead(blueButton);
  if (buttonValueb != lastKnownb) {
    lastTimeb = millis();
  }
  if ((millis() - lastTimeb) > 50) {
    if (buttonValueb != buttonStateb) {
      buttonStateb = buttonValueb;
      if (buttonStateb == HIGH) {
        userMem[inputCheck] = 3;
        blue();
      }
    }
  }
  lastKnownb = buttonValueb;
}
int buttonValuey;
int buttonStatey = LOW;
int lastKnowny = LOW;
int lastTimey = 0;
void debounceYellow(int inputCheck) {
  buttonValuey = digitalRead(yellowButton);
  if (buttonValuey != lastKnowny) {
    lastTimey = millis();
  }
  if ((millis() - lastTimey) > 50) {
    if (buttonValuey != buttonStatey) {
      buttonStatey = buttonValuey;
      if (buttonStatey == HIGH) {
        userMem[inputCheck] = 1;
        yellow();
      }
    }
  }
  lastKnowny = buttonValuey;
}


void errorTone() {
  for (int i = 0; i < 3; i++) {
    tone(buzzer, 250);
    delay(250);
    noTone(buzzer);
    delay(250);

  }
  boot = false;
}
void winner() {
  for (int i = 0; i < 3; i++) {
    digitalWrite(greenLed, HIGH);
    delay(50);
    digitalWrite(yellowLed, HIGH);
    delay(50);
    digitalWrite(redLed, HIGH);
    delay(50);
    digitalWrite(blueLed, HIGH);
    delay (250);
    digitalWrite(greenLed, LOW);
    delay(50);
    digitalWrite(yellowLed, LOW);
    delay(50);
    digitalWrite(redLed, LOW);
    delay(50);
    digitalWrite(blueLed, LOW);
    delay(250);
  }
  noTone(buzzer);
  tone(buzzer, 440);
  delay(100);
  tone(buzzer, 587);
  delay(100);
  tone(buzzer, 784);
  delay(100);
  tone(buzzer, 880);
  delay(100);
  noTone(buzzer);
}
void green() {
  digitalWrite(greenLed, HIGH);
  tone(buzzer, 880);
  delay(500);
  noTone(buzzer);
  delay(blinkDelay);
  digitalWrite(greenLed, LOW);
}
void yellow() {
  digitalWrite(yellowLed, HIGH);
  tone(buzzer, 784);
  delay(500);
  noTone(buzzer);
  delay(blinkDelay);
  digitalWrite(yellowLed, LOW);
}
void red() {
  digitalWrite(redLed, HIGH);
  tone(buzzer, 440 );
  delay(500);
  noTone(buzzer);
  delay(blinkDelay);
  digitalWrite(redLed, LOW);
}
void blue() {
  digitalWrite(blueLed, HIGH);
  tone(buzzer, 587);
  delay(500);
  noTone(buzzer);
  delay(blinkDelay);
  digitalWrite(blueLed, LOW);
}
void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(0));
  for (int i = 0; i < 10; i++) {
    memory[i] = random(4);
  }
  pinMode(greenLed, OUTPUT);
  pinMode(yellowLed, OUTPUT);
  pinMode(redLed, OUTPUT);
  pinMode(blueLed, OUTPUT);
  pinMode(greenButton, INPUT);
  pinMode(yellowButton, INPUT);
  pinMode(redButton, INPUT);
  pinMode(blueButton, INPUT);
  pinMode(buzzer, OUTPUT);
}

void loop() {

  switch (state) {
    case output:

      if (roundsNumber > totalRounds) {
        Serial.println("Big memory!");
        state = finish;

      }
      Serial.println("output");
      if (counter >= roundsNumber)
        state = userInput;
      else if (memory[counter] == 0)
        green();
      else if (memory[counter] == 1)
        yellow();
      else if (memory[counter] == 2)
        red();
      else if (memory[counter] == 3)
        blue();
      delay(500);
      counter++;
      break;
    case userInput:
      Serial.println("input time baaaaaaaabyyyyyyyyy");

      for (int inputCheck = 0; inputCheck < roundsNumber;) {
        Serial.println("debouncing...");
        debounceGreen(inputCheck);
        debounceBlue(inputCheck);
        debounceYellow(inputCheck);
        debounceRed(inputCheck);
        if (userMem[inputCheck] != memory[inputCheck] && userMem[inputCheck] != 4) {
          Serial.println("failed");
          Serial.println(inputCheck);
          state = error;
        }
        else if (userMem[inputCheck] == memory[inputCheck]) {
          Serial.println("passed");
          Serial.println(inputCheck);
          inputCheck++;
        }
      }
      delay(250);
      state = idleReset;
      break;
    case idleReset:
      for (int i = 0; i < totalRounds; i++) {
        userMem[i] = 4;
      }
      roundsNumber++;

      counter = 0;
      state = output;
      break;
    case finish:
      Serial.println("Winner!");
      winner();

      break;
    case error:
      Serial.println("error");
      errorTone();
      break;
  }
}```

OK, here we go - read the instructions and use the "pencil" icon below your post to edit it. :face_with_raised_eyebrow:

1 Like

Please edit your post to add codes. Before you copy and paste from the Arduino IDE, hit CTRL-T to autoformat it.

Sorry about that, just fixed it

Sorry about that, just fixed it

Zero comments not a good start.

My bad, I've never done this before, I am just wondering why the statement "state = error;" does not switch the code to the error case

I see nothing obviously wrong with the code.

I'm not convinced by your claims. How did you make certain that state==error, and that the corresponding case was not executed?

I used the serial monitor and there are comments I left, I'll attach a photo of it looping through it

Following emission of the "failed" message, the state is set to error, then reset by this:

     delay(250);
      state = idleReset;
1 Like

if I were to put that into an if statement, for example, if (inputCheck == roundsNumber) would it fix it?

Better to put in a break statement, after setting the error flag.

Sorry, but Im not 100% following, where would I put the break?

Also, if I remove this portion and intentionally get the input wrong, nothing changes, it just keeps looping through the for loop

What is that supposed to mean? Always post revised code.

One option is to put the break right after state = error; because there is no reason to consider other situations.

Well, it does stop the loop, which I may be able to use to make the code do what I want to. I'm really sorry I'm god awful at explaining my problems

It helps to stop and think about what you write, and whether it will make sense to anyone else, before hitting the "reply" button.

1 Like

It still doesn't switch, but I just got rid of the error case and put its contents where the switch statement was. Thanks so much, I just wasn't able to see the code with an open mind.

What @jremington is saying is that you should follow your code; e.g.

    case userInput:
      Serial.println("input time baaaaaaaabyyyyyyyyy");

      for (int inputCheck = 0; inputCheck < roundsNumber;)
      {
        Serial.println("debouncing...");
        debounceGreen(inputCheck);
        debounceBlue(inputCheck);
        debounceYellow(inputCheck);
        debounceRed(inputCheck);
        if (userMem[inputCheck] != memory[inputCheck] && userMem[inputCheck] != 4)
        {
          Serial.println("failed");
          Serial.println(inputCheck);
          state = error;
        }
        else if (userMem[inputCheck] == memory[inputCheck]) {
          Serial.println("passed");
          Serial.println(inputCheck);
          inputCheck++;
        }
      }
      delay(250);
      state = idleReset;
      break;

If an error occurs, you execute state = error. However, the code in your case continues and will encounter state = idleReset; so the state will be idleReset and not error. If you put a break as shown below, your case will end and the state will be error.

        if (userMem[inputCheck] != memory[inputCheck] && userMem[inputCheck] != 4)
        {
          Serial.println("failed");
          Serial.println(inputCheck);
          state = error;
          break;  // <----------------
        }
1 Like

Just realised that that will only break from the for-loop. You will probably meed an additional condition and not set state to idleState if state is error.