Arduino controlled elevator simulator panel

December 14, 2009

I’ve recently completed a device that I’ve been idly threatening to make for years. This is a device that has no purpose other than entertainment, and it’s fairly limited in that regard too. It was fun building it, and it was a great arduino and electronics learning project for me.

For years my kids have argued over which one of them gets to push the buttons on the elevator control panels in malls and hotels that we would go to. The inside buttons are much more desireable to push than the outside call buttons and thus it would lead to debate outside the elevator and the inevitable delays involved in figuring things out. Since this provided so much entertainment for them, I’ve been threatening for years to just build a box with buttons that light up. When I finally got around to carrying it out, things got way out of hand and I wound up just building the entire elevator panel.

So here’s the completed panel, front and back:

SANY1571

SANY1586

For a quick demo of it in action:

And just to make things a bit interesting, it’s got an ‘alarm’ mode that lights up randomly upon floor button push (there’s a one out of 25 chance of it going off).

In addition, I wanted to add some games, but haven’t gotten around to it yet. The first will be a variant of a ‘Whac-a-mole’ game. One button will light up for a brief time and if the player hits that button, he gets a point. The game gets progressively harder as the user gains points.

How it’s built

The essential hardware components are:

  • Boarduino (arduino clone from Adafruit industries)
  • 74HC595 8-Bit Serial-to-Parallel Shift Register (I got these from Electronix Express
  • 7 Segment display (also from Electronix Express
  • LED Illuminated Momentary push buttons (I got mine from ebay – real elevator buttons are fairly expensive but available from the major electronics dealers like digikey or mouser).
  • Piezo Buzzer
  • Up, Down LEDs
  • Super bright, flashing red and blue LEDs (for the alarm)

I took many pictures of it during it’s construction and posted them into a flickr set. There’s much more visual detail there.

The Arduino is controlling the LEDs, 7 segment display and buzzer using an elevator algorithm (everyone is familiar with this one – if it’s going up, it stops at each selected floor until it has reached the top floor requested, then it reverses course to get floors in the opposite direction). It obviously can’t actually go up, so it’s just waiting a predetermined amount of time before ‘reaching’ the next floor (which triggers the 7 segment display to change and the piezo buzzer to give off an elevator-like chirp). Since we don’t have any doors to open when we reach a floor where we want to stop, the 7 segment display does a left to right, then right to left ‘swipe’ effect along with as close to a ‘swoosh’ sound effect that a piezo buzzer can make. That’s the best I could do with the limited hardware.

Since there’s a total of 20 LEDs (8 button LEDs, 8 for the 7 segment display plus up/down and alarm LEDs) to control and that alone outnumbers the output pins on an arduino, I needed to use something else to control them. I chained 3 595 Shift Registers together and thus with only 3 output pins, I can control up to 24 outputs. (Use of these IC’s is described in a tutorial on the Arduino web site.

The Code

The code is structured around a giant loop looking for events (just like the early days of windows programming, prior to the availability of more event driven models). As events (like ‘reached a floor’ or ‘button pressed’) are detected, event processing functions are called to trigger the buzzer, change the display or keep a button LED lit.

When I started this, I could not find any event driven libraries for Arduino. That would have made things easier to code and understand. A quick search now yields the Aiko framework, which sounds pretty interesting.

The current Arduino sketch looks like this:

/********************************************************************************
 * Arduino sketch that simulates the interactivity of an elevator control panel.*
 * Hardware includes:                                                           *
 * - 8 momentary illuminated floor buttons                                      *
 * - seven segment display                                                      *
 * - piezo buzzer                                                               *
 * - up and down LEDs                                                           *
 * - flashing red and blue alarm LEDs                                           *
 ********************************************************************************/
//CONSTANTS
// Arduino data pins -----------------------------------------------------------
// toggle is hooked up to ANALOG IN 0
#define MODETOGGLE  0
// the rest is hooked up to digital in.
#define BUTTON1     2
#define BUTTON2     3
#define BUTTON3     4
#define BUTTON4     5
#define BUTTON5     6
#define BUTTON6     7
#define BUTTON7     8
#define BUTTON8     9
#define BUZZER      10
//Pin connected to ST_CP of all 74HC595 shift registers
#define LATCHPIN    11
//Pin connected to SH_CP of all 74HC595 shift registers
#define CLOCKPIN    12
////Pin connected to DS of first 74HC595 shift register
#define DATAPIN     13
// Byte shift positions of various LEDs ---------------------------------------
#define UPARROW_SHIFT 0
#define DOWNARROW_SHIFT 1
#define REDFLASHLED_SHIFT 2
#define BLUEFLASHLED_SHIFT 3
// other constants ------------------------------------------------------------
// debug related
#define ERROR 100
#define WARN 200
#define INFO 300
#define DEBUG 400
#define TRACE 500
#define ELEVATOR_MODE 0
#define WACKAMOLE_MODE 1
// analog level sent to piezo at floor arrival beep
#define FLOORBUZZLEVEL  150
// # of millis we buzz for a floor
#define FLOORBUZZMILLIS  300
// how long between the parts of the door swish animation
#define DOOR_SWISH_MILLIS 95
// how long to wait before running swish
#define PRE_SWISH_DELAY_MILLIS 250
// how long to wait after running swish
#define POST_SWISH_DELAY_MILLIS 300
// how long it takes to move from one floor to another
#define FLOOR_TRAVELTIME_MILLIS 1700
#define NUM_FLOOR_BUTTONS  8
#define HALF_NUM_FLOOR_BUTTONS  4
// the number of bytes needed to control all our 595 shift regs.
#define NUM_SHIFT_BYTES    3
// the number of phases for the 'door swipe' effect on the seven segment display
#define SWIPE_PHASE_COUNT  4
// stuff used by the startup routine
#define STARTUP_DELAY_MILLIS 750
#define STARTUP_BUZZ_MILLIS 250
#define STARTUP_BUZZ_ITERS 2
#define STARTUP_ITER_BUZZLEVEL 100
// siren stuff
#define SIREN_START_LEVEL 70
#define SIREN_STOP_LEVEL 200
#define SIREN_STEP_AMOUNT 10
#define SIREN_STEP_MILLIS 50
#define SIREN_SLEEP_BETW 200
// FREAKOUT -----------------------------------------------------------------------
#define FREAKOUT_CHANCE 25
// the number of phases of animation the seven seg goes through
#define SEVEN_SEG_FREAK_PHASES 5
// num millis of wait between each phase of animation
#define FREAK_CUMULATIVE_DELAY 175
// num millis of wait after last phase
#define FREAK_CUMULATIVE_END_DELAY 750
// num iterations of up and down motion (this is how many iterations of the entire effect)
#define FREAK_UP_DOWN_ITERATIONS 2
// num falling iterations
#define FREAK_FALLING_ITERATIONS 12
// num round and round iterations (this is a max, since the actual
//   iterations will be randomly chosen from 1-FREAK_ROUND_ITERATIONS
#define FREAK_ROUND_ITERATIONS 5
// --------------------------------------------------------------------------------
// binary representation of the bit patterns needed to display 0 .. 9 on the 7 segment display
// when it is attached to a 595 shift register IC.
// ASSUMES A attached to pin 1, B attached to pin 2, and so on (pin 0 attached to DP)
//   so the bits that matter are A-G (for any standard 7 segment display):      GFEDCBA-
// 7 seg display led pattern:
//   -A-
//  F   B
//   -G-
//  E   C
//   -D-    DP
//                                 GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-
byte BINARY_NUMBER_PATTERNS[10]= {B01111110, B00001100, B10110110, B10011110, B11001100, B11011010, B11111010, B00001110, B11111110, B11011110};
// 7 segment effects, round and round --------------------------------
byte SEVEN_SEGMENT_ROUND_N_ROUND[] =       { B00000010, B00000100, B00001000, B00010000, B00100000, B01000000 };
// 7 segment effects, side to side -----------------------------------
byte SEVEN_SEGMENT_LEFT_TO_RIGHT_MASK[SWIPE_PHASE_COUNT] = {       B10011110, B00001100, B00000000, B00000001  };
byte SEVEN_SEGMENT_RIGHT_TO_LEFT_MASK[SWIPE_PHASE_COUNT] = {       B00000001, B00001100, B10011110, B11111110  };
// 7 segment effects, top to bottom ----------------------------------
// single row ON, top to bottom {off,A,F+B,E+C,D}                               GFEDCBA-   GFEDCBA-   GFEDCBA-   GFEDCBA-    GFEDCBA-
byte SEVEN_SEGMENT_SINGLE_ROW_TOP_TO_BOTTOM[SEVEN_SEG_FREAK_PHASES] =         {B00000000, B00000010, B01000100, B00101000, B00010000};
// single row ON, bottom to top {off, D,E+C,F+B,A}
byte SEVEN_SEGMENT_SINGLE_ROW_BOTTOM_TO_TOP[SEVEN_SEG_FREAK_PHASES] =         {B00000000, B00010000, B00101000, B01000100, B00000010};
// cumulative rows ON, top to bottom {off,A,AFB,AFBEC,AFBECD}
byte SEVEN_SEGMENT_CUMULATIVE_ROWS_ON_TOP_TO_BOTTOM[SEVEN_SEG_FREAK_PHASES] = {B00000000, B00000010, B01000110, B11101110, B11111110};
// cumulative rows ON, bottom to top {off,D,ECD,FBECD,FBECDA}
byte SEVEN_SEGMENT_CUMULATIVE_ROWS_ON_BOTTOM_TO_TOP[SEVEN_SEG_FREAK_PHASES] = {B00000000, B00010000, B00111000, B11111010, B11111110};
// cumulative rows OFF, bottom to top {FEDCBA, FECBA, FBA, A, off}
byte SEVEN_SEGMENT_CUMULATIVE_ROWS_OFF_BOTTOM_TO_TOP[SEVEN_SEG_FREAK_PHASES] ={B01111110, B01101110, B01000110, B00000010, B00000000};
// cumulative rows OFF,top to bottom { off, A, FBA,FECBA, FEDCBA }
byte SEVEN_SEGMENT_CUMULATIVE_ROWS_OFF_TOP_TO_BOTTOM[SEVEN_SEG_FREAK_PHASES] ={B01111110, B01111100, B00111000, B00010000, B00000000};
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// Control Vars
int LOG_LEVEL = INFO;
// direction of elevator (-1=down, 0=dormant, +1=up)
int _currentDirection = 0;
// floor the elevator is on (1 - 8)
int _currentFloor = 1;
// when we should get to the next floor
unsigned long _nextFloorArrivalTime = 0;
// if we're buzzing, this is when we stop
unsigned long _currentBuzzStopTime = 0;
// the previous values of buttons 1-8 [indexes 0-7, respectively]; Values are 0 or 1
int _prevFloorButtonValues[NUM_FLOOR_BUTTONS] = {0,0,0,0,0,0,0,0};
int _floorButtonInputPins[NUM_FLOOR_BUTTONS] = {BUTTON1, BUTTON2, BUTTON3, BUTTON4, BUTTON5, BUTTON6, BUTTON7, BUTTON8};
// current values of other LED's
int _currentUpArrowLedVal = LOW;
int _currentDownArrowLedVal = LOW;
int _currentRedFlashLedVal = LOW;
int _currentBlueFlashLedVal = LOW;
// this is what we want the current value of the 7 segment display to be.
//  see the comments near the constant BINARY_NUMBER_PATTERNS for the value
//  of each bit.
byte _currentSevenSegByte = B00000000;
// mode of 0 is elevator mode, nonzero is whackamole (tbd)
byte _mode = ELEVATOR_MODE;
boolean _modeChangeFlag = false;
// flag that tells us that the last loop finished a floor buzz.
boolean _doneFloorBuzzEventFlag = false;

// --------------------------------------------------------------------------------
//  Startup
// --------------------------------------------------------------------------------

void setup() {
 log(INFO, "setup()", "Starting up...");
 log(INFO, "setup()", true);
 log(INFO, "setup()", 99);
 log(INFO, "      high: ", HIGH);
 log(INFO, "      low: ", LOW);

 //  pinMode(MODETOGGLE, INPUT);
 for(int i=0; i<NUM_FLOOR_BUTTONS; i++) {
 pinMode(_floorButtonInputPins[i], INPUT);
 }
 randomSeed(analogRead(0));

 pinMode(BUZZER, OUTPUT);
 pinMode(LATCHPIN, OUTPUT);
 pinMode(CLOCKPIN, OUTPUT);
 pinMode(DATAPIN, OUTPUT);
 Serial.begin(9600);

 initState();
 // todo: run a diagnostic routine to excercise all components
}

void initState() {
 _currentDirection = 0;
 _currentFloor = 1;
 _nextFloorArrivalTime = 0;
 _currentBuzzStopTime = 0;
 _currentUpArrowLedVal  = LOW;
 _currentDownArrowLedVal = LOW;
 _currentRedFlashLedVal = LOW;
 _currentBlueFlashLedVal = LOW;
 _currentSevenSegByte = B00000000;
 _modeChangeFlag = false;
 _doneFloorBuzzEventFlag = false;
 setAllFloorButtonLEDs(0);
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
}

// --------------------------------------------------------------------------------
//  Main Loop
// --------------------------------------------------------------------------------
void loop() {
 // runStartupRoutine();
 // delay(3000);
 loopAction();

 // test the 7segment
 //testSevenSegment();
}

/* run the main loop depending on what mode the toggle button is in.
 * There is only one mode implemented (elevator simulation) at this time.
 */
void loopAction() {
 log(TRACE, "TOP of loopAction, mode: ", _mode);
 // was a button pushed? if so, update state
 checkForAnyButtonPresses();
 // now, everything else is mode dependent, so breakout.
 if(_mode==ELEVATOR_MODE) elevatorModeLoopAction();
 else // it's wackamole
 wackamoleLoopAction();
 // update all display LEDs controlled thru shift regs.
 log(TRACE, "*updateShiftRegs ", "");
 updateShiftRegisters();
}

/* Placeholder for TBD mode -
 *  This mode would emulate a wackamole type carnival game with the floor buttons.
 */
void wackamoleLoopAction() {
 // todo: implement wackamole loop
 elevatorModeLoopAction();
}

/* Control loop for the elevator mode.  Each iteration checks for conditions and reacts to them.
 * It is really a problem that could use an event driven model solution.  Since we don't have that
 * we just loop over and look for conditions in a big if .. then .. else ..
 */
void elevatorModeLoopAction() {
 log(TRACE, "elevatorModeLoopAction  ----- fl. ", _currentFloor);
 // have we arrived at a floor yet (assuming we're moving, that is)?
 if(isFloorReached()) { // floor arrival ------------------------------------------------
 log(TRACE, "*floor reached ", "");
 int newFloor = _currentFloor + _currentDirection;
 // update _currentFloor, re-init _nextFloorArrivalTime, starts buzz
 processFloorArrivalEvent(newFloor);
 } else if(isFloorBuzzing()) { // floor buzz in progress --------------------------------
 log(TRACE, "*floor buzzing ", "");
 // clear _currentBuzzStopTime and stop buzzing
 _doneFloorBuzzEventFlag = processInFloorBuzzEvent();
 } else if (_doneFloorBuzzEventFlag) {   // floor buzz over -----------------------------
 log(TRACE, "*doneFloorBuzzEvent triggered ", "");
 _doneFloorBuzzEventFlag = false;
 // are we stopping on this floor???
 if(isStoppingOnFloor(_currentFloor)) {
 log(TRACE, "*stoppingOnFloor: ", _currentFloor);
 // ok, turn off button light, open/close door
 processStopAtFloorEvent(_currentFloor);
 }
 // should this be broken out of the parent conditional?  should it happen this cycle, or wait for the next?
 //    figure out directional logic:(figure out if we're still moving, in what direction, etc.)
 if(isAFloorRequestedInDirection(_currentDirection)) {
 log(TRACE, "*floorRequestedInDirection, curFl: ", _currentFloor);
 // start countdown to next floor,
 processElevatorMovementStartingEvent();
 } else if(isAFloorRequestedInDirection(getOppositeDirection(_currentDirection))) {
 log(TRACE, "*floorRequestedInOPPOSITEDirection, curFl: ", _currentFloor);
 // this resets _currentDirection and the up/down LEDs
 processDirectionMovementChangeEvent(getOppositeDirection(_currentDirection));
 // start countdown to next floor,
 processElevatorMovementStartingEvent();
 } else {
 log(TRACE, "*NOT MOVING ANYMORE, curFl: ", _currentFloor);
 // we're officially not moving
 // this resets _currentDirection and the up/down LEDs
 processDirectionMovementChangeEvent(0);
 }
 } else if(_currentDirection==0) {   // elevator is IDLE --------------------------------
 log(TRACE, "*ELEVATOR idle, curFl: ", _currentFloor);
 if(isAFloorRequestedInDirection(1)) {
 log(TRACE, "*Floor REQUESTED in DIRECTION: ", "up");
 processDirectionMovementChangeEvent(1);
 processElevatorMovementStartingEvent();
 } else if(isAFloorRequestedInDirection(-1)) {
 log(TRACE, "*Floor REQUESTED in DIRECTION: ", "down");
 processDirectionMovementChangeEvent(-1);
 processElevatorMovementStartingEvent();
 }
 }
 log(TRACE, "*THE END elevatorModeLoopAction() --------", "");
}

// --------------------------------------------------------------------------------
//  Startup Routine
// --------------------------------------------------------------------------------
// run a sequence of display actions, used to verify hardware's working and
//  offer a bit of LED eye candy.
void runStartupRoutine() {
 // display 0 ---------------------------------------------------------------
 setCurrentSevenSegmentToDecimalNumber(0);
 updateShiftRegisters();
 delay(STARTUP_DELAY_MILLIS);
 // foreach floor, 1..8, display number, beep, light floor button -----------
 setUpArrowLEDCurrentState(HIGH);
 for(_currentFloor=1; _currentFloor<=NUM_FLOOR_BUTTONS; _currentFloor++) {
 setAllFloorButtonLEDs(0);
 setFloorButtonLED(_currentFloor, 1); // turn on the current floor
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
 if(_currentFloor==NUM_FLOOR_BUTTONS) setUpArrowLEDCurrentState(LOW);
 updateShiftRegisters();
 runBeep(FLOORBUZZLEVEL, STARTUP_BUZZ_MILLIS, STARTUP_DELAY_MILLIS);
 }
 // beep, beep --------------------------------------------------------------
 runStartupBeepBeep();
 // run swipe ---------------------------------------------------------------
 processDoorOpenCloseEvent();
 // beep, beep --------------------------------------------------------------
 runStartupBeepBeep();
 // now back go down --------------------------------------------------------
 setDownArrowLEDCurrentState(HIGH);
 for(_currentFloor=NUM_FLOOR_BUTTONS; _currentFloor>0; _currentFloor--) {
 setAllFloorButtonLEDs(0);
 setFloorButtonLED(_currentFloor, 1); // turn on the current floor
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
 if(_currentFloor==1) setDownArrowLEDCurrentState(LOW);
 updateShiftRegisters();
 runBeep(FLOORBUZZLEVEL, STARTUP_BUZZ_MILLIS, STARTUP_DELAY_MILLIS);
 }
 // beep, beep --------------------------------------------------------------
 runStartupBeepBeep();
 // flash alarm and beep ----------------------------------------------------
 setRedFlashLEDCurrentState(HIGH);
 setBlueFlashLEDCurrentState(HIGH);
 updateShiftRegisters();
 runSirenSound(5);
 // lights out. sleep.
 initState();
 updateShiftRegisters();
 delay(300);
}

void runStartupBeepBeep() {
 for(int i=0; i<STARTUP_BUZZ_ITERS; i++)
 runBeep(STARTUP_ITER_BUZZLEVEL, STARTUP_BUZZ_MILLIS, STARTUP_DELAY_MILLIS);
}

void runSirenSound(int pNumberSirens) {
 for(int i=0; i<pNumberSirens; i++) {
 int curLevel = SIREN_START_LEVEL;
 while(curLevel<SIREN_STOP_LEVEL) {
 analogWrite(BUZZER, curLevel);
 delay(SIREN_STEP_MILLIS);
 curLevel += SIREN_STEP_AMOUNT;
 }
 analogWrite(BUZZER, 0);
 delay(SIREN_SLEEP_BETW);
 }
}

// run the buzzer at analog level pBuzzLevel for pBuzzDurationMillis.
//  then turn it off and delay an additional pPostBuzzMillis
void runBeep(int pBuzzLevel, int pBuzzDurationMillis, int pPostBuzzMillis) {
 analogWrite(BUZZER, pBuzzLevel);
 delay(pBuzzDurationMillis);
 analogWrite(BUZZER, 0);
 if(pPostBuzzMillis>0) delay(pPostBuzzMillis);
}

// --------------------------------------------------------------------------------
//  Event Processing Functions
// --------------------------------------------------------------------------------

// called when it is determined that we are in a floor buzz.
// Figures out if we have to stop buzzing.  If so, turn off up/down LED
// returns true if buzz is DONE.
boolean processInFloorBuzzEvent() {
 unsigned long now = millis();
 boolean buzzDone  = false;
 if(isFloorBuzzing()) {
 if(_currentBuzzStopTime<now) {
 _currentBuzzStopTime=0;
 // we should stop buzzing
 analogWrite(BUZZER, 0);
 buzzDone=true;
 }
 }
 return buzzDone;
}

// called when it is determined that we have reached a floor
// kick off the beep, reset the floor arrival timer, update the floor number
void processFloorArrivalEvent(int pFloorNumber) {
 unsigned long now     = millis();
 _nextFloorArrivalTime = 0;
 _currentFloor         = pFloorNumber;
 _currentBuzzStopTime  = now + FLOORBUZZMILLIS;
 analogWrite(BUZZER, FLOORBUZZLEVEL);
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
}

// Called when we are to stop at a requested floor.
// we must turn off button light and 'open/close' the door
void processStopAtFloorEvent(int pFloorNumber) {
 checkForAnyButtonPresses();
 delay(PRE_SWISH_DELAY_MILLIS);
 checkForAnyButtonPresses();
 processDoorOpenCloseEvent();
 checkForAnyButtonPresses();
 setFloorButtonLED(pFloorNumber, LOW);
 delay(POST_SWISH_DELAY_MILLIS);
 checkForAnyButtonPresses();
}

// called when we arrive at a floor that has been selected for stopping.
//  This is called AFTER the audio effect is over.
//  Run a quickie visual effect - turn on the 7seg's decimal point, then run a
//  right to left swipe 'animation', leaving everything on.  This is followed
//  by a left to right swipe leaving the current floor number.
//  At the same time, simulate a swoosh sound effect on the piezo.
void processDoorOpenCloseEvent() {
 // start off with the floor number and turn on the decimal point
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
 // setCurrentSevenSegmentDecimalPoint(true);
 // update the 7 seg, buttons, etc.
 updateShiftRegisters();
 delay(DOOR_SWISH_MILLIS);
 checkForAnyButtonPresses();
 // let's still respond to button presses while we're running the animation
 checkForAnyButtonPresses();
 // now step through the LEFT to RIGHT mask
 for(int i;i<SWIPE_PHASE_COUNT; i++) {
 _currentSevenSegByte = _currentSevenSegByte & SEVEN_SEGMENT_LEFT_TO_RIGHT_MASK[i];
 analogWrite(BUZZER, 40*i);
 updateShiftRegisters();
 delay(DOOR_SWISH_MILLIS);
 checkForAnyButtonPresses();
 }
 // now step through the RIGHT to LEFT mask
 for(int i;i<SWIPE_PHASE_COUNT; i++) {
 // in right to left, we always start with the actual number
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
 setCurrentSevenSegmentDecimalPoint(true);
 _currentSevenSegByte = _currentSevenSegByte & SEVEN_SEGMENT_RIGHT_TO_LEFT_MASK[i];
 analogWrite(BUZZER, ((40*SWIPE_PHASE_COUNT)/i));
 updateShiftRegisters();
 delay(DOOR_SWISH_MILLIS);
 checkForAnyButtonPresses();
 }
 analogWrite(BUZZER, 0);
 // now return back to the number w/out the decimal point
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
 updateShiftRegisters();
}

// called when the elevator changes direction (up, then down) or stops moving
//  because the top/bottom floor has been reached or no further floors have
//  been requested in the current direction.
// pDirection: >0 when new direction is up, <0 when down, =0 when stop
void processDirectionMovementChangeEvent(int pDirection) {
 int priorDirection = _currentDirection;
 _currentDirection  = pDirection;
 // reinitialize directional LED state vars
 setUpArrowLEDCurrentState(LOW);
 setDownArrowLEDCurrentState(LOW);
 // now set accordingly
 if(_currentDirection>0)      setUpArrowLEDCurrentState(HIGH);
 else if(_currentDirection<0) setDownArrowLEDCurrentState(HIGH);
}

// start countdown to next floor,
void processElevatorMovementStartingEvent() {
 unsigned long now     = millis();
 _nextFloorArrivalTime = now + FLOOR_TRAVELTIME_MILLIS;
}

// called when we determine that a floor button was just pushed.
void processFloorButtonPushEvent(int pFloorNumber) {
 // update state var
 setFloorButtonLED(pFloorNumber, HIGH);
 // todo: do we need to do anything else?
 log(DEBUG, "EVENT: floorbtnPush fired, floor: ", pFloorNumber);
 // every once in a while, make the panel flash like crazy just for fun.
 if( (_mode==ELEVATOR_MODE) && isFreakoutAlarmTriggered())    processFreakoutAlarmEvent();
}

// --------------------------------------------------------------------------------
//  Freakout Alarm
// --------------------------------------------------------------------------------
// determine if we should trigger a freakout alarm, when
//  all kinds of crazy stuff happens.
boolean isFreakoutAlarmTriggered() {
 long rand = random(FREAKOUT_CHANCE);
 if(rand == 1) {
 log(INFO, "FREAKOUT!", "");
 return true;
 }
 return false;
}

// save the current state of the elevator panel, then madly flash
//  the flasher diodes, play a siren on the piezo, flash all the buttons, 7seg
//  and up/down indicators for a few seconds.  Then return to normal.
// run a visual effect on the elevator panel, including:
// * blue and red flashing LEDs light up
// * buzzer is squeeling
// * 7segment flashes
// * button leds flash
void processFreakoutAlarmEvent() {
 // setup ---------------------------
 _currentFloor = 1;
 setRedFlashLEDCurrentState(HIGH);
 setBlueFlashLEDCurrentState(HIGH);
 setAllFloorButtonLEDs(LOW);
 setCurrentSevenSegmentToDecimalNumber(8);
 setCurrentSevenSegmentDecimalPoint(false);
 updateShiftRegisters();
 analogWrite(BUZZER, 0);
 delay(FREAK_CUMULATIVE_END_DELAY);
 for(int i=0; i<FREAK_UP_DOWN_ITERATIONS; i++) {
 // simulate rapid downward motion
 setDownArrowLEDCurrentState(HIGH);
 setUpArrowLEDCurrentState(LOW);
 runFreakoutEffect(SEVEN_SEGMENT_CUMULATIVE_ROWS_OFF_TOP_TO_BOTTOM,
 SEVEN_SEGMENT_SINGLE_ROW_TOP_TO_BOTTOM,
 SEVEN_SEGMENT_CUMULATIVE_ROWS_ON_TOP_TO_BOTTOM,
 false);
 analogWrite(BUZZER, 0);
 delay(FREAK_CUMULATIVE_END_DELAY);
 // go round and round
 _currentSevenSegByte = B00000000;
 setDownArrowLEDCurrentState(LOW);
 setUpArrowLEDCurrentState(LOW);
 updateShiftRegisters();
 for(int j=0; j<2; j++) {
 int r1 = random(FREAK_ROUND_ITERATIONS);
 for(int i=0; i<r1; i++) runFreakout7segRound(true);
 int r2 = random(FREAK_ROUND_ITERATIONS);
 for(int i=0; i<r2; i++) runFreakout7segRound(false);
 }

 // simulate rapid upward motion
 setDownArrowLEDCurrentState(LOW);
 setUpArrowLEDCurrentState(HIGH);
 runFreakoutEffect(SEVEN_SEGMENT_CUMULATIVE_ROWS_OFF_BOTTOM_TO_TOP,
 SEVEN_SEGMENT_SINGLE_ROW_BOTTOM_TO_TOP,
 SEVEN_SEGMENT_CUMULATIVE_ROWS_ON_BOTTOM_TO_TOP,
 false);
 }
 // re-init ---------------------------
 _currentFloor = 1;
 setRedFlashLEDCurrentState(LOW);
 setBlueFlashLEDCurrentState(LOW);
 setAllFloorButtonLEDs(LOW);
 setCurrentSevenSegmentToDecimalNumber(_currentFloor);
 setCurrentSevenSegmentDecimalPoint(false);
 setDownArrowLEDCurrentState(LOW);
 setUpArrowLEDCurrentState(LOW);
 updateShiftRegisters();
 analogWrite(BUZZER, 0);

 delay(FREAK_CUMULATIVE_END_DELAY);
}

boolean checkButtonForExitFreakout() {
 for(int i=1; i<=NUM_FLOOR_BUTTONS; i++) if(isFloorButtonJustPushed(i)) return true;
 return false;
}

void runFreakoutEffect(byte *pLeavingByte, byte *pGoingByte, byte *pArrivingByte, boolean pFloorButtonUp) {
 setCurrentSevenSegmentToDecimalNumber(8);
 setCurrentSevenSegmentDecimalPoint(false);
 updateShiftRegisters();
 adjustFreakoutBuzzer(1);
 delay(FREAK_CUMULATIVE_DELAY);
 // run leaving effect, ie going 'down' - rows disappear one at a time starting from bottom
 byte *bytePtr;
 for(int i=0; i<SEVEN_SEG_FREAK_PHASES; i++) {
 if(checkButtonForExitFreakout()) return;
 bytePtr = pLeavingByte + (i*sizeof(byte));
 _currentSevenSegByte = *bytePtr;
 setAllFloorButtonLEDs((i%2)==0 ? LOW:HIGH);
 updateShiftRegisters();
 adjustFreakoutBuzzer(i);
 delay(FREAK_CUMULATIVE_DELAY);
 }
 // run going effect, ie, fall, one row at a time
 int count = 0;
 int half_of_floors = (pFloorButtonUp) ? 0 : HALF_NUM_FLOOR_BUTTONS;
 for(int i=0; i<FREAK_FALLING_ITERATIONS; i++) {
 if(checkButtonForExitFreakout()) return;
 bytePtr = pGoingByte + ( (count % SEVEN_SEG_FREAK_PHASES) * sizeof(byte));
 _currentSevenSegByte = *bytePtr;
 // if 'going' down, we want to start with the top 2 floor buttons, otherwise bottom 2
 int floor = abs((half_of_floors - (count%HALF_NUM_FLOOR_BUTTONS)) * 2);
 setAllFloorButtonLEDs(LOW);
 setFloorButtonLED(floor, HIGH);
 setFloorButtonLED(floor-1, HIGH);
 updateShiftRegisters();
 adjustFreakoutBuzzer(i);
 delay(FREAK_CUMULATIVE_DELAY);
 count++;
 }
 // accumulate at the bottom
 for(int i=0; i<SEVEN_SEG_FREAK_PHASES; i++) {
 if(checkButtonForExitFreakout()) return;
 bytePtr = pArrivingByte + (i*sizeof(byte));
 _currentSevenSegByte = *bytePtr;
 setAllFloorButtonLEDs((i%2)==0 ? LOW:HIGH);
 updateShiftRegisters();
 adjustFreakoutBuzzer(i);
 delay(FREAK_CUMULATIVE_DELAY);
 }
 setAllFloorButtonLEDs(LOW);
}

void runFreakout7segRound(boolean pClockwise) {
 int num_round_n_round_phases = sizeof(SEVEN_SEGMENT_ROUND_N_ROUND) / sizeof(byte);
 if(pClockwise)
 for(int i=0; i< num_round_n_round_phases; i++) {
 runFreakout7segRound_1(i, pClockwise);
 }
 else
 for(int i=num_round_n_round_phases; i>0; i--) {
 runFreakout7segRound_1(i-1, pClockwise);
 }
}
void runFreakout7segRound_1(int pIndex, boolean pClockwise) {
 if(checkButtonForExitFreakout()) return;
 _currentFloor = nextFloorRoundRound(_currentFloor, pClockwise);
 setAllFloorButtonLEDs(LOW);
 setFloorButtonLED(_currentFloor, HIGH);

 _currentSevenSegByte = SEVEN_SEGMENT_ROUND_N_ROUND[pIndex];
 updateShiftRegisters();
 adjustFreakoutBuzzer(pIndex);
 delay(FREAK_CUMULATIVE_DELAY);
}

int nextFloorRoundRound(int pFloorNum, boolean pClockwise) {
 if(pClockwise) {
 switch(pFloorNum) {
 case 1 : return 3;
 case 2 : return 1;
 case 3 : return 5;
 case 4 : return 2;
 case 5 : return 7;
 case 6 : return 4;
 case 7 : return 8;
 case 8 : return 6;
 default : return 1;
 }
 } else { // counter clockwise
 switch(pFloorNum) {
 case 1 : return 2;
 case 2 : return 4;
 case 3 : return 1;
 case 4 : return 6;
 case 5 : return 3;
 case 6 : return 8;
 case 7 : return 5;
 case 8 : return 7;
 default : return 1;
 }
 }
}
void adjustFreakoutBuzzer(int pNum) {
 int HI = 180;
 int LO = 100;
 int lvl = (pNum % 2) == 0 ? LO : HI;
 analogWrite(BUZZER, lvl);
}

// --------------------------------------------------------------------------------
//  Condition Evaluation Functions
// --------------------------------------------------------------------------------

boolean isFloorButtonJustPushed(int pFloorNumber) {
 int index    = pFloorNumber-1;
 int inputPin = _floorButtonInputPins[index];
 int oldValue = _prevFloorButtonValues[index];
 log(TRACE, "isFloorButtonJustPushed? fl#: ", pFloorNumber);
 return (isButtonJustPushed(inputPin, oldValue));
}

boolean isButtonJustPushed(int pInputPin, int pOldValue) {
 log(TRACE, "  pOldValue: ", pOldValue);
 return ( isButtonPushed(pInputPin) && (pOldValue==LOW) );
}

boolean isButtonPushed(int pInputPin) {
 int val = digitalRead(pInputPin);
 boolean retval = (val==HIGH);
 log(TRACE, "  isButtonPushed on input pin: ", pInputPin);
 log(TRACE, "    is this pin high?: ", retval);
 return retval;
}

boolean isFloorBuzzing() {
 return (_currentBuzzStopTime>0);
}

// returns true if we're moving and we _just_ reached a floor
// reaching a floor means that the _nextFloorArrivalTime has been reached.
boolean isFloorReached() {
 unsigned long now = millis();
 return ( (_nextFloorArrivalTime>0) &&  (_nextFloorArrivalTime<now) ) ;
}
// are we supposed to be stopping on pFloorNum?
//  pFloorNum starts at 1
boolean isStoppingOnFloor(int pFloorNum) {
 boolean stopping = false;
 if( (pFloorNum>0) && (pFloorNum<=NUM_FLOOR_BUTTONS) ) {
 stopping = (_prevFloorButtonValues[pFloorNum-1] != 0);
 }
 return stopping;
}

// given the current direction and floor, is there another stop in this direction?
// pDirection is either -1 (down), +1(up) or 0(stationary)
boolean isAFloorRequestedInDirection(int pDirection) {
 boolean floorRequested = false;
 int nextFloor          = _currentFloor + pDirection;
 if(pDirection!=0) {
 while( !floorRequested && (nextFloor>0) && (nextFloor<=NUM_FLOOR_BUTTONS) ) {
 log(TRACE, "   is floor requested?: ", nextFloor);
 floorRequested = isStoppingOnFloor(nextFloor);
 nextFloor      += pDirection;
 }
 log(DEBUG, "floorRequested: ", floorRequested);
 if(floorRequested) log(INFO, "  nextFloor: ", nextFloor);
 }
 return floorRequested;
}

// --------------------------------------------------------------------------------
//  State Setting Functions
// --------------------------------------------------------------------------------

// set our internal representation of what the Up Arrow LED should be.
// pVal: either HIGH or LOW
void setUpArrowLEDCurrentState(int pVal) {
 _currentUpArrowLedVal = pVal;
}

// set our internal representation of what the Down Arrow LED should be.
// pVal: either HIGH or LOW
void setDownArrowLEDCurrentState(int pVal) {
 _currentDownArrowLedVal = pVal;
}

// set our internal representation of what the Red Flash LED should be.
// pVal: either HIGH or LOW
void setRedFlashLEDCurrentState(int pVal) {
 _currentRedFlashLedVal = pVal;
}

// set our internal representation of what the Blue Flash LED should be.
// pVal: either HIGH or LOW
void setBlueFlashLEDCurrentState(int pVal) {
 _currentBlueFlashLedVal = pVal;
}

// set our internal representation of what pFloorNum's LED should be
// pVal: either HIGH or LOW
// pFloorNum: the floor number from 1 to 8 (NUM_FLOOR_BUTTONS)
void setFloorButtonLED(int pFloorNum, int pVal) {
 if(pVal==LOW) log(DEBUG, "turning off button for floor: ", pFloorNum);
 _prevFloorButtonValues[pFloorNum-1] = pVal;
}

void setAllFloorButtonLEDs(int pVal) {
 for(int j=1; j<=NUM_FLOOR_BUTTONS; j++) setFloorButtonLED(j, pVal);
}

// set the internal state's working variable for the pattern of what
//  should be displayed on the 7segment display to the given decimal integer.
void setCurrentSevenSegmentToDecimalNumber(int pNumForDisplay) {
 _currentSevenSegByte = BINARY_NUMBER_PATTERNS[pNumForDisplay];
 log(DEBUG, "set 7seg decimal: ", pNumForDisplay);
 log(TRACE, "  -->7seg pattern: ", (int)_currentSevenSegByte, BIN);
}

// turns on or off the decimal point
void setCurrentSevenSegmentDecimalPoint(boolean pShowDP) {
 log(TRACE, "set 7seg DP?: ", pShowDP);
 log(TRACE, "      7seg pattern BEFORE: ", (int)_currentSevenSegByte, BIN);
 if(pShowDP)  _currentSevenSegByte = _currentSevenSegByte | 1 ;
 else         _currentSevenSegByte = _currentSevenSegByte & B11111110 ;
 log(DEBUG, "  -->7seg pattern AFTER : ", (int)_currentSevenSegByte, BIN);
}

// --------------------------------------------------------------------------------
//  Input Processors
// --------------------------------------------------------------------------------

// checks for any button presses of any sort
void checkForAnyButtonPresses() {
 checkForFloorButtonPresses();
 // todo: check the other buttons and switches
 // read toggle button - MODETOGGLE input
 checkForModeButtonPress();
}

// TODO: since we have only one mode, this does nothing right now.
void checkForModeButtonPress() {
 //  int newMode = digitalRead(MODETOGGLE);
 /*  int newMode = analogRead(MODETOGGLE);
 if( (_mode!=ELEVATOR_MODE) && (newMode==ELEVATOR_MODE) ) {
 // we went from whackamole to elevator mode
 processModeChange(ELEVATOR_MODE);
 } else if( (_mode==ELEVATOR_MODE) && (newMode!=ELEVATOR_MODE) ) {
 // we went from elevator mode to whackamole mode
 processModeChange(WACKAMOLE_MODE);
 }
 */
}

void processModeChange(int pNewMode) {
 _mode           = pNewMode;
 _modeChangeFlag = true;
 // todo: do whatever else it takes to do a modechange.
}

// check to see if any buttons are being pushed right now
// if so, update the internal state and call the button event handler
//  does NOT update shift regs
void checkForFloorButtonPresses() {
 for(int i=1; i<=NUM_FLOOR_BUTTONS; i++) {
 if((i!=_currentFloor) && isFloorButtonJustPushed(i)) {
 // this button was just pressed, and wasn't in the pressed state before
 processFloorButtonPushEvent(i);
 }
 }
}

// --------------------------------------------------------------------------------
//  Miscellaneous Functions
// --------------------------------------------------------------------------------

// directions are either +1(up), -1(down) or 0(stationary)
// pDirection should be either +1 or -1
// returns an integer representing the opposite direction of pDirection -1->+1, +1->-1, 0->0
int getOppositeDirection(int pDirection) {
 return pDirection*=-1;
}

void log(int pLogLevel, char* pPrefix, char* pMsg)   {  if(logPrePayload(pLogLevel, pPrefix))   Serial.println(pMsg);  }
void log(int pLogLevel, char* pPrefix, int pMsg)     {  log(pLogLevel, pPrefix, pMsg, DEC);  }
void log(int pLogLevel, char* pPrefix, int pMsg, int FORMAT)     {  if(logPrePayload(pLogLevel, pPrefix))   Serial.println(pMsg, FORMAT);  }
void log(int pLogLevel, char* pPrefix, boolean pMsg) {  if(logPrePayload(pLogLevel, pPrefix))   {Serial.println(pMsg?"TRUE":"FALSE");  } }

// internal function - return true if logLevel says we should be logging this.
boolean logPrePayload(int pLogLevel, char* pPrefix) {
 boolean shouldLog = (pLogLevel<=LOG_LEVEL);
 if(shouldLog) {
 Serial.print(millis());
 Serial.print(" ");
 if(pPrefix) {
 Serial.print(pPrefix);
 Serial.print(" ");
 }
 }
 return shouldLog;
}

// --------------------------------------------------------------------------------
//  Shift Register Related Functions
// --------------------------------------------------------------------------------
// some code dealing with the 595 adapted from http://arduino.cc/en/Tutorial/ShftOut11
//
// ARDUINO PINS:
// -------------       +-------+
// DATAPIN(13)-------->| 595 Q0|----> * UP Led
// CLOCKPIN(12)----+-->| #1  Q1|----> * DOWN Led               ie, sending shiftOutByte a byte of 00110000
// LATCHPIN(11)-+----->|     Q2|----> * RED flashing Led           powers the RED and BLUE flashing LEDs only.
//              |  |   |     Q3|----> * BLUE flashing Led
//              |  |   |     Q4|----> X
//              |  |   |     Q5|----> X
//              |  |   |     Q6|----> X
//              |  |   |     Q7|----> X
//              |  |   +-------+
//              |  |       | (data)
//              |  |       |
//              |  |   +-------+
//              |  |   | 595 Q0|----> O Button LED floor L
//              |  +-->| #2  Q1|----> O Button LED floor 2      ie, sending shiftOutByte a byte of 00001001
//              +--|-->|     Q2|----> O Button LED floor 3          turns on floor #4 and L's LED.
//              |  |   |     Q3|----> O Button LED floor 4
//              |  |   |     Q4|----> O Button LED floor 5
//              |  |   |     Q5|----> O Button LED floor 6
//              |  |   |     Q6|----> O Button LED floor 7
//              |  |   |     Q7|----> O Button LED floor 8
//              |  |   +-------+
//              |  |       | (data)
//              |  |       |
//              |  |   +-------+     +---------------------+
//              |  |   | 595 Q0|---->|(DP) pin DP   *7seg* |    ie, sending shiftOutByte a byte of 10110110
//              |  +-->| #3  Q1|---->|(A) pin A       ___  |        displays the number '2'
//              +----->|     Q2|---->|(B) pin B      |   | |     see descr. for BINARY_NUMBER_PATTERNS above
//                     |     Q3|---->|(C) pin C      |   | |    Seven Segment display layout:
//                     |     Q4|---->|(D) pin D       ---  |         -A-
//                     |     Q5|---->|(E) pin E      |   | |        F   B
//                     |     Q6|---->|(F) pin F      |   | |         -G-
//                     |     Q7|---->|(G) pin G       ---  |        E   C
//                     +-------+     +---------------------+         -D-
//
// So, blasting out a full set of data to the entire sequence of 595's would involve sending the 7seg's byte first,
//     then the button LED array's byte, and finally the up/down/flashing LEDs byte

// Based on the internal state of a pile of variables, construct the 3 bytes that we send
//  to the shift registers.
// Based on the assumption that the 595 shift registers are
//  hooked up as depicted above, it assumes that:
//  * the first byte we shift is for the seven segment display (hooked
//     up last in the chain)
//  * the second byte is for the button LEDs
//  * the third, and final byte is for the remaing LEDs
void updateShiftRegisters() {
 byte bytes[3] = {0,0,0};
 // 1) Seven Segment display
 //    easy, this is calculated for us elsewhere
 bytes[0] = _currentSevenSegByte;
 // 2) Button LEDs
 //    Use _prevFloorButtonValues array, which should now be set for what we want to happen
 //    Q7 maps to the most significant (left most) bit, down to Q0 mapped to the LSB (right most)
 for(int i=0; i<8; i++) {
 // bytes is initialized to 0, so we can ignore anybutton that's off
 if(_prevFloorButtonValues[i]!=0) {
 // ie, if i=2 and _prevFloorButtonValues[i]=1, we will or together bytes[1] and 00000100
 //       that will turn on floor 3's button led.
 byte currentMask = 1 << i;
 bytes[1] = bytes[1] | currentMask;
 }
 }
 // 3) other LEDs

 if(_currentUpArrowLedVal==HIGH)   bytes[2] = bytes[2] | (1 << UPARROW_SHIFT);
 if(_currentDownArrowLedVal==HIGH) bytes[2] = bytes[2] | (1 << DOWNARROW_SHIFT);
 if(_currentRedFlashLedVal==HIGH)  bytes[2] = bytes[2] | (1 << REDFLASHLED_SHIFT);
 if(_currentBlueFlashLedVal==HIGH)  bytes[2] = bytes[2] | (1 << BLUEFLASHLED_SHIFT);

 log(DEBUG, "updateShiftRegisters ", "-----------------------");
 log(DEBUG, "  byte 0 (7seg)   : ", (int)bytes[0], BIN);
 log(DEBUG, "  byte 1 (fl btns): ", (int)bytes[1], BIN);
 log(DEBUG, "  byte 2 (LEDs)   : ", (int)bytes[2], BIN);
 log(DEBUG, "/updateShiftRegisters ", "----------------------");
 // Now DO IT.
 shiftBytes(DATAPIN, CLOCKPIN, LATCHPIN, 3, bytes);
}

// shift out pNumBytes number of bytes to our 595s.  We're going to start with the FIRST
//  byte and move forward.
// the pLatchPin is set low before all shifting, and finally set high when shifting is done (causing the
//   shift register to affect the changes.
void shiftBytes(int pDataPin, int pClockPin, int pLatchPin, int pNumBytes, byte *pDataOut) {
 // latchPin is low while sending data to chip.
 digitalWrite(pLatchPin, 0);
 // pinMode(pClockPin, OUTPUT);
 // pinMode(pDataPin, OUTPUT);

 //clear everything out just in case to
 //prepare shift register for bit shifting
 digitalWrite(pDataPin, 0);
 digitalWrite(pClockPin, 0);

 //internal function setup
 int i=0;
 byte *current_byte;
 for(i=0; i<pNumBytes; i++) {
 current_byte = pDataOut + i;
 shiftOutByte(pDataPin, pClockPin, *current_byte);
 }
 //stop shifting
 digitalWrite(pClockPin, 0);
 // set the latch pin high to signal chip that it no longer needs to listen for information
 digitalWrite(pLatchPin, 1);
}

// shift out a single byte.  Start with the most significant bit (which maps to Q7),
//  all the way down to the least significant bit (which maps to Q0)
void shiftOutByte(int pDataPin, int pClockPin, byte pDataOut) {
 int i=0;
 int pinState;

 //for each bit in the byte pDataOut
 //NOTICE THAT WE ARE COUNTING DOWN in our for loop
 //This means that %00000001 or "1" will go through such
 //that it will be pin Q0 that lights.
 for (i=7; i>=0; i--)  {
 digitalWrite(pClockPin, 0);
 //if the value passed to pDataOut and a bitmask result
 // true then set pinState to HIGH, else LOW
 // ie, if we are at i=6 and our value is
 // %11010100 it would the code compares it to %01000000
 // and proceeds to set pinState to 1.
 if ( pDataOut & (1<<i) ) {
 pinState= HIGH;
 } else {
 pinState= LOW;
 }

 //Sets the pin to HIGH or LOW depending on pinState
 digitalWrite(pDataPin, pinState);
 //register shifts bits on upstroke of clock pin
 digitalWrite(pClockPin, 1);
 //zero the data pin after shift to prevent bleed through
 digitalWrite(pDataPin, 0);
 }
}

// --------------------------------------------------------------------------------
// TEST ROUTINES
// --------------------------------------------------------------------------------

// display 0
// when button pushed, display that button's floor num on 7seg and beep
// run this in the loop and look at the Serial output
void testSevenSegment() {
 //  LOG_LEVEL=DEBUG;
 log(INFO, "      high: ", HIGH);
 log(INFO, "      low: ", LOW);
 // display 0 and dec pt. --------------------------------------------------------
 setCurrentSevenSegmentToDecimalNumber(0);
 setCurrentSevenSegmentDecimalPoint(true);
 updateShiftRegisters();
 runBeep(FLOORBUZZLEVEL, STARTUP_BUZZ_MILLIS, STARTUP_DELAY_MILLIS);
 delay(STARTUP_DELAY_MILLIS);

 while(true) {
 updateShiftRegisters();
 checkForFloorButtonPresses();
 for(int i=0; i<NUM_FLOOR_BUTTONS; i++) {
 if(_prevFloorButtonValues[i]>0) {
 // set 7seg to that floor's button value
 setCurrentSevenSegmentToDecimalNumber(i+1);
 updateShiftRegisters();
 runBeep(FLOORBUZZLEVEL, STARTUP_BUZZ_MILLIS, STARTUP_DELAY_MILLIS);
 // test the swipe
 _currentFloor = i+1;
 processDoorOpenCloseEvent();
 // turn off that floor
 _prevFloorButtonValues[i]=0;
 updateShiftRegisters();
 }
 }
 delay(100);
 }
}
About these ads

11 Responses to “Arduino controlled elevator simulator panel”


  1. […] controlled elevator simulator panel, love it! Semi-crazy writes- I’ve recently completed a device that I’ve been idly threatening to make for years. […]


  2. […] Still, it could be a great prop for a short film. More infos on his project blog. […]


  3. […] Still, it could be a great prop for a short film. More infos on his project blog. […]

  4. Samantha Says:

    Nice blog, i like it, its informative,
    i will visit his blog more often.
    i like your topic, specially about
    Arduino controlled elevator simulator panel

    Cheers

  5. Lloyd Says:

    I have a son who would love this! I was actually researching how I could make one of these and found your blog as a result. I would love to make this for my son but I only have soldering skills, no programming. I asked a friend to look at your description and he thinks I just have to buy the components and solder it together. Is he right? If not can I just copy and paste your code? Or, if you are willing, could you give me a more detailed account of what I have to do in order to make one for my son. If so I would be soooo appreciative.
    Thank you for any response you might give me.

    • semicrazy Says:

      Lloyd,

      All the code that is running it is in the blog post – there is nothing missing. Assuming your arduino (boarduino) pins mappings are the same, it should work.

      As for more details, I like to take a lot of pictures when I do my projects, so it winds up being like documentation for me to go back to if I need it. There’s a flickr set with those photos here: http://www.flickr.com/photos/swinz/sets/72157621290370977/
      At the very least, the 595 pin mappings photo may help explain what I did with mine.

      Post back if you wind up doing it. Enjoy.

  6. Lloyd Says:

    Thank you for responding, I really appreciate it. I checked out the photos and you really did a nice job. You have inspired me. I am going to order the parts and when I complete it (assuming I do complete it) I will most certainly post back. Thanks again.

  7. chaydmi Says:

    hello , i need to control a simple elevator ( 3 floors , just up and down ) by 2 shift registers and a comparator , thank you


  8. […] Here at Freetronics we can get you started with your own simulator by offering a range of Arduino-compatible boards, as well as our shift register, sound and RGB LEDmodules. However for more information about the project visit Lloyd’s blog. […]

  9. joie Says:

    hi! I just want to ask, what if you did not include the buzzer and the LEDs, just the push buttons and the 7 segment display, what would the code be? I am having some difficult time understanding some of the codes. hehe! But this is really informative! :) Thanks!


  10. […] I think I stumbled on a more advanced next project to think about. It could be something like this, an elevator-simulator. But maybe rather designed as a possible lab experiment. Another idea is to […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: