State machine not transitioning

I have written a state machine for a vessel pressure testing device. When I run it recognizes the button I have wired and changes currentState to 1 but just stays in the main loop and never enters the case 1.

#include <HX711.h>
#include <genieArduino.h>
#include <Wire.h>
#include "MeanFilterLib.h"
#include "closedState.h"
#include "pressurizeState.h"
#include "maxpressureState.h"
#include "ventingState.h"
#include <SoftwareSerial.h>
 
#define DOUT 6
#define CLK 5  // loadcell input pins
HX711 scale;
 

int pressureread1 = A0;
int pressureread2 = A1;
int pressureswitch = A2;
const int goSwitch = 10;
const int DoorSwitch = 9;
const int valve_1_Pin = 8;
const int valve_2_Pin = 7;
float inputVoltage1;  // transducer tank side
float outputPressure1;
float inputVoltage2;  // transducer vessel side
float outputPressure2;
float calibration_factor = 710;
 

enum State { closed,
       pressurize,
       maxpressure,
       venting
};
State currentState = 0;  // current state of the system
Genie genie;
 
SoftwareSerial genieSerial(2, 3);
 

MeanFilter<float> meanFilter(50);
#define RESETLINE 4
void setup() {
  Serial.begin(115200);
  genieSerial.begin(115200);
  genie.Begin(genieSerial);
 
  //genie.AttachEventHandler(myGenieEventHandler);
 

  pinMode(RESETLINE, OUTPUT);  // Set D4 on Arduino to Output (4D Arduino Adaptor V2 - Display Reset)
  digitalWrite(RESETLINE, 1);  // Reset the Display via D4
  delay(100);
  digitalWrite(RESETLINE, 0);  // unReset the Display via D4
 
  // Let the display start up after the reset (This is important)
  // Increase to 4500 or 5000 if you have sync problems as your project gets larger. Can depent on microSD init speed.
  delay(5000);
 
  // Set the brightness/Contrast of the Display - (Not needed but illustrates how)
  // Most Displays use 0-15 for Brightness Control, where 0 = Display OFF, though to 15 = Max Brightness ON.
  // Some displays are more basic, 1 (or higher) = Display ON, 0 = Display OFF.
  genie.WriteContrast(14);
 
  scale.begin(DOUT, CLK);
  scale.tare();
  long zero_factor = scale.read_average();
 

  pinMode(valve_1_Pin, OUTPUT);  // set valves and start with closed.
  pinMode(valve_2_Pin, OUTPUT);
  digitalWrite(valve_1_Pin, LOW);
  digitalWrite(valve_2_Pin, LOW);
 

  pinMode(12, INPUT_PULLUP);  // TEST BUTTON
}
 
