// Mock DYPlayerArduino library functionality for Wokwi simulation
class MockDYPlayer {
public:
MockDYPlayer(Stream *stream) {}
void begin() {
Serial.println("Audio player initialized.");
}
void setVolume(int volume) {
Serial.print("Volume set to ");
Serial.println(volume);
}
void playSpecified(int soundNumber) {
Serial.print("Playing sound number: ");
Serial.println(soundNumber);
}
};
// Use the mock class instead of the actual DY::Player
MockDYPlayer player(&Serial1);
// Pin definitions (adjusted for ESP32)
const int UP_BUTTON_PIN = 5;
const int DOWN_BUTTON_PIN = 18;
const int FLOOR_4_BUTTON_PIN = 21;
const int CLOSE_BUTTON_PIN = 35;
const int OPEN_BUTTON_PIN = 36;
const int ENTER_DOOR_LIMIT_SWITCH_PIN = 37;
const int EXIT_DOOR_LIMIT_SWITCH_PIN = 38;
const int SMALL_MOTOR_PIN = 40;
const int BIG_MOTOR_PIN = 41;
const int UP_LED_PIN = 2;
const int DOWN_LED_PIN = 4;
const int FLOOR_1_LED_PIN = 12;
const int FLOOR_2_LED_PIN = 13;
const int FLOOR_3_LED_PIN = 14;
const int FLOOR_4_LED_PIN = 15;
const int WLED_BUTTON_PIN = 39; // GPIO pin connected to WLED ESP32's button input
const int BUSY_PIN = 42; // GPIO pin connected to DYPlayer's busy pin (mocked)
// Sound definitions
const int SOUND_STAFF_CLOSE_DOOR = 1;
const int SOUND_DOOR_CLOSING = 2;
const int SOUND_ELEVATOR_RUNNING = 3;
const int SOUND_MALFUNCTION = 4;
const int SOUND_RESET = 5;
const int SOUND_DOOR_OPENING = 6;
const int SOUND_ARRIVAL = 7;
// State variables
bool isUpButtonPressed = false;
bool isDoorClosed = false;
int currentFloor = 1;
int chosenFloor = 0;
bool elevatorMoving = false;
// Blinking variables
bool blinkState = false;
unsigned long lastBlinkTime = 0;
const unsigned long blinkInterval = 500; // Blink interval in milliseconds
// Debounce variables for door switches
unsigned long enterDoorLastChangeTime = 0;
int enterDoorLastState = HIGH;
bool enterDoorStableState = HIGH;
unsigned long exitDoorLastChangeTime = 0;
int exitDoorLastState = HIGH;
bool exitDoorStableState = HIGH;
const unsigned long debounceDelay = 50; // Debounce delay in milliseconds
// WLED control variables
enum WLEDButtonState {
WLED_IDLE,
WLED_SHORT_PRESS,
WLED_LONG_PRESS,
WLED_DOUBLE_PRESS
};
WLEDButtonState wledButtonState = WLED_IDLE;
unsigned long wledButtonPressStartTime = 0;
bool wledButtonPressed = false;
int wledPressCount = 0;
// Elevator state machine
enum ElevatorState {
IDLE,
WAITING_FOR_DOOR_CLOSE,
ELEVATOR_RUNNING,
ELEVATOR_MALFUNCTIONING,
OPENING_DOOR,
WAITING_FOR_EXIT,
HANDLING_EXIT_DOOR
};
ElevatorState currentState = IDLE;
unsigned long stateStartTime = 0;
void setup() {
Serial.begin(115200);
Serial.println("Elevator Simulation Started.");
// Initialize pins
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
pinMode(FLOOR_4_BUTTON_PIN, INPUT_PULLUP);
pinMode(CLOSE_BUTTON_PIN, INPUT_PULLUP);
pinMode(OPEN_BUTTON_PIN, INPUT_PULLUP);
pinMode(ENTER_DOOR_LIMIT_SWITCH_PIN, INPUT_PULLUP);
pinMode(EXIT_DOOR_LIMIT_SWITCH_PIN, INPUT_PULLUP);
pinMode(BUSY_PIN, INPUT);
pinMode(SMALL_MOTOR_PIN, OUTPUT);
pinMode(BIG_MOTOR_PIN, OUTPUT);
digitalWrite(SMALL_MOTOR_PIN, LOW);
digitalWrite(BIG_MOTOR_PIN, LOW);
pinMode(UP_LED_PIN, OUTPUT);
pinMode(DOWN_LED_PIN, OUTPUT);
pinMode(FLOOR_1_LED_PIN, OUTPUT);
pinMode(FLOOR_2_LED_PIN, OUTPUT);
pinMode(FLOOR_3_LED_PIN, OUTPUT);
pinMode(FLOOR_4_LED_PIN, OUTPUT);
pinMode(WLED_BUTTON_PIN, OUTPUT);
digitalWrite(WLED_BUTTON_PIN, LOW);
// Initialize floor lights
updateFloorLights();
// Initialize WLED state with a short press
wledButtonState = WLED_SHORT_PRESS;
// Initialize audio player
player.begin();
player.setVolume(20); // Set volume (0-30)
}
void loop() {
unsigned long currentMillis = millis();
// Handle WLED actions
handleWLEDActions(currentMillis);
// Handle LED updates and motor control
handleLEDsAndMotors(currentMillis);
// Handle state transitions
ElevatorState previousState = currentState;
switch (currentState) {
case IDLE:
handleIdleState();
break;
case WAITING_FOR_DOOR_CLOSE:
handleWaitingForDoorClose(currentMillis);
break;
case ELEVATOR_RUNNING:
handleElevatorRunning(currentMillis);
break;
case ELEVATOR_MALFUNCTIONING:
handleElevatorMalfunctioning(currentMillis);
break;
case OPENING_DOOR:
handleOpeningDoor(currentMillis);
break;
case WAITING_FOR_EXIT:
// Implement any necessary logic here
break;
case HANDLING_EXIT_DOOR:
// Implement any necessary logic here
break;
}
// Update WLED state if the elevator state has changed
if (currentState != previousState) {
updateWLEDState();
}
// Always check exit door state
checkExitDoor(currentMillis);
// Blinking logic
if (currentMillis - lastBlinkTime >= blinkInterval) {
lastBlinkTime = currentMillis;
blinkState = !blinkState;
updateFloorLights();
}
}
void handleIdleState() {
if (!isUpButtonPressed && digitalRead(UP_BUTTON_PIN) == LOW) {
isUpButtonPressed = true;
digitalWrite(UP_LED_PIN, HIGH);
digitalWrite(DOWN_LED_PIN, LOW);
Serial.println("Up button pressed.");
delay(200); // Simple delay to prevent bouncing
}
if (isUpButtonPressed && digitalRead(FLOOR_4_BUTTON_PIN) == LOW) {
chosenFloor = 4;
currentState = WAITING_FOR_DOOR_CLOSE;
Serial.println("Floor 4 selected.");
delay(200); // Simple delay to prevent bouncing
}
}
void handleWaitingForDoorClose(unsigned long currentMillis) {
static unsigned long lastSoundTime = 0;
// Debounce logic for enter door limit switch
int currentEnterDoorState = digitalRead(ENTER_DOOR_LIMIT_SWITCH_PIN);
if (currentEnterDoorState != enterDoorLastState) {
enterDoorLastChangeTime = currentMillis;
enterDoorLastState = currentEnterDoorState;
}
if ((currentMillis - enterDoorLastChangeTime) > debounceDelay) {
if (enterDoorStableState != currentEnterDoorState) {
enterDoorStableState = currentEnterDoorState;
if (enterDoorStableState == LOW) {
// Door is closed
isDoorClosed = true;
playSoundIfNotPlaying(SOUND_DOOR_CLOSING); // Play door closing sound
currentState = ELEVATOR_RUNNING;
stateStartTime = currentMillis;
elevatorMoving = true;
digitalWrite(SMALL_MOTOR_PIN, HIGH);
playSoundIfNotPlaying(SOUND_ELEVATOR_RUNNING); // Play elevator running sound
Serial.println("Door closed. Elevator is running.");
}
}
}
if (!isDoorClosed) {
// Door is not closed yet
// Play staff instruction periodically
if (currentMillis - lastSoundTime >= 2000) { // Play every 2 seconds
lastSoundTime = currentMillis;
playSoundIfNotPlaying(SOUND_STAFF_CLOSE_DOOR);
Serial.println("Door not closed. Playing staff instruction to close door.");
}
}
}
void handleElevatorRunning(unsigned long currentMillis) {
unsigned long timePerFloor = 5000; // 5 seconds per floor
if (currentMillis - stateStartTime >= timePerFloor) {
currentFloor++;
stateStartTime = currentMillis;
Serial.print("Elevator reached floor ");
Serial.println(currentFloor);
// Malfunction occurs before reaching the chosen floor
if (currentFloor >= chosenFloor) {
currentState = ELEVATOR_MALFUNCTIONING;
stateStartTime = currentMillis;
digitalWrite(UP_LED_PIN, LOW); // Turn off UP LED
playSoundIfNotPlaying(SOUND_MALFUNCTION); // Play malfunction sound
Serial.println("Elevator malfunction occurred.");
}
}
}
void handleElevatorMalfunctioning(unsigned long currentMillis) {
static int stage = 0;
static bool fallingStarted = false;
static bool fallingEnded = false;
unsigned long elapsedTime = currentMillis - stateStartTime;
// Ensure small motor is running during malfunction
digitalWrite(SMALL_MOTOR_PIN, HIGH);
// Start falling at 8th second
if (elapsedTime >= 8000 && !fallingStarted) {
fallingStarted = true;
wledButtonState = WLED_DOUBLE_PRESS; // Signal start of falling to WLED
Serial.println("Elevator starts falling.");
}
// End falling at 12th second
if (elapsedTime >= 12000 && !fallingEnded) {
fallingEnded = true;
wledButtonState = WLED_SHORT_PRESS; // Signal end of falling to WLED
Serial.println("Elevator falling ends.");
}
switch (stage) {
case 0:
if (elapsedTime < 500) {
digitalWrite(BIG_MOTOR_PIN, HIGH);
} else {
digitalWrite(BIG_MOTOR_PIN, LOW);
stage++;
}
break;
case 1:
if (elapsedTime >= 8000 && elapsedTime < 8500) {
digitalWrite(BIG_MOTOR_PIN, HIGH);
} else if (elapsedTime >= 8500) {
digitalWrite(BIG_MOTOR_PIN, LOW);
stage++;
}
break;
case 2:
if (elapsedTime >= 9000 && elapsedTime < 11500) {
digitalWrite(BIG_MOTOR_PIN, HIGH);
} else if (elapsedTime >= 11500) {
digitalWrite(BIG_MOTOR_PIN, LOW);
stage++;
}
break;
case 3:
if (elapsedTime >= 12000 && elapsedTime < 13000) {
digitalWrite(BIG_MOTOR_PIN, HIGH);
} else if (elapsedTime >= 13000) {
digitalWrite(BIG_MOTOR_PIN, LOW);
stage++;
}
break;
case 4:
if (elapsedTime >= 19000) {
digitalWrite(BIG_MOTOR_PIN, LOW);
digitalWrite(SMALL_MOTOR_PIN, LOW); // Turn off small motor at the end of malfunction
digitalWrite(DOWN_LED_PIN, LOW); // Turn off DOWN LED
currentState = OPENING_DOOR;
currentFloor = 3; // Set current floor to 3
chosenFloor = 0; // Reset chosen floor
stateStartTime = currentMillis;
stage = 0; // Reset stage for next cycle
fallingStarted = false;
fallingEnded = false;
playSoundIfNotPlaying(SOUND_STAFF_CLOSE_DOOR); // Play staff instruction to open door
Serial.println("Elevator malfunction resolved. Opening door.");
}
break;
}
}
void handleOpeningDoor(unsigned long currentMillis) {
// Debounce logic for enter door limit switch
int currentEnterDoorState = digitalRead(ENTER_DOOR_LIMIT_SWITCH_PIN);
if (currentEnterDoorState != enterDoorLastState) {
enterDoorLastChangeTime = currentMillis;
enterDoorLastState = currentEnterDoorState;
}
if ((currentMillis - enterDoorLastChangeTime) > debounceDelay) {
if (enterDoorStableState != currentEnterDoorState) {
enterDoorStableState = currentEnterDoorState;
if (enterDoorStableState == HIGH) {
playSoundIfNotPlaying(SOUND_DOOR_OPENING); // Play door opening sound
currentState = WAITING_FOR_EXIT;
Serial.println("Door opened. Waiting for exit.");
}
}
}
}
void checkExitDoor(unsigned long currentMillis) {
static bool exitDoorOpened = false;
// Debounce logic for exit door limit switch
int currentExitDoorState = digitalRead(EXIT_DOOR_LIMIT_SWITCH_PIN);
if (currentExitDoorState != exitDoorLastState) {
exitDoorLastChangeTime = currentMillis;
exitDoorLastState = currentExitDoorState;
}
if ((currentMillis - exitDoorLastChangeTime) > debounceDelay) {
if (exitDoorStableState != currentExitDoorState) {
exitDoorStableState = currentExitDoorState;
if (exitDoorStableState == HIGH && !exitDoorOpened) {
exitDoorOpened = true;
currentState = HANDLING_EXIT_DOOR;
Serial.println("Exit door opened.");
} else if (exitDoorStableState == LOW && exitDoorOpened) {
// Door has closed after being opened
resetElevator();
currentState = IDLE;
exitDoorOpened = false;
Serial.println("Exit door closed. Elevator reset to IDLE state.");
}
}
}
}
void handleLEDsAndMotors(unsigned long currentMillis) {
// Ensure small motor is off when not moving and not malfunctioning
if (!elevatorMoving && currentState != ELEVATOR_MALFUNCTIONING) {
digitalWrite(SMALL_MOTOR_PIN, LOW);
}
// Floor lights are updated in the blinking logic
}
void updateFloorLights() {
digitalWrite(FLOOR_1_LED_PIN, (currentFloor == 1 && blinkState) ? HIGH : LOW);
digitalWrite(FLOOR_2_LED_PIN, (currentFloor == 2 && blinkState) ? HIGH : LOW);
digitalWrite(FLOOR_3_LED_PIN, (currentFloor == 3 && blinkState) ? HIGH : LOW);
digitalWrite(FLOOR_4_LED_PIN, (currentFloor == 4 && blinkState) ? HIGH : LOW);
}
void resetElevator() {
isUpButtonPressed = false;
isDoorClosed = false;
currentFloor = 1;
chosenFloor = 0;
elevatorMoving = false;
digitalWrite(SMALL_MOTOR_PIN, LOW); // Ensure small motor is off
digitalWrite(BIG_MOTOR_PIN, LOW); // Ensure big motor is off
updateFloorLights();
digitalWrite(UP_LED_PIN, LOW);
digitalWrite(DOWN_LED_PIN, LOW);
// Reset WLED state
wledButtonState = WLED_SHORT_PRESS;
// Play reset sound
playSoundIfNotPlaying(SOUND_RESET);
Serial.println("Elevator reset.");
}
void updateWLEDState() {
switch (currentState) {
case IDLE:
wledButtonState = WLED_SHORT_PRESS;
break;
case ELEVATOR_RUNNING:
wledButtonState = WLED_LONG_PRESS;
break;
case ELEVATOR_MALFUNCTIONING:
// Double press handled in malfunction function
break;
default:
// Keep previous WLED state
break;
}
}
void handleWLEDActions(unsigned long currentMillis) {
switch (wledButtonState) {
case WLED_SHORT_PRESS:
if (!wledButtonPressed) {
digitalWrite(WLED_BUTTON_PIN, HIGH);
wledButtonPressStartTime = currentMillis;
wledButtonPressed = true;
} else if (currentMillis - wledButtonPressStartTime >= 100) {
digitalWrite(WLED_BUTTON_PIN, LOW);
wledButtonState = WLED_IDLE;
wledButtonPressed = false;
}
break;
case WLED_LONG_PRESS:
if (!wledButtonPressed) {
digitalWrite(WLED_BUTTON_PIN, HIGH);
wledButtonPressStartTime = currentMillis;
wledButtonPressed = true;
} else if (currentMillis - wledButtonPressStartTime >= 800) {
digitalWrite(WLED_BUTTON_PIN, LOW);
wledButtonState = WLED_IDLE;
wledButtonPressed = false;
}
break;
case WLED_DOUBLE_PRESS:
if (!wledButtonPressed && wledPressCount == 0) {
digitalWrite(WLED_BUTTON_PIN, HIGH);
wledButtonPressStartTime = currentMillis;
wledButtonPressed = true;
wledPressCount = 1;
} else if (wledButtonPressed && wledPressCount == 1 && currentMillis - wledButtonPressStartTime >= 100) {
digitalWrite(WLED_BUTTON_PIN, LOW);
wledButtonPressStartTime = currentMillis;
wledButtonPressed = false;
} else if (!wledButtonPressed && wledPressCount == 1 && currentMillis - wledButtonPressStartTime >= 100) {
digitalWrite(WLED_BUTTON_PIN, HIGH);
wledButtonPressStartTime = currentMillis;
wledButtonPressed = true;
wledPressCount = 2;
} else if (wledButtonPressed && wledPressCount == 2 && currentMillis - wledButtonPressStartTime >= 100) {
digitalWrite(WLED_BUTTON_PIN, LOW);
wledButtonState = WLED_IDLE;
wledButtonPressed = false;
wledPressCount = 0;
}
break;
case WLED_IDLE:
// Do nothing
break;
}
}
void playSoundIfNotPlaying(int soundNumber) {
if (!isPlaying()) {
player.playSpecified(soundNumber);
}
}
bool isPlaying() {
// Simulate the audio player busy state
return false; // Always return false in simulation
}