switch/case vs if/else

With the help of many on this forum I completed my first arduino project. The concept of state machine kept coming up over and over during my newbie questions and the final project ended up with a pseudo state machine using if/else commands. PaulMurrayCbr (thanks again Paul) was awesome enough to write an entire program with switch/case to perform the same functions.

I had to print out his code and study it for quite some time for it to make sense to me, it finally does. One part I can't figure out though is the following line:

digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW);

What this seems to me is that it will write the deployPin equal to buttonDownPin only while buttonDownPin is LOW. Is my thinking correct? The problem is, when I load the switch/case code and activate buttonDownPin the program just runs all the way through state to state and I have to unplug the arduino to get it to stop. This part isn't making sense to me, I thought if you let go of the button the program should stop. This is how I have my if/else program set up.

I'll post the two complete programs below. Is there any specific reason for using one versus the other? Maybe in a longer program where memory is an issue the switch/case would save room or speed things up?

PaulMurrayCbr's switch/case code

/**
  Down()
  Van door fully opens (openPin) and hits limitOpenPin
  Lift fully unfolds (deployPin) and hits limitDeploy
  Lift lowers (downPin) until limitFloor and stops
  Load wheelchair from van
  Press buttonDownPin again to lower lift to ground - limitDown

  Up()
  Load wheelchair from ground
  upPin until limitFloor level and then stop
  Roll wheelchair into van
  upPin until limitDeploy
  foldPin until limitStowPin
  van door closePin until upPin released
*/

enum State {
  CLOSED,
  OPENING,
  UNFOLDING,
  DOWN_TO_FLOOR,
  FLOOR,
  DOWN_TO_GROUND,
  GROUND,
  UP_TO_FLOOR,
  // FLOOR
  UP_TO_STOW,
  FOLDING,
  CLOSING
} state = CLOSED;

const byte buttonDownPin = 14;
const byte buttonUpPin = 15;
const byte limitOpenPin = 2;
const byte limitDeployPin = 3;
const byte limitFloorPin = 4;
const byte limitDownPin = 5;
const byte limitStowPin = 6;
const byte limitFoldPin = 7;
const byte OpenPin = 8;
const byte ClosePin = 9;
const byte deployPin = 10;
const byte stowPin = 11;
const byte downPin = 12;
const byte upPin = 13;

void setup() {
  pinMode(buttonDownPin, INPUT_PULLUP);
  pinMode(buttonUpPin, INPUT_PULLUP);
  pinMode(limitOpenPin, INPUT_PULLUP);
  pinMode(limitDeployPin, INPUT_PULLUP);
  pinMode(limitFloorPin, INPUT_PULLUP);
  pinMode(limitDownPin, INPUT_PULLUP);
  pinMode(limitStowPin, INPUT_PULLUP);
  pinMode(limitFoldPin, INPUT_PULLUP);

  pinMode(OpenPin, OUTPUT);
  pinMode(ClosePin, OUTPUT);
  pinMode(deployPin, OUTPUT);
  pinMode(stowPin, OUTPUT);
  pinMode(downPin, OUTPUT);
  pinMode(upPin, OUTPUT);
}

void loop() {
  switch (state) {
    case CLOSED:    state = state_CLOSED(); break;
    case OPENING:   state = state_OPENING(); break;
    case UNFOLDING: state = state_UNFOLDING(); break;
    case DOWN_TO_FLOOR: state = state_DOWN_TO_FLOOR(); break;
    case FLOOR:     state = state_FLOOR(); break;
    case DOWN_TO_GROUND: state = state_DOWN_TO_GROUND(); break;
    case GROUND:    state = state_GROUND(); break;
    case UP_TO_FLOOR: state = state_UP_TO_FLOOR(); break;
    case UP_TO_STOW: state = state_UP_TO_STOW(); break;
    case FOLDING:   state = state_FOLDING(); break;
    case CLOSING:   state = state_CLOSING(); break;
    
  }
}

State state_CLOSED() {
  if(digitalRead(buttonDownPin)==LOW) {
    digitalWrite(OpenPin, HIGH);
    return OPENING;
  }

  return CLOSED;
}

State state_OPENING() {
  if(digitalRead(limitOpenPin)==LOW) {
    digitalWrite(OpenPin, LOW);
    digitalWrite(deployPin, HIGH);
    return UNFOLDING;
  }

  digitalWrite(OpenPin, digitalRead(buttonDownPin)==LOW);
    
  return OPENING;
}