void loop() {
  int buttonState = !digitalRead(12);
  if (!digitalRead(12)) {
    Serial.println("Button is HIGH");
  } else {
    Serial.println("Button is LOW");
  }
  //Serial.print("Button state");
  //Serial.println(buttonState);
  scale.set_scale(calibration_factor);
  float weight = scale.get_units();
 

  static unsigned long waitPeriod = millis();




  float mean1 = meanFilter.AddValue(analogRead(pressureread1));
  inputVoltage1 = mean1 * (5.0 / 1023);
  outputPressure1 = (((inputVoltage1 / 661) - .004) * (10000 / .016) - 47);  //-68
  outputPressure1 = outputPressure1 < 0 ? 0 : outputPressure1;
 
  float mean2 = meanFilter.AddValue(pressureread2);
  inputVoltage2 = mean2 * (5.0 / 1023);
  outputPressure2 = ((inputVoltage2 / 661) - .004) * (10000 / .016);
  outputPressure2 = outputPressure2 < 0 ? 0 : outputPressure2;
  genie.DoEvents();
 

  // Serial.print (outputPressure1);
  //Serial.print (" PSI");
  //Serial.println (" ");
  static unsigned long lastDisplayUpdate = 0;
  if (millis() - lastDisplayUpdate >= 50) {  // Write the values to each object on the Visi Genie display
    genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0x01, outputPressure2);
    genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0x02, outputPressure1);
    genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0x00, weight);
    lastDisplayUpdate = millis();
  }
 
  Serial.println(currentState);
  // State machine loop
  bool valve_1_Open = false;  // setting valve flags closed
 
   valve_2_Open = false;
 
  Serial.println("hey guys");
 
    switch (currentState) {
      case closed:  // Closed state
        Serial.println("closed");
        static unsigned long closedstartTime = 0;  // variable to store closed state start time
        static bool timerStarted = false;          // flag indicates timer has started
        if (!timerStarted) {
          closedstartTime = millis();  // start timer
          timerStarted = true;
        }
        unsigned long elapsedTime = ((unsigned long)(millis() - closedstartTime) / 1000);
        genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0x03, elapsedTime);  // display elapsed time
 

        if (ClosedState::checkConditions(outputPressure1, outputPressure2, goSwitch, DoorSwitch, weight) || buttonState == 1) {
          currentState = 1;
          Serial.println(currentState);
          Serial.println("transitioning to pressurize");
          timerStarted = false;  // reset closed timer
          // transition to pressurize state
        }
        valve_1_Open = false;
        valve_2_Open = false;
        delay(1000);
 
        break;
      case 1:  // Pressurize state
        Serial.println("pressurize");
        static unsigned long pressurizestartTime = 0;  // variable to store pressurized state start time
        static bool press_timerStarted = false;        // flag indicates timer has started
        if (!press_timerStarted) {
          pressurizestartTime = millis();  // start timer
          press_timerStarted = true;
        }
        elapsedTime = ((millis() - pressurizestartTime) / 1000);
        genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0x03, elapsedTime);  // display elapsed time
 
        if (PressurizeState::checkConditions(outputPressure1, outputPressure2, goSwitch, DoorSwitch, weight) || buttonState == HIGH) {
          currentState = maxpressure;
          Serial.println(currentState);  // transition to maxpressure state
          press_timerStarted = false;    // reset timer
        }
        valve_1_Open = true;
        valve_2_Open = false;
        delay(1000);
        break;
      case maxpressure:  // Maxpressure state
        Serial.println("maxpressure");
        static unsigned long maxstartTime = 0;  // variable to store pressurized state start time
        static bool max_timerStarted = false;   // flag indicates timer has started
        if (!max_timerStarted) {
          maxstartTime = millis();  // start timer
          max_timerStarted = true;
        }
        elapsedTime = ((millis() - maxstartTime) / 1000);
        genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0x03, elapsedTime);  // display elapsed time
 
        if (MaxpressureState::checkConditions(outputPressure1, outputPressure2, goSwitch, DoorSwitch, weight) || buttonState == HIGH) {
          currentState = venting;
          Serial.println(currentState);  // transition to maxpressure state
          max_timerStarted = false;      // reset timer
        }
        valve_1_Open = false;
        valve_2_Open = false;
        delay(1000);
        break;
      case venting:  // Venting state
        Serial.println("venting");
        static unsigned long ventingstartTime = 0;  // variable to store pressurized state start time
        static bool vent_timerStarted = false;      // flag indicates timer has started
        if (!vent_timerStarted) {
          ventingstartTime = millis();  // start timer
          vent_timerStarted = true;
        }
        elapsedTime = ((millis() - ventingstartTime) / 1000);
        genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0x03, elapsedTime);  // display elapsed time
 
        if (VentingState::checkConditions(outputPressure1, outputPressure2, goSwitch, DoorSwitch, weight) || buttonState == HIGH) {
          currentState = closed;
          Serial.println(currentState);  // transition to maxpressure state
          vent_timerStarted = false;     // reset timer
        }
        valve_1_Open = false;
        valve_2_Open = true;
        delay(1000);
        break;
      default:
        Serial.println(currentState);
        Serial.println("default");
        // add code for error handling here
        valve_1_Open = true;
        valve_2_Open = true;
        delay(1000);
        break;
   
  }
  // check the valve_1 flag and set the valve pin accordingly
  if (valve_1_Open) {
    digitalWrite(valve_1_Pin, HIGH);  // open the valve
  } else {
    digitalWrite(valve_1_Pin, LOW);  // close the valve
  }
 
  // check the valve_2 flag and set the valve pin accordingly
  if (valve_2_Open) {
    digitalWrite(valve_2_Pin, HIGH);  // open the valve
  } else {
    digitalWrite(valve_2_Pin, LOW);  // close the valve
  }
  delay(100);
}
 

