0

I have a state machine program used to monitor sensors and activate relays that has been used and adapted on multiple machines and it typically performs exactly as needed. I am attempting to adapt the program to include an additional relay and two sensors and I can't figure out why the program won't update the states for two of the sensors added. All other sensors update and the program runs as intended until the program enters the state to check that the sensors have been interrupted. The final machine will use photo interrupters but I have bread boarded the circuit with both the sensors as well as using switches to attempt to debug the system.

Everything else works flawlessly on the breadboard and the program runs great save for the part where the program is waiting for the state of the sensors to read "BOTH_RSENSORS_LOW" on line 304 of the program. I have the serial monitor running to check the status of "RsensorState" that is updated every time line 302 runs. Within the main loop, "RsensorState" should be getting updated on line 174 which is supposed to be updated on line 437 where it should be returning the state based on the sensor inputs. The serial monitor shows no change to the state while changing the input on the pins attached to it. I have tested other pins as well in case they are shorted somehow to HIGH. I have the exact same sensors working fine to return the state of sensors 1 and 2. I have gone over the code and don't know what is missing. Every other block works great and as wanted. Perhaps I have a small syntax error that I am simply not seeing. I have attempted to lay out the program in as easy a format as I can and build the steps of the logic in blocks that are easy for me to navigate.

Board: Arduino DUE

Software: Arduino IDE 1.8.5

Includes the libraries Bounce2.h and LiquidCrystal.h

The device is designed to use a system of two buttons that need to be pressed within a specified time of each other. If at any point they are released they should turn off the relays and wait til both of them are released completely before being able to proceed again. The program is set up in 10 states to perform the functions of the machine.

CLEAR_SENSORS Turns off relay2 attached to a physical stop designed to align the part so the finished part can be slid through the machine and then checks to see if sensors 1 and 2 are not blocked meaning the part has been removed so the stop can then be be put back in place. Sensors 1 and two are on either side of the stop so that the stop will not be activated and hit the part as it is getting pushed through.

CHECK_SENSORS Turns back on relays and waits until a part is put back in and the first sensor can see that a part is in place. When it sees a part it moves to the next state.

WAIT_FOR_BOTH_LOW Checks the two safety buttons to be sure they are both released and the operator has not tried to short one out or has stuck something in the switch to hold it on. This is returned to any time the switches fail the timeout parameters.

WAIT_FOR_BUTTON Waits for a button to be pressed and goes into the next step.

ONE_PUSHED_WAIT_FOR_OTHER_BUTTON looks for the other button to be pressed within the specified time frame. If it is not then the program goes back to waiting for both to be released.

FINAL_CHECK checks to make sure the part has not been removed. If it has it goes back to waiting for the part to be put back in place and for all buttons to be released.

LOWER_RAILS checks to make sure the buttons are still being held and if so lowers an aligning and holding railing attached to Relay3

CHECK_RAIL_SENSORS checks 2 sensors to be sure they are both blocked and the railing is fully down. They are to act as limit sensors. This is the step the program gets stuck on.

SAFETY_WINDOW is a small time frame the machine waits until it turns on the relays and then after that time frame turns on relay1

RELAY_ON turns off the main relay1 and the holding relay3 if the buttons are released and turns them off after the time frame for the relays to run has expired. It then goes to the beginning where it waits for the part to be removed.

When I bypass the CHECK_RAIL_SENSORS step the whole things works flawlessly. I am stumped why that particular part isn't working. From what I can tell it has to do with the RsensorState not getting updated as expected like the other sensors are. sensorState updates and functions exactly as expected so I am not sure why RsensorState does not. Am I making a syntax error I am not aware of? I have gone through it a hundred times and maybe I am just not seeing it. All the components have been tested and function as expected. Here is the code:

// This sketch reads inputs from two buttons and a sensor to turns on 2 relays
// RelayPin2 is engaged until the end of the first relay's cycle
// It requires that both buttons be pressed within a certain time of each other and held
// A relay pin is then turned on for a specified period of time
// RelayPin3 turns on and is verified by Sensors 3 and 4
// RelayPin2 is turned off after the first relay pin has completed it's cycle
// When the sensor is clear, RelayPin2 is turned on again
// Both buttons must then be released again for another cycle to begin
// A state machine is used, only to make it more clear what is going on.
// Used with an Arduino DUE on Arduino 1.8.5 IDE
// Original sketch found on https://arduino.stackexchange.com/questions/45584/state-machine-logic-problem-with-added-states-monitoring-safety-switches/
// Uses the Bounce2 library: https://github.com/thomasfredericks/Bounce2
//
// When a button is pressed, the input turns HIGH.
//
// Normally only one 'enum' is used for the state, that controls the code.
// This sketch has also a 'enum' for the buttons, which is called the input state.
// define the different states for this sketch.
// An 'enum' is like a number of #define with increasing numbers.

// include the library code:
#include <Bounce2.h>
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Logical states of the main state machine
enum
{
  CLEAR_SENSORS,
  CHECK_SENSORS,
  WAIT_FOR_BOTH_LOW,
  WAIT_FOR_BUTTON,
  ONE_PUSHED_WAIT_FOR_OTHER_BUTTON,
  FINAL_CHECK,
  LOWER_RAILS,
  CHECK_RAIL_SENSORS,
  SAFETY_WINDOW,
  RELAY_ON,
} state;       // an 'enum' is its own type.

// define the different input states.
enum
{
  BOTH_BUTTONS_LOW,      // no buttons are pressed.
  BOTH_BUTTONS_HIGH,     // both buttons are pressed.
  ONE_BUTTON_HIGH,       // probably one of the buttons is pressed.
};

// Define the different sensor states

enum
{
  BOTH_SENSORS_HIGH, // Both Sensors are not blocked
  BOTH_SENSORS_LOW, // Both Sensors are blocked
  SENSOR1_HIGH_SENSOR2_LOW,  // Sensor 1 is not blocked but Sensor 2 is
  SENSOR2_HIGH_SENSOR1_LOW,   // Sensor 2 is not blocked but Sensor 1 is
};

// Define the different rail sensor states

enum
{
  BOTH_RSENSORS_HIGH, // Both sensors are not blocked
  BOTH_RSENSORS_LOW,  // Both rail sensors are blocked
  SENSOR3_HIGH_SENSOR4_LOW,  // Sensor 3 is not blocked Sensor 4 is
  SENSOR4_HIGH_SENSOR3_LOW,  // Sensor 4 is not blocked Sensor 3 is 
};

const int ButtonPin1 = 2;
const int ButtonPin2 = 3;
const int Sensor1Pin = 6;
const int Sensor2Pin = 24;
const int Sensor3Pin = 30;
const int Sensor4Pin = 31;
const int RelayPin = 5;
const int RelayPin2 = 13;
const int RelayPin3 = 34;
const int IndicatorPin = 4;
const int IndicatorPin2 = 25;
const int IndicatorPin3 = 26;
const int IndicatorPin4 = 27;
const int IndicatorPin5 = 35;

// The 'previousMillis' is exclusively for the state machine portion only.
// It is used for different software timers in different states.
// That is okay, because within the state machine the states are seperated from each other.
// Funtions outside the state machine would require a separate timer.
unsigned long previousMillis;

const unsigned long RelayOnTime = 1750;    // Length of time for relay
const unsigned long TimeFrameForSecondButton = 300; // Length of time to allow two pushes
const unsigned long PushSafetyWindow = 300; // Safety Delay after push to be sure part is in place

// Counters for the number of failed relay triggers and successful cycles.
// They can probably be normal integers as well.
unsigned long failed = 0;
unsigned long success = 0;

