Stuck State Machine

This is my second time writing a state machine. With both so far i have been able to use the serial monitor to figure out where my problem is. I've been pounding my head off my desk debugging this code over the last few days but I'm officially stumped.

Disclaimer: I'm sure I'm missing something so simple but I can't for the life of me figure out where I'm going wrong.

The code is a state machine that I'm using to run an automatic hot stamping machine for work. The relays are used to trigger pneumatic pistons used to cycle parts and to stamp. The IR Sensor is used to check to see if the machine is loaded with parts. So once the stamp button is pressed the machine will continue to cycle the relays as long as the machine stays loaded. All of this works, it even stops when the ir sensor no longer detects the machine is loaded.

The problem came in when I added "button1". button1 is supposed to be used as a stop button. if the user needs to stop the machine while its still loaded they can just press and hold this button then it should go back to the beginning of the state machine and be ready for the stampButton to be pressed again to start again(it checks for this button press in state 10 before it has time to cycle). This works, in state 10 it checks the button and if pressed it jumps to state 12 but this is where I run into problems.

The code gets stuck in state 12 instead of jumping to state 0. The code is then locked in state 12 and nothing I've tried gets it out. I've tried using state 12 as a button debouncing state (i.e. Case 12: t_0_button1 = millis(); state = 13; break;) but that gets stuck as well. That's why I changed it to just simply jump to state 0.

I do not know why the code is getting stuck in state 12. Someone please help me with my beginner mistakes.

below is the code and attached is the serial monitor display:

#include <Stepper.h> //stepper library
#include <LiquidCrystal_I2C.h> //LCD library
#include <Wire.h>


LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


//declairing pins
int irSensor = 9; //Clip load check sensor
int LED = 4; //Load indicator
int stampButton = 2; //Arcade button
int relay1 = 6; //Pusher
int relay2 = 5; //Stamp
int detect;
int val_irSensor = 0;
int val_button1 = 0;
int val_stampButton = 0;
int val_LED = 0;
int state = 0;
int statePrev = 0;
int button1 = 3; //stop button


//declaring Stepper Pins
int in1Pin = 13; //in1 pin of gluing stepper driver
int in2Pin = 12; //in2 pin of gluing stepper driver
int in3Pin = 11; //in3 pin of glueing stepper driver
int in4Pin = 10; //in4 pin of glueing stepper driver
const int stepsPerRev = 200; //steps per one revalution of the glueing stepper motor and turn table
Stepper myStepper(stepsPerRev, in1Pin, in2Pin, in3Pin, in4Pin); //setting pins of the gluing stepper motor and naming the motor


//declaring unsigned long
unsigned long t_button1 = 0;
unsigned long t_0_button1 = 0;
unsigned long t_stampButton = 0;
unsigned long t_0_stampButton = 0;
unsigned long bounce_delay = 5;

void setup() {
  //declaring inputs
  pinMode (irSensor, INPUT);
  //declaring outputs
  pinMode (LED, OUTPUT);
  pinMode (relay1, OUTPUT);
  pinMode (relay2, OUTPUT);
  //calling pullup resistors
  pinMode (stampButton, INPUT_PULLUP);
  pinMode (button1, INPUT_PULLUP);
  digitalWrite (relay1, HIGH);
  digitalWrite (relay2, HIGH);
  digitalWrite (LED, HIGH);
  pinMode(in1Pin, OUTPUT);
  pinMode(in2Pin, OUTPUT);
  pinMode(in3Pin, OUTPUT);
  pinMode(in4Pin, OUTPUT);

  lcd.begin(16, 2);
  lcd.clear();
  lcd.print ("HELLO!");
  delay (2000);
  lcd.setCursor (0, 0);

  Serial.begin (9600);
  Serial.println ("start");

}