State state_UNFOLDING() {
  if(digitalRead(limitDeployPin)==LOW) {
    digitalWrite(deployPin, LOW);
    digitalWrite(downPin, HIGH);
    return DOWN_TO_FLOOR;
  }

  digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW);
  
  return UNFOLDING;
}

State state_DOWN_TO_FLOOR() {
  if(digitalRead(limitFloorPin)==LOW) {
    digitalWrite(downPin, LOW);
    return FLOOR;
  }
  
  digitalWrite(downPin, digitalRead(buttonDownPin)==LOW);
  
  return DOWN_TO_FLOOR;
}

State state_FLOOR() {
  
  if(digitalRead(buttonDownPin)==LOW) {
    digitalWrite(downPin, HIGH);
    return DOWN_TO_GROUND;
  }
  
  if(digitalRead(buttonUpPin)==LOW) {
    digitalWrite(upPin, HIGH);
    return UP_TO_STOW;
  }
  
  return FLOOR;
}

State state_DOWN_TO_GROUND() {
  if(digitalRead(limitDownPin)==LOW) {
    digitalWrite(downPin, LOW);
    return GROUND;
  }
  
  digitalWrite(downPin, digitalRead(buttonDownPin)==LOW);
  
  return DOWN_TO_GROUND;
}

State state_GROUND() {
  if(digitalRead(buttonUpPin)==LOW) {
    digitalWrite(upPin, HIGH);
    return UP_TO_FLOOR;
  }
  
  return GROUND;
}

State state_UP_TO_FLOOR() {
  if(digitalRead(limitFloorPin)==LOW) {
    digitalWrite(upPin, LOW);
    return FLOOR;
  }
  
  digitalWrite(upPin, digitalRead(buttonUpPin)==LOW);
  
  return UP_TO_FLOOR;
}

State state_UP_TO_STOW() {
  if(digitalRead(limitDeployPin)==LOW) {
    digitalWrite(upPin, LOW);
    digitalWrite(stowPin, HIGH);
    return FOLDING;
  }
  
  digitalWrite(upPin, digitalRead(buttonUpPin)==LOW);
  
  return UP_TO_STOW;
}

State state_FOLDING() {
  if(digitalRead(limitStowPin)==LOW) {
    digitalWrite(stowPin, LOW);
    digitalWrite(ClosePin, HIGH);
    return CLOSING;
  }
  
  digitalWrite(stowPin, digitalRead(buttonUpPin)==LOW);
  
  return FOLDING;
}

State state_CLOSING() {
  if(digitalRead(buttonUpPin)==HIGH) {
    digitalWrite(ClosePin,LOW);
    return CLOSED;
  }
  
  digitalWrite(ClosePin, digitalRead(buttonUpPin)==LOW);
  
  return CLOSING;
}

digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW);If buttonDownPin reads LOW, write true (aka 1) to deployPin, otherwise write false, aka zero.

digitalWrite(deployPin, !digitalRead(buttonDownPin)); is another way of writing the same thing, equally badly.

What this seems to me is that it will write the deployPin equal to buttonDownPin only while buttonDownPin is LOW. Is my thinking correct?

No. What that does is read the state of the buttonDownPin, and compare the state to LOW. The result of the comparison is true or false. Since these values correspond to HIGH and LOW, respectively, the result of the comparison can be used as the state to set the deployPin to.

Add Serial.print() statements to loop() to see what state really is. The code assumes that you have the switches wired with one leg to ground and the other leg to the digital pin. Is that how you have them wired?

While AWOLs answer is, of course, correct, perhaps it will help you to understand such code if the actual workings were explained.

digitalRead(buttonDownPin) will read buttonDownPin and return it's state. Either high or low, which is equivalent to true or false, 0 or 1. (All are correct, use whatever suits you.)

The value read (high or low) will be compared to LOW ( == LOW). That again will return true or false (high or low). If the value read was high then HIGH does not equal LOW and the result will be LOW. As AWOL already explained, comparing to LOW is the same Thing as inverting the value with the NOT Opetator (!).

The final (negated) value of all of the above will then be sent to deployPin with digitalWrite(deployPin, ...)

Such evaluations are normally processed from the inside towards the outside.

I wouldn't argue with AWOL, but I don't agree that such a line is inherently "bad".

I had to separate my if/else code as I exceeded the maximum characters for one post.

My if/else code that is currently working and in use. I was having some sporadic issues and figured I had to debounce all limit switch readings. I understand that delay() isn't the best way to do this, but in this program the delay time isn't really an issue and it did clear the random issues I was having.

int buttonDownPin = 14;
int buttonUpPin = 15;
int limitOpenPin = 2;
int limitDeployPin = 3;
int limitFloorPin = 4;
int limitDownPin = 5;
int limitStowPin = 6;
int limitFoldPin = 7;
int OpenPin = 8;
int ClosePin = 9;
int deployPin = 10;
int stowPin = 11;
int downPin = 12;
int upPin = 13;
/*Stage1 //Door closed, ramp stowed, lift up
  Stage2 //Door open, ramp stowed, lift up
  Stage3 //Door open, ramp deployed, lift floor level
  Stage4 //Door open, ramp deployed, lift down*/
int Stage = 1;

void setup() {
  // put your setup code here, to run once:
  pinMode (buttonDownPin, INPUT_PULLUP);
  pinMode (buttonUpPin, INPUT_PULLUP);
  pinMode (limitOpenPin, INPUT_PULLUP);
  pinMode (limitDeployPin, INPUT_PULLUP);
  pinMode (limitFloorPin, INPUT_PULLUP);
  pinMode (limitDownPin, INPUT_PULLUP);
  pinMode (limitStowPin, INPUT_PULLUP);
  pinMode (limitFoldPin, INPUT_PULLUP);
  pinMode (OpenPin, OUTPUT);
  pinMode (ClosePin, OUTPUT);
  pinMode (deployPin, OUTPUT);
  pinMode (stowPin, OUTPUT);
  pinMode (downPin, OUTPUT);
  pinMode (upPin, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (digitalRead (buttonUpPin) == LOW || digitalRead (buttonDownPin) == LOW) {
    delay (250);
    OutIn();
  }

  else {
    for (int i = 8; i <= 13; i++) {
      // all outputs (8-13) off (relay board is active low)
      digitalWrite(i, HIGH);
      delay (10);
    }
  }
}

void OutIn() {
  if (Stage == 1) {
    Door();
  }
  else if (Stage == 2) {
    Fold();
  }
  else if (Stage == 3) {
    Floor();
  }
  else if (Stage == 4) {
    Platform();
  }
}

void Door() {
  if (digitalRead (buttonUpPin) == LOW) {
    digitalWrite(ClosePin, LOW);
  }
  else if (digitalRead (buttonDownPin) == LOW && digitalRead (limitOpenPin) == HIGH) {
    delay (100);
    digitalWrite (OpenPin, LOW);
  }
  else  if (digitalRead (buttonDownPin) == LOW && digitalRead (limitOpenPin) == LOW) {
    delay (100);
    digitalWrite (OpenPin, HIGH);
    Stage = 2;
  }
}


void Fold() {
  if (digitalRead (buttonUpPin) == LOW  && digitalRead (limitStowPin) == HIGH && digitalRead (limitFoldPin) == HIGH) {
    delay (100);
    digitalWrite(upPin, LOW);
  }
  else  if (digitalRead (buttonUpPin) == LOW  && digitalRead (limitStowPin) == LOW && digitalRead (limitFoldPin) == HIGH) {
    delay (100);
    digitalWrite (upPin, HIGH);
    digitalWrite (stowPin, LOW);
  }
  else   if (digitalRead (buttonUpPin) == LOW  && digitalRead (limitStowPin) == LOW && digitalRead (limitFoldPin) == LOW) {
    delay (100);
    digitalWrite (stowPin, HIGH);
    Stage = 1;
  }



  else if ((digitalRead (buttonDownPin) == LOW) && (digitalRead (limitDeployPin) == HIGH) && (digitalRead (limitFloorPin) == HIGH)) {
    delay (100);
    digitalWrite (deployPin, LOW);
  }
  
  else  if ((digitalRead (buttonDownPin) == LOW) && (digitalRead (limitDeployPin) == LOW) && (digitalRead (limitFloorPin) == HIGH)) {
    delay (100);
    digitalWrite (deployPin, HIGH);
    digitalWrite (downPin, LOW);
  }
  else  if ((digitalRead (buttonDownPin) == LOW) && (digitalRead (limitDeployPin) == LOW) && (digitalRead (limitFloorPin) == LOW)) {
    delay (100);
    digitalWrite (downPin, HIGH);
    Stage = 3;
  }

  //if upButton fold platform until fold limit, set to stage1
  //if downButton lower platform until floor limit, set to stage3
}
void Floor() {
  if (digitalRead (buttonUpPin) == LOW) {
    delay (1000);
    Stage = 2;
  }
  else if (digitalRead (buttonDownPin) == LOW) {
    delay (1000);
    Stage = 4;
  }
  //if upButton stop motion while button pressed, set to stage2
  // if downButton stop motion while button pressed, set to stage4
}
void   Platform() {
  if (digitalRead (buttonUpPin) == LOW && digitalRead (limitFloorPin) == LOW) {
    delay (100);
    digitalWrite(upPin, LOW);
  }
  else  if (digitalRead (buttonUpPin) == LOW && digitalRead (limitFloorPin) == HIGH) {
    delay (100);
    digitalWrite (upPin, HIGH);
    Stage = 3;
  }
  
  else if (digitalRead (buttonDownPin) == LOW) {// && (limitDownPin) == HIGH) {
    delay (100);
    digitalWrite (downPin, LOW);
  }


  //  else if (digitalRead (buttonDownPin) == LOW && digitalRead(limitDownPin) == LOW) {
  //    digitalWrite (downPin, HIGH);
  //  }


  // if upButton raise lift to floor, set to Stage3
  // if downButton lower lift until down limit* /
}

One thing that I failed to make apparent is the relay board I'm using is active LOW, so in effect LOW is what turns things on. LOW = TRUE for button pressed if that makes sense.

digitalRead(buttonDownPin) will read buttonDownPin and return it's state. Either high or low, which is equivalent to true or false, 0 or 1. (All are correct, use whatever suits you.)

The value read (high or low) will be compared to LOW ( == LOW). That again will return true or false (high or low). If the value read was high then HIGH does not equal LOW and the result will be LOW. As AWOL already explained, comparing to LOW is the same Thing as inverting the value with the NOT Opetator (!).

The final (negated) value of all of the above will then be sent to deployPin with digitalWrite(deployPin, ...)

This was my thought, you did a much better job explaining. Now I'm thinking the ==LOW in the statement, digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW);, is the problem. If it just said, digitalWrite(deployPin, digitalRead(buttonDownPin));, then it would write the pin HIGH on button release turning the motor off. The way it is now with the ==LOW there is no means to turn it off (HIGH) on button release.

The way it is now with the ==LOW there is no means to turn it off (HIGH) on button release.

On the release of which switch? When the buttonDownPin is not pressed, digitalRead(buttonDownPin) will not return LOW, so the digitalWrite() call will write false to the pin, since the value read from the buttonDownPin pin will not equal LOW.

On the release of which switch? When the buttonDownPin is not pressed, digitalRead(buttonDownPin) will not return LOW, so the digitalWrite() call will write false to the pin, since the value read from the buttonDownPin pin will not equal LOW.

Yes, on the release of buttonDownPin. I don't have the test vehicle any longer, had to give it back to the customer, so I can no longer do real world tests, just trying to learn. I'll try to better articulate my confusion on the line of code below.

digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW);