```any help would be much appreciated.

Thanks

If this statement does not return a true value then your machine never will leave this state.

Yes, when I press the button it does pass the if statement. It prints the “transitioning to pressurizing” and it set the currentState = 1 and prints that as well. Then it just loops in the main infinitely and never enters the case 1. It does this even though it has changed the currentState to 1.

seems like you just have a sequencer that just progresses sequentially thru a set a states.

seems like there a lot of redundant code which makes it more likely to have bugs

  • capture the time once at the top of loop: unsigned long msec= millis();, rather than call millis() again
  • why not have one set of prints outside the case statement to reports the time since the last transition and has a delay to slow things down
  • assume some pressure > or < some threshold is the event that causes a transition and open/closes valves
  • don't understand the purpose of the button

you could probably simulate this using the state of a valve to incrementally change the value of a simulated pressure. simulating would be much faster and help get the logic correct before testing on a target

I like the timer change suggestion, if you believe this will help the I will definitely change. To clarify the header files for the conditions have set values required to the transition and the button was just added during programing to manually manipulate thru states and will not be needed for the machine. I need however the timer to start at the beginning of each state because I will implement error conditions relative to the time in each state at a further point as well as add a hold time in the max pressure state. Thanks for the suggestions, any take on why the program isn’t acting like a regulator state machine and entering into the next switch case. Nothing obviously wrong in my coding? I had hoped it would be something I missed and am concerned that something to do with the display updating and timing has created some unique happenstance that I’m unaware of. Thanks

I will also try simulating with the pressure instead of the button as you suggested.

what does following do?

Why do you use numbers instead of the enum names?

In C++ you can make the machine state a property of a machine class and monitor all changes in the property setter. Then use a development system with a debugger (simulator...) and set a breakpoint in the property setter. This way you'll find pretty fast the place where the state is reset to zero.

I do have enums a just changed a couple of instances to their respectively numbers when I was attempting to debug this problem, hoping to get lucky.

#ifndef PRESSURIZESTATE_H
#define PRESSURIZESTATE_H

class PressurizeState {
public:
  static bool checkConditions(float outputPressure1, float outputPressure2, int goSwitch, int DoorSwitch, float weight) {
    return (outputPressure2 >= 850) && (goSwitch == 1) && (DoorSwitch == 1) && (weight >= 1100) ;
  }
};

#endif

Like visual studio?

Does VS support breakpoints in Arduino code?

For running Arduino programs with breakpoints without an Arduino you can use an Arduino simulator (Wokwi...)

sure you don't want the OR of those conditions?

consider

        0 msec,   0 psi, state 1 Pressurize
      249 msec,  20 psi, state 1 Pressurize
      500 msec,  40 psi, state 1 Pressurize
      751 msec,  60 psi, state 1 Pressurize
     1002 msec,  80 psi, state 1 Pressurize
     1253 msec, 100 psi, state 1 Pressurize
     1503 msec, 120 psi, state 1 Pressurize
     1754 msec, 140 psi, state 1 Pressurize
     2004 msec, 160 psi, state 1 Pressurize
     2255 msec, 180 psi, state 1 Pressurize
        0 msec, 200 psi, state 2 MaxPressure
      251 msec, 200 psi, state 2 MaxPressure
      501 msec, 200 psi, state 2 MaxPressure
      752 msec, 200 psi, state 2 MaxPressure
     1003 msec, 200 psi, state 2 MaxPressure
     1254 msec, 200 psi, state 2 MaxPressure
        0 msec, 200 psi, state 3 Vent
      249 msec, 160 psi, state 3 Vent
      500 msec, 120 psi, state 3 Vent
        0 msec,  80 psi, state 0 Idle
        0 msec,  80 psi, state 1 Pressurize
      250 msec, 100 psi, state 1 Pressurize
      501 msec, 120 psi, state 1 Pressurize
      752 msec, 140 psi, state 1 Pressurize
byte valveSt [2];
int  pressureRate [2] = { 20, -40 };
int  pressure;

enum { Close, Open };

enum { Idle, Pressurize, MaxPressure, Vent };
const char *StateStr [] = { "Idle", "Pressurize", "MaxPressure", "Vent" };
int state;

const int PsiHigh = 200;
const int PsiLow  =  80;

const unsigned long MsecMaxPressure = 1500;
unsigned long msecLst;

char s [80];

// -----------------------------------------------------------------------------
int
getPressure (void)
{
    if (Open == valveSt [0])
        pressure += pressureRate [0];

    if (Open == valveSt [1])
        pressure += pressureRate [1];

#if 0
    sprintf (s, " %s: valves %d %d, %3d psi",
            __func__, valveSt [0], valveSt [1], pressure);
    Serial.println (s);
#endif

    return pressure;
}

// -----------------------------------------------------------------------------
void loop ()
{
    unsigned long msec = millis ();

    pressure = getPressure ();

    switch (state) {
    case Idle:
        valveSt [0] = Open;
        msecLst = msec;
        state++;
        break;

    case Pressurize:
        if (PsiHigh <= pressure)  {
            valveSt [0] = Close;
            msecLst = msec;
            state++;
        }
        break;

    case MaxPressure:
        if (msec - msecLst > MsecMaxPressure)  {
            valveSt [1] = Open;
            msecLst = msec;
            state++;
        }
        break;

    case Vent:
        if (PsiLow >= pressure)  {
            valveSt [1] = Close;
            msecLst = msec;
            state = Idle;
        }
        break;
    }

    sprintf (s, " %8lu msec, %3d psi, state %d %s",
            msec - msecLst, pressure, state, StateStr [state]);
    Serial.println (s);

    delay (250);
}

void
setup ()
{
    Serial.begin (9600);
}

I think it took me longer to review this and understand what was taking place than it took you to write it but I like that it is much less convoluted, however, I will still need all the various switch and loadcell conditions to be true in order to change states. I think that referring to this script will help me to improve mine for sure.

Not only that, but you have provided me with a very good example of how to simulate using pressure values.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.