void loop() {
  int stampPot = analogRead (A1);
  int stampTime = map(stampPot, 1023, 0, 0, 10 );
  int foilpot = analogRead (A2);
  int foilshiftlength = map (foilpot, 1023, 0, 0, 500);

  //printing on screen
  lcd.setCursor(0, 0);
  lcd.print ("stamp time = ");
  lcd.setCursor(13, 0);
  lcd.print("   ");
  lcd.setCursor(13, 0);
  lcd.print (stampTime);
  lcd.setCursor(0, 1);
  lcd.print ("foil shift = ");
  lcd.setCursor(13, 1);
  lcd.print("   ");
  lcd.setCursor(13, 1);
  lcd.print (foilshiftlength);

  Serial.print("stamp time = ");
  Serial.println(stampTime);
  Serial.print("foil shift = ");
  Serial.println(foilshiftlength);
  Serial.println ("CYCLE");

  SM_LM (); //calling state machine
  Serial.println("IR Check");
  val_irSensor = digitalRead (irSensor);

  if (state == 0 ) {

    Serial.println ("State = 0");
    lcd.setCursor(0, 0);
    lcd.print ("stamp time = ");
    lcd.setCursor(13, 0);
    lcd.print("   ");
    lcd.setCursor(13, 0);
    lcd.print (stampTime);
    lcd.setCursor(0, 1);
    lcd.print ("foil shift = ");
    lcd.setCursor(13, 1);
    lcd.print("   ");
    lcd.setCursor(13, 1);
    lcd.print (foilshiftlength);

    Serial.print("stamp time = ");
    Serial.println(stampTime);
    Serial.print("foil shift = ");
    Serial.println(foilshiftlength);

    state = 1;
  }
  if (state == 1) {
    Serial.println("state = 1");
    val_button1 = digitalRead(button1);
    if (val_button1 == LOW) {
      state = 13;
    }
    int detect = digitalRead(irSensor);
    if (detect == LOW) {
      digitalWrite(LED, LOW);
      state = 0;
    }
    if (detect == HIGH) {
      digitalWrite (LED, HIGH);
      state = 2;
    }
  }

  if (state == 6) {
    Serial.println ("State = 6");
    int detect = digitalRead(irSensor);
    if (detect == LOW) {
      digitalWrite(LED, LOW);
    }
    if (detect == HIGH) {
      digitalWrite (LED, HIGH);
    }
    digitalWrite(relay1, LOW);
    delay (2000);
    state = 7;
  }

  if (state == 7) {
    Serial.println ("State = 7");
    digitalWrite (relay2, LOW);
    delay (stampTime * 100);
    digitalWrite (relay2, HIGH);
    delay (2000);
    state = 8;
  }
  if (state == 8) {
    Serial.println ("State = 8");
    myStepper.setSpeed(260);
    myStepper.step(foilshiftlength);
    state = 9;
  }
  if (state == 10) {
    Serial.print("state = ");
    Serial.println(state);
    val_button1 = digitalRead(button1);
    if (val_button1 == LOW) {
      state = 12;
    }
    if (val_button1 != LOW) {
      state = 11;
    }
  }
  if (state == 11) {
    Serial.print("state = ");
    Serial.println(state);
    digitalWrite (relay1, HIGH);
    delay (2000);
    state = 6;
  }

  if (state != statePrev) {
    Serial.print("state = ");
    Serial.println(state);
  }
}

void SM_LM () {
  statePrev = state;

  switch (state) {

    case 0: //reset
      statePrev = 0;
      Serial.print("state = ");
      Serial.println(state);
      break;

    case 1: //checking if hot stamp machine is loaded
      break;

    case 2:
      Serial.println ("State = 2");
      val_LED = digitalRead(LED);
      if (val_LED == LOW) {
        state = 0;
      }
      if (val_LED == HIGH) {
        state = 3;
      }
      break;
    case 3: //checking for button press
      Serial.println ("State = 3");
      val_stampButton = digitalRead(stampButton);
      if (val_stampButton == LOW) {
        Serial.println ("Button Press");
        state = 4;
      }
      break;

    case 4: //button timer
      Serial.println ("State = 4");
      t_0_stampButton = millis ();
      state = 5;
      break;

    case 5: //button debounce
      Serial.println ("State = 5");
      val_stampButton = digitalRead(stampButton);
      t_stampButton = millis();
      if (val_stampButton == HIGH) {
        state = 0;
      }
      if (t_stampButton - t_0_stampButton > bounce_delay) {
        state = 6;
      }

    case 6: //activate pusher
      break;

    case 7: //stamp
      break;

    case 8: //foil stepper motor place holder
      break;

    case 9:

      int detect = digitalRead(irSensor);
      if (detect == LOW) {
        digitalWrite(LED, LOW);
      }
      if (detect == HIGH) {
        digitalWrite (LED, HIGH);
      }
      val_LED = digitalRead(LED);
      if (val_LED == LOW) {
        digitalWrite (relay1, HIGH);
        state = 0;
      }
      if (val_LED == HIGH) {
        state = 10;
      }
      break;

    case 10:
      break;

    case 11:
      break;

    case 12: //button timer
      state = 0;
      break;

    case 13:
      t_0_button1 = millis();
      state = 14;
      break;

    case 14: //button bounce
      Serial.print("state = ");
      Serial.println(state);
      t_button1 = millis();
      if (val_button1 == HIGH) {
        state = 6;
      }
      if (t_button1 - t_0_button1 > bounce_delay) {
        digitalWrite (relay1, HIGH);
        delay (2000);
        state = 0;
      }

  }
}

With that many states, you should use an enum to assign identifiers to the states.

I'll look into this!

Also, I see a section where you use if-else with states, and another where you use switch-case with states. Why are there two? That is highly unusual.

Yes, definitely give states meaningful names such as “waitingForMoreParts” instead of a simple number. Also, create a diagram with a representation of the states and the transitions between the states. But what may help you immediately is, instead of doing this: state = 5, call a function setState( int newState ). In this function you can print the new state for debugging, record the last state, record the time of entry to the state etc. and, of course, set the new state.
The ideal structure for a state machine is a single switch statement where every state has its own case statement, although it is not always possible to achieve such a clean model.

   case 9:

      int detect = digitalRead(irSensor);
      if (detect == LOW) {
        digitalWrite(LED, LOW);
      }
      if (detect == HIGH) {
        digitalWrite (LED, HIGH);
      }
      val_LED = digitalRead(LED);
      if (val_LED == LOW) {
        digitalWrite (relay1, HIGH);
        state = 0;
      }
      if (val_LED == HIGH) {
        state = 10;
      }
      break;

    case 10:
      break;

Be careful, in case 9: that begins around line 253 you declare a variable and initialize it with digitalRead(irSensor). Enclose that entire case in braces { } , otherwise the compiler cannot handle the variable declaration properly. If you have the IDE set to show all compiler warnings you will see:

/home/pi/Arduino/forumTest/forumTest.ino: In function 'void SM_LM()':
/home/pi/Arduino/forumTest/forumTest.ino:272:10: warning: jump to case label [-fpermissive]
     case 10:
          ^~
/home/pi/Arduino/forumTest/forumTest.ino:255:11: note:   crosses initialization of 'int detect'
       int detect = digitalRead(irSensor);
           ^~~~~~
/home/pi/Arduino/forumTest/forumTest.ino:275:10: warning: jump to case label [-fpermissive]
     case 11:
          ^~
/home/pi/Arduino/forumTest/forumTest.ino:255:11: note:   crosses initialization of 'int detect'
       int detect = digitalRead(irSensor);
           ^~~~~~
/home/pi/Arduino/forumTest/forumTest.ino:278:10: warning: jump to case label [-fpermissive]
     case 12: //button timer
          ^~
/home/pi/Arduino/forumTest/forumTest.ino:255:11: note:   crosses initialization of 'int detect'
       int detect = digitalRead(irSensor);
           ^~~~~~
/home/pi/Arduino/forumTest/forumTest.ino:282:10: warning: jump to case label [-fpermissive]
     case 13:
          ^~
/home/pi/Arduino/forumTest/forumTest.ino:255:11: note:   crosses initialization of 'int detect'
       int detect = digitalRead(irSensor);
           ^~~~~~
/home/pi/Arduino/forumTest/forumTest.ino:287:10: warning: jump to case label [-fpermissive]
     case 14: //button bounce
          ^~
/home/pi/Arduino/forumTest/forumTest.ino:255:11: note:   crosses initialization of 'int detect'
       int detect = digitalRead(irSensor);
           ^~~~~~

So I fixed Case 9.

But I'm having trouble understanding enums. is this absolutely necessary? I'm not sure if this is actually my problem or would it just help me understand more? cause the previous state machine I wrote was 27 cases long and runs perfect. So I'm not entirely sure.

I'm not saying you're wrong or coming at you guys. I really appreciate your help, I just don't understand enums or how to name the states other than using numbers.

“ I just don't understand enums or how to name the states other than using numbers.”

enum Color { UNDEF, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE };

Not rocket science, UNDEF is 0, YELLOW is 3, PURPLE is 6 . . .

Helps in documentation.

Code is not filled with magic numbers.


example:

enum blinkStates {
BLINK_DIS, // blink disable
BLINK_EN, // blink enable
LED_ON, // we want the led to be on for interval
LED_OFF // we want the led to be off for interval
}


Can make things simple to add a new state in between previously coded states.

Helps others understand your code !


You don’t have to use enums if you don’t want to.

Another thing, there isn't much point in running a state machine unless all significant states remain in effect for multiple passes of loop(), so it can implement cooperative multitasking. It is also possible to jump immediately from state to state (I saw one example of it in your code), and/or spend a relative eternity (more than a few milliseconds) executing the code in a state. Those things you can do, but it's not compatible with cooperative multitasking, and it's not the usual reason why one would use a state machine in this environment.

tdurand29:
So I fixed Case 9.

But I'm having trouble understanding enums. is this absolutely necessary?

Yes, you just proved it. I have no idea what Case 9 is, and I shouldn't have to know.

Seems to me states 6, 7, 8 and 9 could all be replaced with one state.

State 6 always sets state 7 at the end, and then falls through to an if that checks for/implements state 7....
State 7 always sets state 8 at the end, and then falls through to an if that checks for/implements state 8....
State 8 always sets state 9 at the end.

So the end result of state 6 is that we execute the code for states 6, 7 and 8 entirely linearly and finish in state 9.

Second the calls for naming your states and having only ONE control structure (either “if” or more ideally “switch/case”) that implements those states.

You know, in human, what the machine is doing. Enums just lat you apply those human names to the machine states you already know.

Using numbers is great while your hot on the topic. But 2 weeks later they will be meaningless.

-jim lee