// Instantiate Bounce objects
Bounce debouncer1 = Bounce();
Bounce debouncer2 = Bounce();
Bounce debouncer3 = Bounce();
Bounce debouncer4 = Bounce();
Bounce debouncer5 = Bounce();
Bounce debouncer6 = Bounce();

void setup()
{
  lcd.begin(16, 2);
  lcd.print("On");
  Serial.begin(9600);
  Serial.println("Unit Online");

// Set up first button
  pinMode(ButtonPin1, INPUT);
  debouncer1.attach(ButtonPin1);
  debouncer1.interval(15); //Debounce time

// Set up second button  
  pinMode(ButtonPin2, INPUT);
  debouncer2.attach(ButtonPin2);
  debouncer2.interval(15); //Debounce time

// Set up Sensor1
  pinMode (Sensor1Pin, INPUT);
  debouncer3.attach(Sensor1Pin);
  debouncer3.interval(5); //Debounce time

// Set up Sensor2
  pinMode (Sensor2Pin, INPUT);
  debouncer4.attach(Sensor2Pin);
  debouncer4.interval(5); //Debounce time

  // Set up Sensor3
  pinMode (Sensor3Pin, INPUT);
  debouncer5.attach(Sensor3Pin);
  debouncer5.interval(5); //Debounce time

// Set up Sensor4
  pinMode (Sensor4Pin, INPUT);
  debouncer6.attach(Sensor4Pin);
  debouncer6.interval(5); //Debounce time

  pinMode(RelayPin, OUTPUT);      // OUTPUT to Relay
  pinMode(RelayPin2, OUTPUT);     // OUTPUT to Second Relay
  pinMode(RelayPin3, OUTPUT);     // OUTPUT to Third Relay
  pinMode(IndicatorPin, OUTPUT);  // OUTPUT to Indicator LED
  pinMode(IndicatorPin2, OUTPUT); // OUTPUT to Second Indicator LED
  pinMode(IndicatorPin3, OUTPUT); // OUTPUT to Third Indicator LED
  pinMode(IndicatorPin4, OUTPUT); // OUTPUT to Fourth Indicator LED
  pinMode(IndicatorPin5, OUTPUT); // OUTPUT to Fifth Indicator LED

  Serial.println("Going to CLEAR_SENSORS state");

// Set initial state of the machine
  state = CLEAR_SENSORS;
}


