One if statement overwriting another if statement

I am working on writing a code to control motor that will raise and lower a door on serving window between our church kitchen and multipurpose room. I am using a single momentary push button and two hall effect sensors for inputs. One hall effect sensor tell it when fully raised and one tell it when fully lowered. Outputs control three relay, raise, and lower, and power relay. Power relay need to be energized for raise and lower. I want to work just like a garage door opener works.

  • If button is pushed while door is fully closed, raise door and continue to fully open.
  • If button is pushed while door is fully open, lower door and continue to fully closed.
  • If button is pushed while door is raising, stop door (door will be in middle of travel.
  • If button is pushed while door is stopped midway, lower door.
  • If button is pushed while door is lowering, reverse direction and raise door.
  • Door will stop when fully raised is sensed.
  • Door will stop when fully lowered is sensed.

The way I have my current code written it all works except for when door is stopped midway and the button is pushed, it should lower door. It does initially but once "doorDirection" is written, it changes to raise. What I door understand is all my if statement for controlling door are nested inside a if statement for "Buttonstate" so if button has not change it should change to raise. Delays were added in void functions as troubleshooting device. Serial print was also an attempt at debugging. Can somebody tell what I am missing here so I can fix it?
Thanks in advance for the help.
Code is below



const int upsensPin = 4;      // UP SENSOR tells door is open
const int downsensPin = 5;    // DOWN SENSOR tells door is down
const int buttonPin = 6;      // CONTROL SWITCH
const int powerrelayPin = 9;  // green power relay energize
const int uprelayPin = 10;    // blue up relay energize
const int downrelayPin = 11;  // orange down relay energize


int doorDirection = 0;  // 0=stopped, 1=lower, 2=raise
int buttonState;
int previousButtonState;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(upsensPin, INPUT);
  pinMode(downsensPin, INPUT);
  pinMode(powerrelayPin, OUTPUT);
  pinMode(uprelayPin, OUTPUT);
  pinMode(downrelayPin, OUTPUT);
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(powerrelayPin, HIGH);
  digitalWrite(downrelayPin, HIGH);

  Serial.begin(9600);
}
void raise() {
  digitalWrite(downrelayPin, HIGH);
  digitalWrite(uprelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
  delay(500);
  doorDirection = 2;
}

void lower() {
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
  delay(500);
  doorDirection = 1;
}

void stop() {
  digitalWrite(powerrelayPin, HIGH);
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, HIGH);
  delay(500);
  doorDirection = 0;
}


void loop() {

  Serial.print("Door direction : \t");
  Serial.println(doorDirection);
  Serial.print("Button State : \t");
  Serial.println(buttonState);
  //Serial.print("Up sensor: \t");
  //Serial.println(upsens);
  //Serial.print("Down sensor: \t");
  //Serial.println(downsens);

  //-----------------------------------------------------------------------------
  // declares what buttonState is------------------------------------------------0.1
  //-----------------------------------------------------------------------------
  buttonState = digitalRead(buttonPin);
  //-----------------------------------------------------------------------------
  // if button state is different then previous button state and state is LOW---0.2
  //-----------------------------------------------------------------------------
  if (buttonState != previousButtonState) {
    if (buttonState == LOW) {
      //------------------------------------------------------------------------------
      // high limit is active and button is pushed - begin lowering------------------1
      //------------------------------------------------------------------------------
      if (digitalRead(upsensPin) == LOW) {
        lower();
      }
      //-----------------------------------------------------------------------------
      // low limit sensor is active and button is pushed - begin raising------------2
      //-----------------------------------------------------------------------------

      // should add both sensor to if statement
      if (digitalRead(downsensPin) == LOW) {
        raise();
      }
      //------------------------------------------------------------------------------
      // Door was stopped midway and button was pushed - lower-----------------------3 currently changes to raise should fully lower
      //------------------------------------------------------------------------------
      if (doorDirection == 0 && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        lower();
      }
      //------------------------------------------------------------------------------
      // Door was raising and button was pushed - stop-------------------------------4
      //------------------------------------------------------------------------------
      if (doorDirection == 2 && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        stop();
      }
      //-------------------------------------------------------------------------------
      // Door was lowering and button was pushed - raise------------------------------5 if statement cause issue with #5 if this is removed, 5 works
      //-------------------------------------------------------------------------------
      if (doorDirection == 1  && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        raise();
      }
    }
  }
  //-----------------------------------------------------------------------------
  // low limit is reached while door was lowering -  door is now fully closed---6
  //-----------------------------------------------------------------------------
  if (digitalRead(downsensPin) == LOW && doorDirection == 1) {
    stop();
  }
  //-----------------------------------------------------------------------------
  // high limit reached while door is raising  -- door is fully open------------7
  //-----------------------------------------------------------------------------
  if (digitalRead(upsensPin) == LOW && doorDirection == 2) {
    stop();
  }
  //-----------------------------------------------------------------------------
  // makes previous button state what current button state is-------------------8
  //-----------------------------------------------------------------------------
  previousButtonState = buttonState;
}