When buttonDownPin == LOW it will write deployPin to the value of buttonDownPin, which is LOW.

When buttonDownPin == HIGH it will do nothing, leaving deployPin in its last state which would most likely be LOW if buttonDownPin had ever been pressed. Then the program continues to execute every time another limit switch is made.

If my thinking above is not correct I don't know how to explain the fact that the program (and machinery) just keeps running until unplugged after buttonDownPin is released. Could it also be that if buttonDownPin == HIGH (released) in the above equation FALSE will be returned which is also LOW. If it's equal to LOW it's LOW and if it's equal to HIGH it's FALSE. That would make the return to deployPin LOW no matter what?

Also, I'd still like to know if one option is inherently better between the if/else and switch/case options?

Bri6462:
When buttonDownPin == HIGH it will do nothing, leaving deployPin in its last state which would most likely be LOW if buttonDownPin had ever been pressed. Then the program continues to execute every time another limit switch is made.

No, it's not an only-if operator. It will always return a value. Since it's boolean, that value can only be true or false.

When the value read from the buttonDownPin is HIGH, then it will write LOW to the deployPin.

This ==LOW convention for buttons can be written another way:

const int ButtonIsPressed = LOW;
...
digitalWrite(deployPin, digitalRead(buttonDownPin)==ButtonIsPressed);

This way it's a little more obvious that we are looking for the button-is-pressed state and we will return true or false depending on if it's pressed. And, if somehow the hardware changes in the future to make the button pull the input high, you could just change the definition at the top instead of having to search every place it is used.

Bri6462:
I had to print out his code and study it for quite some time for it to make sense to me, it finally does. One part I can't figure out though is the following line:

digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW);

Oh, man - did I put that in a sketch? Naughty! digitalWrite takes HIGH or LOW, not true or false. Of course: true/false works perfectly fine, because the function simply understands zero to be LOW and nay non-zero value to be HIGH, but that's not the point.

I should have written:

digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW ? HIGH : LOW);

Mind you, this also works and is actually a bit clearer:

digitalWrite(deployPin, !digitalRead(buttonDownPin));

But even though it works, it's not correct.

Bri6462:
The problem is, when I load the switch/case code and activate buttonDownPin the program just runs all the way through state to state and I have to unplug the arduino to get it to stop.

!!

Well, you shouldn't have to unplug the arduino - releasing the button should be enough. But, I see your problem. The state transition out of the "waiting" positions (CLOSED, FLOOR, GROUND) moves on if the button is pressed, when it should be moving on if the button changes from un-pressed to pressed. Likewise, there's no 'reverse' functionality - if the door is opening, to close it with the close button.

I'll have a look at it in a minute.

I'll have a look at it in a minute.

Thanks Paul. After my studying of your code I thought it looked like it should stop when the button was released. It didn't and I'm having a hard time figuring out why.

You can "unwind" this statement

digitalWrite(deployPin, digitalRead(buttonDownPin)==LOW ? HIGH : LOW);

as:

   tempState = digitalRead(buttonDownPin);
   if (tempState == LOW) {
      digitalWrite(deployPin, HIGH);
   } else {
      digitalWrite(deployPin, LOW);
   }

Paul's just squishing things into a single statement.

Ok, dude. I have rigged up the "floor" state so that is stops until there is a button press rather than the button being held down.

All states now allow you to press/release the up/down buttons.

You have to hold the button down while the machinery is in operation - it shouldn't run anything when the buttons are released or when both buttons are pressed.

/**
  Down()
  Van door fully opens (openPin) and hits limitOpenPin
  Lift fully unfolds (deployPin) and hits limitDeploy
  Lift lowers (downPin) until limitFloor and stops
  Load wheelchair from van
  Press buttonDownPin again to lower lift to ground - limitDown

  Up()
  Load wheelchair from ground
  upPin until limitFloor level and then stop
  Roll wheelchair into van
  upPin until limitDeploy
  foldPin until limitStowPin
  van door closePin until upPin released
*/

enum State {
  OPENING_CLOSING,
  FOLDING_UNFOLDING,
  UNFOLDED_TO_FLOOR,
  FLOOR,
  FLOOR_TO_GROUND,
  GROUND
} state = OPENING_CLOSING;

const byte buttonDownPin = 14;
const byte buttonUpPin = 15;

const byte limitOpenPin = 2;
const byte limitDeployPin = 3;
const byte limitFloorPin = 4;  
const byte limitDownPin = 5;
const byte limitStowPin = 6;
const byte limitFoldPin = 7;

const byte OpenPin = 8;
const byte ClosePin = 9;
const byte deployPin = 10;
const byte stowPin = 11;
const byte downPin = 12;
const byte upPin = 13;

int buttonDownState;
boolean buttonDownPressed;
int buttonUpState;
boolean buttonUpPressed;

boolean goingDown;
boolean goingUp;
boolean stopped;

void setup() {
  pinMode(buttonDownPin, INPUT_PULLUP);
  pinMode(buttonUpPin, INPUT_PULLUP);
  
  pinMode(limitOpenPin, INPUT_PULLUP);
  pinMode(limitDeployPin, INPUT_PULLUP);
  pinMode(limitFloorPin, INPUT_PULLUP);
  pinMode(limitDownPin, INPUT_PULLUP);
  pinMode(limitStowPin, INPUT_PULLUP);
  pinMode(limitFoldPin, INPUT_PULLUP);

  pinMode(OpenPin, OUTPUT);
  pinMode(ClosePin, OUTPUT);
  pinMode(deployPin, OUTPUT);
  pinMode(stowPin, OUTPUT);
  pinMode(downPin, OUTPUT);
  pinMode(upPin, OUTPUT);
}