void loop()
{

  unsigned long currentMillis = millis();

// Update the Bounce instances
  debouncer1.update();
  debouncer2.update();
  debouncer3.update();
  debouncer4.update();

  int inputState = getInputState();
  int sensorState = getSensorState();
  int RsensorState = getRSensorState();

// Process the data in a state machine.
// The output part is also in the state machine.
  switch(state)
  {
    case CLEAR_SENSORS:
       // Turn off relay2 and wait til everything is clear
        lcd.setCursor(0,0);
        lcd.print("Clear Machine");
        digitalWrite(RelayPin2, LOW);     // Turn OFF Relay 2
        digitalWrite(IndicatorPin2, LOW); // Turn OFF IndicatorPin 2
        digitalWrite(IndicatorPin4, HIGH); // Turn ON IndicatorPin 4
      if(sensorState == BOTH_SENSORS_HIGH)
      {
        // Prepare to go to the next state
        Serial.println("Machine Clear Going to CHECK_SENSORS state");
        state = CHECK_SENSORS;
      }
      break;
    case CHECK_SENSORS:
      // Turn on Relay 2 and then verify the sensor is engaged before proceeding
        lcd.setCursor(0,0);
        lcd.print("Load Barricade");
        digitalWrite(RelayPin2, HIGH);  // Turn on Relay 2
        digitalWrite(IndicatorPin2, HIGH); // Turn ON IndicatorPin 2
        digitalWrite(IndicatorPin3, HIGH); // Turn ON IndicatorPin 3
        digitalWrite(IndicatorPin4, LOW); // Turn OFF IndicatorPin 4
      if(sensorState == SENSOR2_HIGH_SENSOR1_LOW)
      {
        // Prepare to go to the next state
        Serial.println("Machine Clear Going to WAIT_FOR_BOTH_LOW state");
        state = WAIT_FOR_BOTH_LOW;
      }
      break;
    case WAIT_FOR_BOTH_LOW:
      // Both buttons must be released to be able to continue.
        lcd.setCursor(0,0);
        lcd.print("Wait For Release");
        digitalWrite(IndicatorPin3, LOW); // Turn OFF IndicatorPin 3
      if(inputState == BOTH_BUTTONS_LOW)
      {
        // Prepare to go to the next state.
        Serial.println("Going to WAIT_FOR_BUTTON state");
        state = WAIT_FOR_BUTTON;
      }
      break;
    case WAIT_FOR_BUTTON:
// This is the state when everything is idle and no buttons are pressed.
// Normally both buttons are released.
// Waiting for the first button to be pressed.
// Both buttons could be pressed at the same time !
        lcd.setCursor(0,0);
        lcd.print("Engage Buttons   ");
      if(inputState == ONE_BUTTON_HIGH || inputState == BOTH_BUTTONS_HIGH)
      {
// At least one button is pressed.
// Prepare to go to the next state.
// Start a timer, because there is a timeout.
        Serial.println("Going to ONE_PUSHED_WAIT_FOR_OTHER_BUTTON state");
        previousMillis = currentMillis;
        state = ONE_PUSHED_WAIT_FOR_OTHER_BUTTON;
       }
      break;
    case ONE_PUSHED_WAIT_FOR_OTHER_BUTTON:
        lcd.setCursor(0,0);
        lcd.print("Wait for Other");
      if(inputState == BOTH_BUTTONS_LOW)
      {
// The button was released. That's not what we want.
// Return to the state to wait for both off.
        Serial.println("Going to WAIT_FOR_BOTH_LOW state");
        state = WAIT_FOR_BOTH_LOW;
      }
      else if(currentMillis - previousMillis >= TimeFrameForSecondButton)
      {
// The timeout has ended.
// The second button was not pushed after the first button
// within the timeout. That's too bad.
        Serial.println("Too Long Going to WAIT_FOR_BOTH_LOW state");
        state = WAIT_FOR_BOTH_LOW;
      }
      else if(inputState == BOTH_BUTTONS_HIGH)
      {
// The timeout has not ended and is still running.
// And now both buttons are pressed.
// We can continue.
// Start the timer for the SAFETY_WINDOW state.
        Serial.println("Going to FINAL_CHECK state");
        previousMillis = currentMillis;  
        state = FINAL_CHECK;
      }
      break;
    case FINAL_CHECK:
      if(sensorState != SENSOR2_HIGH_SENSOR1_LOW)
      // Check to make sure the barricade hasn't been removed
      {
        // Prepare to go to the next state
        Serial.println("Machine Clear Going to CHECK_SENSORS state");
        state = CHECK_SENSORS;
      }
      else if(sensorState == SENSOR2_HIGH_SENSOR1_LOW)
      {
        Serial.println("Going to LOWER_RAILS state");
        state = LOWER_RAILS;
      }
      break;    
    case LOWER_RAILS:
        lcd.setCursor(0,0);
        lcd.print("Lowering Rails  ");
      if(inputState != BOTH_BUTTONS_HIGH)
      // Check to be sure safety buttons are held and if not wait for another try
      {
        Serial.println("Going to WAIT_FOR_BOTH_LOW state from LOWER_RAILS");
        state = WAIT_FOR_BOTH_LOW;
      }
      else
      // If buttons 1 and 2 are still held proceed to turn on Relay 3 and onto the sensor check
      {
        digitalWrite(RelayPin3, HIGH);  // Turn ON RelayPin3
        digitalWrite(IndicatorPin5, HIGH); // Turn ON IndicatorPin5
        Serial.println("Going to CHECK_RAIL_SENSORS state");
        state = CHECK_RAIL_SENSORS;
      }
      break;
    case CHECK_RAIL_SENSORS:
       lcd.setCursor(0,0);
       lcd.print("Checking Rails  ");
       Serial.println(RsensorState);       
       // Wait until the sensors are blocked to move into the next state
      if(RsensorState == BOTH_RSENSORS_LOW)
      {
        Serial.println("Going to SAFETY_WINDOW state");
        state = SAFETY_WINDOW;
      }
      break;
    case SAFETY_WINDOW:
        lcd.setCursor(0,0);
        lcd.print("Wait a second");
      if(inputState != BOTH_BUTTONS_HIGH)
      {
// During the safety time window, a button was released.
// Return to idle.
        Serial.println("Going to WAIT_FOR_BOTH_LOW state");
        digitalWrite(RelayPin, LOW);  // Turn OFF RelayPin
        digitalWrite(IndicatorPin, LOW); // Turn OFF IndicatorPin
        digitalWrite(RelayPin3, LOW);  // Turn OFF RelayPin3
        digitalWrite(IndicatorPin5, LOW); // Turn OFF IndicatorPin5
        state = WAIT_FOR_BOTH_LOW;        
      }
      else if(currentMillis - previousMillis >= PushSafetyWindow)
      {
// The buttons are still pressed.
// The time window has reached the end.
// Prepare to go to the next state.
        Serial.println("Going to RELAY_ON state");
        previousMillis = currentMillis;
        digitalWrite(RelayPin, HIGH);  // Turn ON RelayPin
        digitalWrite(IndicatorPin, HIGH); // Turn ON IndicatorPin      
        state = RELAY_ON;
      }
      break;
    case RELAY_ON:
        lcd.setCursor(0,0);
        lcd.print("Relay Engaged   ");
      if(inputState != BOTH_BUTTONS_HIGH)
      {
// One of the buttons was released.
// Stop the relay and return to idle.
// Although the relay was triggered, it was not succesful.
        failed++;
        Serial.print("Failed=");
        Serial.println(failed);
        Serial.println("Going to WAIT_FOR_BOTH_LOW state");
        digitalWrite( RelayPin, LOW);  // Turn Off RelayPin
        digitalWrite(IndicatorPin, LOW); // Turn Off IndicatorPin
        digitalWrite(RelayPin3, LOW);  // Turn OFF RelayPin3
        digitalWrite(IndicatorPin5, LOW); // Turn OFF IndicatorPin5
        state = WAIT_FOR_BOTH_LOW;
      }
      else if(currentMillis - previousMillis >= RelayOnTime)
      {
        // Everyting was okay.
        // The buttons are still pressed.
        // It was complete and succesful.
        // It is time to turn off the relay and return to idle.
        success++;
        Serial.print("Success=");
        Serial.println(success);
        Serial.println("Going to CLEAR_SENSORS state");
        lcd.setCursor(0,1);
        lcd.print("Cycles ");
        lcd.print(success);
        digitalWrite(RelayPin, LOW);  // Turn Off RelayPin
        digitalWrite(IndicatorPin, LOW); // Turn Off IndicatorPin
        digitalWrite(RelayPin3, LOW);  // Turn OFF RelayPin3
        digitalWrite(IndicatorPin5, LOW); // Turn OFF IndicatorPin5
        state = CLEAR_SENSORS;
      }
      break;
    default:
        Serial.println("Error, unknown state");
      break;
    }
}


// This function returns the input state.
// There is no StateChangeDetection, because a state machine is used.
  int getInputState()
{
  int returnInputState;

// Read the debounce state and create an input state according to the 'enum' values.
  int value1 = debouncer1.read();
  int value2 = debouncer2.read();

    if(value1 == LOW && value2 == LOW)
    {
      returnInputState = BOTH_BUTTONS_LOW;
    }
    else if(value1 == HIGH && value2 == HIGH)
    {
      returnInputState = BOTH_BUTTONS_HIGH;
    }
    else
    {
    // Not both on, not both off, so it must be something else.
    returnInputState = ONE_BUTTON_HIGH;
    }

  return(returnInputState);
}

 // This function is to return the state of sensors 1 and 2
  int getSensorState()
{
  int returnSensorState;

// Read the sensor states and create an input state
  int value3 = debouncer3.read();
  int value4 = debouncer4.read();

    if(value3 == HIGH && value4 == HIGH)
    {
      returnSensorState = BOTH_SENSORS_HIGH;
    }
    else if(value3 == HIGH && value4 == LOW)
    {
      returnSensorState = SENSOR1_HIGH_SENSOR2_LOW;
    }
    else if(value4 == HIGH && value3 == LOW)
    {
      returnSensorState = SENSOR2_HIGH_SENSOR1_LOW;
    }
    else
    {
      returnSensorState = BOTH_SENSORS_LOW;
    }
  return(returnSensorState);
}

 // This function is to return the state of Sensors 3 and 4
  int getRSensorState()
{
  int returnRSensorState;

// Read the sensor states and create an input state
  int value5 = debouncer5.read();
  int value6 = debouncer6.read();

  if(value5 == LOW && value6 == LOW)
    {
      returnRSensorState = BOTH_RSENSORS_LOW;
    }
    else if(value5 == HIGH && value6 == LOW)
    {
      returnRSensorState = SENSOR3_HIGH_SENSOR4_LOW;
    }
    else if(value6 == HIGH && value5 == LOW)
    {
      returnRSensorState = SENSOR4_HIGH_SENSOR3_LOW;
    }
    else
    {
      returnRSensorState = BOTH_RSENSORS_HIGH;
    }
  return(returnRSensorState);
}

The part giving me trouble is this state:

case CHECK_RAIL_SENSORS:
   lcd.setCursor(0,0);
   lcd.print("Checking Rails  ");
   Serial.println(RsensorState);       
   // Wait until the sensors are blocked to move into the next state
  if(RsensorState == BOTH_RSENSORS_LOW)
  {
    Serial.println("Going to SAFETY_WINDOW state");
    state = SAFETY_WINDOW;
  }
  break;

It may be something to do with how the sensor state is acquired. I am using Bounce2 to debounce the inputs.

 // This function is to return the state of Sensors 3 and 4
  int getRSensorState()
{

  int returnRSensorState;

// Read the sensor states and create an input state
  int value5 = debouncer5.read();
  int value6 = debouncer6.read();

  if(value5 == LOW && value6 == LOW)
    {
      returnRSensorState = BOTH_RSENSORS_LOW;
    }
    else if(value5 == HIGH && value6 == LOW)
    {
      returnRSensorState = SENSOR3_HIGH_SENSOR4_LOW;
    }
    else if(value6 == HIGH && value5 == LOW)
    {
      returnRSensorState = SENSOR4_HIGH_SENSOR3_LOW;
    }
    else
    {
      returnRSensorState = BOTH_RSENSORS_HIGH;
    }
  return(returnRSensorState);
}

Any input is greatly appreciated! Please let me know if more clarification is necessary.

Edit 07/12/18

IF you use this code don't forget to update this section for it to work! The complete, corrected code is too large to post:

// Update the Bounce instances
  debouncer1.update();
  debouncer2.update();
  debouncer3.update();
  debouncer4.update();
  debouncer5.update();
  debouncer6.update();
2

Your debouncer update section:

// Update the Bounce instances
  debouncer1.update();
  debouncer2.update();
  debouncer3.update();
  debouncer4.update();

is missing debouncer5.update(); and debouncer6.update();. It is required to call update() once per loop() to ensure the object is up to date.

  • Oh man I can't believe I missed that! Thank you I was going cross eyed trying to figure out what I missed! It works great now! – HiWay Jul 12 '18 at 20:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.