What is "written"?

You should record which direction the door was moving before the stop (doorDirection = 0) so the door goes the correct direction after the stop.

1 Like

Ask or search on State Machine for a way to do this more simply.

1 Like

This will lower the door when it stopped midway. lower() will change the direction to 1.

Two if’s below, the condition is true because the door is not at either end, and the direction is now 1:

if (doorDirection == 1  && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        raise();

Change the if construction to
if(…) {
} else if(…) {
} else if(…) {
} etc

So only one if executes even when the variables change their values and another if condition would be true.

And yes, using a machine state approach will make your program easier to understand, more maintainable

1 Like

What happens if power is lost when window is midway and the Arduino resets? Are you sure you're not making a trap for a child's fingers?

1 Like

Note that if the door could have a single Hall switch positioned so that a magnet at the bottom of the door gets detected when the door is up and a magnet at the top of the door gets detected when the door is down. The detector would be in the middle of the door travel. But you could have one magnet in the middle and two sensors.. each half a door height above and below center.

I would go further using no-delay code.

in void loop()

  • one function that watches the button and updates a global button state variable every time loop() runs.
  • one function for each Hall switch that updates a variable for each Hall switch every time that loop() runs.
  • one function that watches the button state and reads the door state state when the button is pressed to change the door state between stop, go up, go down.
  • one function for the door, that has a stop state, a go up state that changes the door state to stop when the sensor state reaches the limit and a go down state that changes door state to stop when the sensor reaches that limit.
    end of loop(), loop runs again.

That arrangement breaks the if()'s up and reduces the complexity away from one big knot.
The button always gets seen and when pressed always acts to stop or direct the door up or down.
The limit switch(es) always get seen and the door control uses that to stop the door.---- which would need 2 more states, one for stopped at the top and one for stopped at the bottom so the button press knows what direction to start up/down movement.

That control could be arranged differently.
Breaking the whole into functions allows for the Inputs of button and Hall are always being read then for Input changes to always be acted upon instead of only reading inputs when a complex set of if() conditions lets them be read which can be hard to debug.

There is a minimum complexity no matter how it's coded. Without delays it will run smoother.

1 Like

Right in setup(), check the limits and if in between, raise the door?

1 Like

I placed the code in the wokwi simulator.

@mancera1979's point in #4 is well taken. I made some of the if statements into if/else.

I added a temporary hack to let the activity of the door proceed uncritically until the limit switch is left at either the top or bottom. In the real sketch later, this should be part of the machinery that watches the door going up, reaching limits and/or being told to do otherwise by button, written to not block.

      if (digitalRead(upsensPin) == LOW) {
        lower();
        while (digitalRead(upsensPin) == LOW);  // wait until door gets off switch
      }

There are a few other things, like I de-spammed the loop status reporting and moved all digitalWrite()s off to the side. This was because I miswired the LEDs and could not figre out why it was not powering the system, but also asking the door to go up and down at the same time...

The sketch is limping along. My time is up - she who must not be kept waiting has texted and we are off to the beach... but this door thing would almost keep me indoors this day, if it were not perfectly good for wasting elsewhere. You (all) should look forward to being retired. :expressionless:

Take the link and try it:

Wokwi_badge UA if tangled doors

// https://wokwi.com/projects/400042875593055233
// https://forum.arduino.cc/t/one-if-statement-overwriting-another-if-statement/1269008

const int upsensPin = 4;      // UP SENSOR tells door is open
const int downsensPin = 5;    // DOWN SENSOR tells door is down

const int buttonPin = 6;      // CONTROL SWITCH

const int powerrelayPin = 9;  // green power relay energize
const int uprelayPin = 10;    // blue up relay energize
const int downrelayPin = 11;  // orange down relay energize

// not used yet.
# define ENERGIZED = HIGH;    // one stop shop for changining relay sense
# define OFF = LOW;    // one stop shop for changining relay sense

int doorDirection = 0;  // 0=stopped, 1=lower, 2=raise

enum {STOPPED, LOWER, RAISE};
char *tags[] = {"stopped", "lowering", "raising"};

int buttonState;
int previousButtonState;

void setup() {
  Serial.begin(9600); 
  
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(upsensPin, INPUT_PULLUP);  //... because switches
  pinMode(downsensPin, INPUT_PULLUP);  //... because switches

  pinMode(powerrelayPin, OUTPUT);
  pinMode(uprelayPin, OUTPUT);
  pinMode(downrelayPin, OUTPUT);

  allStop();
}

void raise() {
  doorGoUp();
  Serial.println("                please raise.");
  doorDirection = RAISE;
}

void lower() {
  doorGoDown();
  Serial.println("                please          lower.");
  doorDirection = LOWER;
}

void stop() {
  allStop();
  Serial.println("                please                      all stop.");
  doorDirection = STOPPED;
}

void doorGoUp() 
{
  Serial.println("turn on and go UP");
  digitalWrite(downrelayPin, HIGH);
  digitalWrite(uprelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
}

void doorGoDown()
{
  Serial.println("turn on and go DOWN");
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
}

void allStop()
{
  Serial.println("turn off");
  digitalWrite(powerrelayPin, HIGH);
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, HIGH);
}

void loop() {

  static int lastPrintedDD = 999;
  static int lastPrintedBS = 999;

  if (lastPrintedDD != doorDirection) {
    Serial.print("door Direction now : ");
    Serial.println(tags[doorDirection]);

    lastPrintedDD = doorDirection;
  }

  if (lastPrintedBS != buttonState) {
    Serial.print("button State now : \t");
    Serial.println(buttonState);

    lastPrintedBS = buttonState;
  }

  //Serial.print("Up sensor: \t");
  //Serial.println(upsens);
  //Serial.print("Down sensor: \t");
  //Serial.println(downsens);

  //-----------------------------------------------------------------------------
  // declares what buttonState is------------------------------------------------0.1
  //-----------------------------------------------------------------------------
  buttonState = digitalRead(buttonPin);
  //-----------------------------------------------------------------------------
  // if button state is different then previous button state and state is LOW---0.2
  //-----------------------------------------------------------------------------
  if (buttonState != previousButtonState) {
    if (buttonState == LOW) {
      //------------------------------------------------------------------------------
      // high limit is active and button is pushed - begin lowering------------------1
      //------------------------------------------------------------------------------
      if (digitalRead(upsensPin) == LOW) {
        lower();
        while (digitalRead(upsensPin) == LOW);  // wait until door gets off switch
      }
      //-----------------------------------------------------------------------------
      // low limit sensor is active and button is pushed - begin raising------------2
      //-----------------------------------------------------------------------------

      // should add both sensor to if statement
      if (digitalRead(downsensPin) == LOW) {
        raise();
        while (digitalRead(downsensPin) == LOW);  // wait until door gets off switch
      }
      //------------------------------------------------------------------------------
      // Door was stopped midway and button was pushed - lower-----------------------3 currently changes to raise should fully lower
      //------------------------------------------------------------------------------
      if (doorDirection == STOPPED && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        lower();
      }
      //------------------------------------------------------------------------------
      // Door was raising and button was pushed - stop-------------------------------4
      //------------------------------------------------------------------------------
else      if (doorDirection == RAISE && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        stop();
      }
      //-------------------------------------------------------------------------------
      // Door was lowering and button was pushed - raise------------------------------5 if statement cause issue with #5 if this is removed, 5 works
      //-------------------------------------------------------------------------------
else      if (doorDirection == LOWER  && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        raise();
      }
    }
  }
  //-----------------------------------------------------------------------------
  // low limit is reached while door was lowering -  door is now fully closed---6
  //-----------------------------------------------------------------------------
  if (digitalRead(downsensPin) == LOW && doorDirection == LOWER) {
    stop();
  }
  //-----------------------------------------------------------------------------
  // high limit reached while door is raising  -- door is fully open------------7
  //-----------------------------------------------------------------------------
  if (digitalRead(upsensPin) == LOW && doorDirection == RAISE) {
    stop();
  }
  //-----------------------------------------------------------------------------
  // makes previous button state what current button state is-------------------8
  //-----------------------------------------------------------------------------
  previousButtonState = buttonState;
}

HTH

a7

1 Like

I added the "else if" and that corrected my issue, thanks for the help!

Thank you all for chiming in with the help. Works like it should now using "if else". I will look into the machine state. Now on to fabrication part of the build. Thanks again

Better late than never.

This simulation has a little door with limit switches hooked into the inputs and outputs of @44jdb's code w/ modifications, in particular more if/else than I originally thought, and that solved getting off the limit switches without blocking.

The white wires carry the data. Only because the original is totally not blocking can we stand up beside it a process to run "concurrently", here a simple representation of the door in motion and the faked limit switch signals.

The software connection between the two halves is as thin as the hardware.

I did not check if it works exactly like the full specifications but it should be close. Alternate implementations of the functionality could be swapped in for the OP's work.

Try it here

Wokwi_badge UA Real Fake Door


// https://wokwi.com/projects/400042875593055233
// now https://wokwi.com/projects/400052399351512065 with doorDoor
// https://forum.arduino.cc/t/one-if-statement-overwriting-another-if-statement/1269008

const int upsensPin = 4;      // UP SENSOR tells door is open
const int downsensPin = 5;    // DOWN SENSOR tells door is down

const int buttonPin = 6;      // CONTROL SWITCH

const int powerrelayPin = 9;  // green power relay energize
const int uprelayPin = 10;    // blue up relay energize
const int downrelayPin = 11;  // orange down relay energize

// not used yet.
# define ENERGIZED = HIGH;    // one stop shop for changining relay sense
# define OFF = LOW;    // one stop shop for changining relay sense

int doorDirection = 0;  // 0=stopped, 1=lower, 2=raise

enum {STOPPED, LOWER, RAISE};
char *tags[] = {"stopped", "lowering", "raising"};

int buttonState;
int previousButtonState;

void setup() {
  Serial.begin(9600);
  Serial.println("\nif tangle world!\n");
  
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(upsensPin, INPUT_PULLUP);  //... because switches
  pinMode(downsensPin, INPUT_PULLUP);  //... because switches

  buttonState = digitalRead(buttonPin); // read so we don't get spurious activity

  pinMode(powerrelayPin, OUTPUT);
  pinMode(uprelayPin, OUTPUT);
  pinMode(downrelayPin, OUTPUT);

  allStop();

  setupSimulation();
}

void raise() {
  doorGoUp();
  Serial.println("                please raise.");
  doorDirection = RAISE;
}

void lower() {
  doorGoDown();
  Serial.println("                please          lower.");
  doorDirection = LOWER;
}

void stop() {
  allStop();
  Serial.println("                please                      all stop.");
  doorDirection = STOPPED;
}

void doorGoUp() 
{
  Serial.println("turn on and go UP");
  digitalWrite(downrelayPin, HIGH);
  digitalWrite(uprelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
}

void doorGoDown()
{
  Serial.println("turn on and go DOWN");
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
}

void allStop()
{
  Serial.println("turn off");
  digitalWrite(powerrelayPin, HIGH);
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, HIGH);
}

unsigned long now;    // millis for the entire not-blocked loop

void loop() {
//...
  now = millis();
  advanceSimulation();

  report();

  buttonState = digitalRead(buttonPin);

  if (buttonState != previousButtonState) {
    previousButtonState = buttonState;

    if (buttonState == LOW) {
      if (digitalRead(upsensPin) == LOW) {
        lower();
//        while (digitalRead(upsensPin) == LOW);  // wait until door gets off switch may no longer be necessary
      }
      else if (digitalRead(downsensPin) == LOW) {
        raise();
//        while (digitalRead(downsensPin) == LOW);  // wait until door gets off switch may no longer be necessary
      }
      else if (doorDirection == STOPPED && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        lower();
      }
      else if (doorDirection == RAISE && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        stop();
      }
      else if (doorDirection == LOWER  && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        raise();
      }
    }
  }

  if (digitalRead(downsensPin) == LOW && doorDirection == LOWER) {
    stop();
  }

  if (digitalRead(upsensPin) == LOW && doorDirection == RAISE) {
    stop();
  }

  delay(20);  // poor man's debounce - so sue me.
}

// inquiring minds want to know

void report() {

  return;  // we don't need no stinkin' report

  static int lastPrintedDD = 999;
  static int lastPrintedBS = 999;

  if (lastPrintedDD != doorDirection) {
    Serial.print("door Direction now : ");
    Serial.println(tags[doorDirection]);

    lastPrintedDD = doorDirection;
  }

  if (lastPrintedBS != buttonState) {
    Serial.print("button State now : \t");
    Serial.println(buttonState);

    lastPrintedBS = buttonState;
  }

//  Serial.print("Up sensor: \t");
//  Serial.println(upsens);
//  Serial.print("Down sensor: \t");
//  Serial.println(downsens);
}


// below this line are functions to produce a simulation of the physical process
// it places the sensor output on the processOutput pin, which
// can be fed directly to the processInput pin for measurement and monitoring

// initiate the process with the goose button
// control the speed for testing with the slide fader at processSpeedControl

# include <Adafruit_NeoPixel.h>
# define PIN 7          // the pin
# define NPIXELS 16     // number of LEDs on strip
# define UPA    A0      // make it go up manually
# define DOWNA  A3      // make it go down manually

# define upLED    A2  // on (LOW) when door is at extreme
# define downLED  A1  // on (LOW) when door is at other extreme

# define PRESST LOW
Adafruit_NeoPixel doorStrip(NPIXELS, PIN, NEO_GRB + NEO_KHZ800);

static int doorPostition;    // pixel number for door traveler

void setupSimulation()
{
  doorStrip.begin();
  doorStrip.fill(0x606060, 0, NPIXELS);
  doorStrip.setPixelColor(1, 0xff00ff);
  doorStrip.show();
  delay(500);
  doorStrip.fill(0x606060, 0, NPIXELS);
  doorStrip.show();
  delay(500);

  pinMode(UPA, INPUT_PULLUP);
  pinMode(DOWNA, INPUT_PULLUP);

  pinMode(upLED, OUTPUT);
  pinMode(downLED, OUTPUT);

  doorPostition = 0;
  doorStrip.setPixelColor(doorPostition, 0xff0000);
  doorStrip.show();    
}

// if the up motor is powered, move the door up
// if the down motor is powered, move the door down

# define SPEED 200  // milliseconds per step

# define ON   LOW
# define OFF  HIGH

void advanceSimulation()
{
  static unsigned long timer;

  if (timer && (now - timer < SPEED)) return;
  timer = now;

// later the real outputs will hook into the buttons for automatic control
  bool goUp = digitalRead(UPA) == PRESST;
  bool goDown = digitalRead(DOWNA) == PRESST;

// plausible request?
  if (goUp && goDown) {
    Serial.println("up and down at once impossible.");
    goUp= false;
    goDown = false;
  }

// do it one step closer
  if (goUp) {
    doorStrip.setPixelColor(doorPostition, 0x606060);
    doorPostition++;
    if (doorPostition >= NPIXELS) {
      doorPostition = NPIXELS - 1;  // door fully opened
    }
    doorStrip.setPixelColor(doorPostition, 0xff0000);    
  }

  if (goDown) {
    doorStrip.setPixelColor(doorPostition, 0x606060);
    doorPostition--;
    if (doorPostition < 0) {
      doorPostition = 0;  // door fully closed
    }
    doorStrip.setPixelColor(doorPostition, 0xff0000);    
  }

// update limit switches output
  if (doorPostition == 0) {
    digitalWrite(downLED, ON);
    digitalWrite(upLED, OFF);
  }
  else if (doorPostition == NPIXELS - 1) {
    digitalWrite(upLED, ON);
    digitalWrite(downLED, OFF);
  }
  else {
    digitalWrite(upLED, OFF);
    digitalWrite(downLED, OFF);
  }

  doorStrip.show();
}

Passes for fun in my empty life.

a7

State Machine, short for Finite State Machine where State is short for Process State. The code that gets run depends on what is hapenning in the process... what step, kept in a variable that gets changed as "the machine" runs.

It can be done with if-else statements but usually a switch-case statement is used, run inside of a loop. That code-machine is run over and over, every time the State part only executes and over time the cases/steps/states proceed along.. like a machine.

Switch-case is like a faster if-elseif-elseif-etc-else ladder.

FSM or finite state machine or just state machine. Lotsa stuff all over on that.

A useful language feature is the switch/case as @GoForSmoke points out.

Using the enum feature can allow you to give nice names to numbers, essentially, so you don't have to carry around that 2 means see I forget already, was that moving up?

And last, but shoukd be first, is mention of the IPO model. See it in the abstract here

IPO Model

and thank @paulpaulson for relentlessly promoting it here on these fora.

It would mean your loop had three sections:

  • I input - get all switches and buttons good and read.

  • P process - based on the inputs and what the door is doing now, decide what the door should do next

  • O output - turn on or off any motors and run any LEDs and stuff

The process section is where a state machine might serve very well.

Here's a tutorial by @J-M-L that will not be a waste of time.

It's yet another, as I said there are a crap-ton of explainers on this, read a few and it will start to see sense.

Look forward to it, the combination of these ideas is a game changer.

a7

I had to be told that i wrote a state machine to solve a problem at work. It was if-else code. And state can be a constructed value of bits as true/false conditions depending on how you write, can be a collection of pin states and work just fine... be fast as H when those pins are together on the same port.
Switch-case is not required in a state machine, state is!

Waiting for she who will not be kept waiting... I replaced the loop() with one that uses a Finite State Machine.

state is most of the, um, state, but I keep one variable for the condition of having been stopped in the middle, so it can know which direction to go when goosed. So STOPPED could be two states if we were being purists.

Try it here:

Wokwi_badge UA FSM Real Fake Door

// https://wokwi.com/projects/400042875593055233
// now https://wokwi.com/projects/400052399351512065 with doorDoor
// and now https://wokwi.com/projects/400141987251449857 with FSM loop

// https://forum.arduino.cc/t/one-if-statement-overwriting-another-if-statement/1269008

// now replacing the loop

const int upsensPin = 4;      // UP SENSOR tells door is open
const int downsensPin = 5;    // DOWN SENSOR tells door is down

const int buttonPin = 6;      // CONTROL SWITCH

const int powerrelayPin = 9;  // green power relay energize
const int uprelayPin = 10;    // blue up relay energize
const int downrelayPin = 11;  // orange down relay energize

// not used yet.
# define ENERGIZED = HIGH;    // one stop shop for changining relay sense
# define OFF = LOW;    // one stop shop for changining relay sense

int buttonState;
int previousButtonState;

enum State {UP = 0, DOWN, RISES, FALLS, STOPPED, NONE} state = DOWN;
char *sTags[] =  {"UP", "DOWN", "RISES", "FALLS", "STOPPED", "NONE"};

// motoring values moving up or down or not at all
enum Direction {mDOWN, mUP, mNONE} doorDirection;
char *dTags[] =  {"moving down", "moving up", "NONE"};

void setup() {
  Serial.begin(9600);
  Serial.println("\nFSM door control world!\n");
  
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(upsensPin, INPUT_PULLUP);  //... because switches
  pinMode(downsensPin, INPUT_PULLUP);  //... because switches

  previousButtonState = digitalRead(buttonPin) == LOW; // read so we don't get spurious activity

  pinMode(powerrelayPin, OUTPUT);
  pinMode(uprelayPin, OUTPUT);
  pinMode(downrelayPin, OUTPUT);

  allStop();

// here's where the limit swtiches coulr be checked to acquire the current door position
// and set the appropriate state (and direction if in between)

  setupSimulation();
}

unsigned long now;    // millis for the entire not-blocked loop

void loop() {
//... housekeeping
  now = millis();
  advanceSimulation();

  static State lastState = NONE;
  if (lastState != state) {
    lastState = state;
    Serial.print("state is "); Serial.println(sTags[state]);
  }

// INPUT  
  bool doorAtBottom = digitalRead(downsensPin) == LOW;
  bool doorAtTop = digitalRead(upsensPin) == LOW;
  
  if (doorAtBottom && doorAtTop) Serial.println("IMPOSSIBLE ERROR!");

  bool buttonPress = false;
  buttonState = digitalRead(buttonPin) == LOW;  // true pressed
  if (buttonState != previousButtonState) {
    previousButtonState = buttonState;
    if (buttonState) {
      buttonPress = buttonState;
      Serial.println("                             button got pressed!");
    }
  }
  
  bool motorU = false;
  bool motorD = false;
  
  switch (state) {
  case UP :
    if (buttonPress) state = FALLS;
    break;
  
  case DOWN :
    if (buttonPress) state = RISES;
    break;
  
  case RISES :
    doorDirection = mUP;
    if (doorAtTop) state = UP;
    else motorU = true;

    if (buttonPress) state = STOPPED;
    break;
  
  case FALLS :
    doorDirection = mDOWN;
    if (doorAtBottom) state = DOWN;
    else motorD = true;

    if (buttonPress) state = STOPPED;
    break;

  case STOPPED :
  	if (buttonPress) {
  	  if (doorDirection == mUP) state = FALLS;
  	  if (doorDirection == mDOWN) state = RISES;
  	}
    break;

  case NONE :
    Serial.println("Also not possible!");
    break;
  }

// OUTPUT
  if (motorU) {
    doorGoUp();
  }
  else if (motorD) {
    doorGoDown();
  }
  else {
    allStop();
  }
  
  delay(20);  // poor man's debounce, who cares?
}

// talk to the door motors, yeah?
void doorGoUp() 
{
//  Serial.println("turn on and go UP");
  digitalWrite(downrelayPin, HIGH);
  digitalWrite(uprelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
}

void doorGoDown()
{
//  Serial.println("turn on and go DOWN");
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
}

void allStop()
{
//  Serial.println("turn off");
  digitalWrite(powerrelayPin, HIGH);
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, HIGH);
}

// below this line are functions to produce a simulation of the physical process
// it places the sensor output on the processOutput pin, which
// can be fed directly to the processInput pin for measurement and monitoring

// initiate the process with the goose button
// control the speed for testing with the slide fader at processSpeedControl

# include <Adafruit_NeoPixel.h>
# define PIN 7          // the pin
# define NPIXELS 16     // number of LEDs on strip
# define UPA    A0      // make it go up manually
# define DOWNA  A3      // make it go down manually

# define upLED    A2  // on (LOW) when door is at extreme
# define downLED  A1  // on (LOW) when door is at other extreme

# define PRESST LOW
Adafruit_NeoPixel doorStrip(NPIXELS, PIN, NEO_GRB + NEO_KHZ800);

static int doorPostition;    // pixel number for door traveler

void setupSimulation()
{
  doorStrip.begin();
  doorStrip.fill(0x606060, 0, NPIXELS);
  doorStrip.setPixelColor(1, 0xff00ff);
  doorStrip.show();
  delay(500);
  doorStrip.fill(0x606060, 0, NPIXELS);
  doorStrip.show();
  delay(500);

  pinMode(UPA, INPUT_PULLUP);
  pinMode(DOWNA, INPUT_PULLUP);

  pinMode(upLED, OUTPUT);
  pinMode(downLED, OUTPUT);

  doorPostition = 0;
  doorStrip.setPixelColor(doorPostition, 0xff0000);
  doorStrip.show();    
}

// if the up motor is powered, move the door up
// if the down motor is powered, move the door down

# define SPEED 200  // milliseconds per step

# define ON   LOW
# define OFF  HIGH

void advanceSimulation()
{
  static unsigned long timer;

  if (timer && (now - timer < SPEED)) return;
  timer = now;

// later the real outputs will hook into the buttons for automatic control
  bool goUp = digitalRead(UPA) == PRESST;
  bool goDown = digitalRead(DOWNA) == PRESST;

// plausible request?
  if (goUp && goDown) {
    Serial.println("up and down at once impossible.");
    goUp= false;
    goDown = false;
  }

// do it one step closer
  if (goUp) {
    doorStrip.setPixelColor(doorPostition, 0x606060);
    doorPostition++;
    if (doorPostition >= NPIXELS) {
      doorPostition = NPIXELS - 1;  // door fully opened
    }
    doorStrip.setPixelColor(doorPostition, 0xff0000);    
  }

  if (goDown) {
    doorStrip.setPixelColor(doorPostition, 0x606060);
    doorPostition--;
    if (doorPostition < 0) {
      doorPostition = 0;  // door fully closed
    }
    doorStrip.setPixelColor(doorPostition, 0xff0000);    
  }

// update limit switches output
  if (doorPostition == 0) {
    digitalWrite(downLED, ON);
    digitalWrite(upLED, OFF);
  }
  else if (doorPostition == NPIXELS - 1) {
    digitalWrite(upLED, ON);
    digitalWrite(downLED, OFF);
  }
  else {
    digitalWrite(upLED, OFF);
    digitalWrite(downLED, OFF);
  }

  doorStrip.show();
}

a7

No demo I made took this asymmetry into account. Finally looked...

If the button is pushed when the door is going down, it reverses. But does this mean that another button push should stop that now upward travel, and then the next button push would make the door start going down?

What does the solution sketch do in this situation? Is it what is desired or did it just happen out that way?

The state machine is simpler when it is more complicated. Here's with only four states and no other variables that are part of fully defining the state:

  switch (state) {
  case UP :
    if (buttonPress) state = MOVING_DOWN;
    break;
  
  case MOVING_DOWN :
    if (doorAtBottom) state = DOWN;
    if (buttonPress) state = MOVING_UP;
    break;
  
  case DOWN :
    if (buttonPress) state = MOVING_UP;
    break;
  
  case MOVING_UP :
    if (doorAtTop) state = UP;
    if (buttonPress) state = UP;
    break;
  }

a7

@alto777 and @GoForSmoke Thanks for all the additional info and help. You guys are above my level. This was my first attempt at writing code.

@alto777 Yes, by design Asymmetry allows a circular operation when door is between high and low limits.

  • Going up, button pressed, stop

  • Stopped midway, button pressed, go down

  • Going down, button pressed, go up

  • Going up, button pressed, stop

  • etc...

  • If at lower limit, obviously go up

  • If at upper limit, obviously go down

  • Reach upper limit, stop

  • Reach lower limit, stop

Below is my corrected original code.
I may switch out to State Machine code written/updated by @alto777

const int upsensPin = 4;      // UP SENSOR tells door is open
const int downsensPin = 5;    // DOWN SENSOR tells door is down
const int buttonPin = 6;      // CONTROL SWITCH
const int powerrelayPin = 9;  // green power relay energize
const int uprelayPin = 10;    // blue up relay energize
const int downrelayPin = 11;  // orange down relay energize


int doorDirection = 0;  // 0=stopped, 1=lower, 2=raise
int buttonState;
int previousButtonState;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(upsensPin, INPUT);
  pinMode(downsensPin, INPUT);
  pinMode(powerrelayPin, OUTPUT);
  pinMode(uprelayPin, OUTPUT);
  pinMode(downrelayPin, OUTPUT);
  stop ();

}
void raise() {
  digitalWrite(downrelayPin, HIGH);
  digitalWrite(uprelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
  doorDirection = 2;
}

void lower() {
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, LOW);
  digitalWrite(powerrelayPin, LOW);
  doorDirection = 1;
}