void loop() {
   int buttonPrevState;

   buttonPrevState = buttonDownState;
   buttonDownState = digitalRead(buttonDownPin);
   buttonDownPressed = (buttonPrevState==HIGH) && (buttonDownState==LOW);
  
   buttonPrevState = buttonUpState;
   buttonUpState = digitalRead(buttonUpPin);
   buttonUpPressed = (buttonPrevState==HIGH) && (buttonUpState==LOW);

   goingDown = (buttonDownPressed==LOW) &&  (buttonUpPressed==HIGH);
   goingUp = (buttonUpPressed==LOW) &&  (buttonDownPressed==HIGH);
   stopped = !goingDown && !goingUp;
   
  switch (state) {
    case OPENING_CLOSING:   state = state_OPENING_CLOSING(); break;
    case FOLDING_UNFOLDING: state = state_FOLDING_UNFOLDING(); break;
    case UNFOLDED_TO_FLOOR: state = state_UNFOLDED_TO_FLOOR(); break;
    case FLOOR:     state = state_FLOOR(); break;
    case FLOOR_TO_GROUND: state = state_FLOOR_TO_GROUND(); break;
    case GROUND:    state = state_GROUND(); break;
    
  }
}

State state_OPENING_CLOSING() {
  if(goingDown) {
    if(digitalRead(limitOpenPin)==LOW) {
      digitalWrite(OpenPin, LOW);
      digitalWrite(ClosePin, LOW);
      return FOLDING_UNFOLDING;
    }
    else {
      digitalWrite(OpenPin, HIGH);
     digitalWrite(ClosePin, LOW);
    }
  }
  else if(goingUp) {
    // we do not have a limit switch on close.
    // this means the sketch stays in OPENING/CLOSING state
     digitalWrite(OpenPin, LOW);
    digitalWrite(ClosePin, HIGH);
  }
  else /* stopped */ {
      digitalWrite(OpenPin, LOW);
      digitalWrite(ClosePin, LOW);
  }
    
  return OPENING_CLOSING;
}

State state_FOLDING_UNFOLDING() {
  if(goingDown) {
    if(digitalRead(limitDeployPin)==LOW) {
      digitalWrite(deployPin, LOW);
      digitalWrite(stowPin, LOW);
      return UNFOLDED_TO_FLOOR;
    }
    else {
      digitalWrite(deployPin, HIGH);
      digitalWrite(stowPin, LOW);
    }
  }
  else if(goingUp) {
    if(digitalRead(limitStowPin)==LOW) {
      digitalWrite(deployPin, LOW);
      digitalWrite(stowPin, LOW);
      return OPENING_CLOSING;
    }
    else {
      digitalWrite(deployPin, LOW);
      digitalWrite(stowPin, HIGH);
    }
  }
  else /* stopped */ {
      digitalWrite(deployPin, LOW);
      digitalWrite(stowPin, LOW);
  }
  
  return FOLDING_UNFOLDING;
}

State state_UNFOLDED_TO_FLOOR() {
  if(goingDown) {
    if(digitalRead(limitFloorPin)==LOW) {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, LOW);
      return FLOOR;
    }
    else {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, HIGH);
    }
  }
  else if(goingUp) {
    if(digitalRead(limitDeployPin)==LOW) {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, LOW);
      return FOLDING_UNFOLDING;
    }
    else {
      digitalWrite(upPin, HIGH);
      digitalWrite(downPin, LOW);
    }
  }
  else /* stopped */ {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, LOW);
  }
  
  return UNFOLDED_TO_FLOOR;
}

State state_FLOOR() {
  // this will cause a press to be ignored
  // if it occurs while the other button is
  // being held down
  
  if(stopped) return FLOOR;
  
  if(buttonDownPressed) {
    return FLOOR_TO_GROUND;
  }
  
  if(buttonUpPressed) {
    return UNFOLDED_TO_FLOOR;
  }
  
  return FLOOR;
}

State state_FLOOR_TO_GROUND() {
  if(goingDown) {
    if(digitalRead(limitDownPin)==LOW) {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, LOW);
      return GROUND;
    }
    else {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, HIGH);
    }
  }
  else if(goingUp) {
    if(digitalRead(limitFloorPin)==LOW) {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, LOW);
      return FLOOR;
    }
    else {
      digitalWrite(upPin, HIGH);
      digitalWrite(downPin, LOW);
    }
  }
  else /* stopped */ {
      digitalWrite(upPin, LOW);
      digitalWrite(downPin, LOW);
  }  
  return FLOOR_TO_GROUND;
}

State state_GROUND() {
  // this will cause a press to be ignored
  // if it occurs while the other button is
  // being held down
  
  if(stopped) return GROUND;
  
  if(buttonDownPressed) {
    // do nothing;
  }
  
  if(buttonUpPressed) {
    return FLOOR_TO_GROUND;
  }

  return GROUND;
}

Thanks Paul! She should be bringing the van by in the next few weeks, I'll try to load the program and see how it works. In the meantime I'll print it out and study so I can understand what's going on.