void stop() {
  digitalWrite(powerrelayPin, HIGH);
  digitalWrite(uprelayPin, HIGH);
  digitalWrite(downrelayPin, HIGH);
  doorDirection = 0;
}


void loop() {

  //-----------------------------------------------------------------------------
  // declares what buttonState is------------------------------------------------0.1
  //-----------------------------------------------------------------------------
  buttonState = digitalRead(buttonPin);
  //-----------------------------------------------------------------------------
  // if button state is different then previous button state and state is LOW---0.2
  //-----------------------------------------------------------------------------
  if (buttonState != previousButtonState) {
    if (buttonState == LOW) {
      //------------------------------------------------------------------------------
      // high limit is active and button is pushed - begin lowering------------------1
      //------------------------------------------------------------------------------
      if (digitalRead(upsensPin) == LOW) {
        lower();
      }
      //-----------------------------------------------------------------------------
      // low limit sensor is active and button is pushed - begin raising------------2
      //-----------------------------------------------------------------------------
      else if (digitalRead(downsensPin) == LOW) {
        raise();
      }
      //------------------------------------------------------------------------------
      // Door was stopped midway and button was pushed - lower-----------------------3 
      //------------------------------------------------------------------------------
      else if (doorDirection == 0 && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        lower();
      }
      //------------------------------------------------------------------------------
      // Door was raising and button was pushed - stop-------------------------------4
      //------------------------------------------------------------------------------
       else if (doorDirection == 2 && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        stop();
      }
      //-------------------------------------------------------------------------------
      // Door was lowering and button was pushed - raise------------------------------5 
      //-------------------------------------------------------------------------------
      else if (doorDirection == 1  && digitalRead(downsensPin) == HIGH && digitalRead(upsensPin) == HIGH) {
        raise();
      }
    }
  }
  //-----------------------------------------------------------------------------
  // low limit is reached while door was lowering -  door is now fully closed---6
  //-----------------------------------------------------------------------------
  if (digitalRead(downsensPin) == LOW && doorDirection == 1) {
    stop();
  }
  //-----------------------------------------------------------------------------
  // high limit reached while door is raising  -- door is fully open------------7
  //-----------------------------------------------------------------------------
  if (digitalRead(upsensPin) == LOW && doorDirection == 2) {
    stop();
  }
  //-----------------------------------------------------------------------------
  // makes previous button state what current button state is-------------------8
  //-----------------------------------------------------------------------------
  previousButtonState = buttonState;
}

Thanks again,

44jdb

S'lotta fun, so thanks for asking for help.

I put the last FSM version as primary in the simulation, and moved to door simulator into *.h/*.cpp tabs where it may ever serve again one day.

As far as I can tell, the sketch controls the process according to the specifications.

I would probably stay with the code you wrote until/unless you want to look closer into using the FSM, which you are already doing without knowing it - in your code it is just a bit more complicated to see and say what all the state consists in.

Here and where I am leaving it. As much fun as it is to play with, it is no day at the beach. I cannot find words to describe the weather - we have the azure sky of deepest summer, no wind and just too hot enough...


Wokwi_badge UA FSM Real Fake Door


a7

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