Start the elevator simulation. Write a program that connects to the simulation, prints a message (e.g. Connected to the elevator!) when the connection works, then disconnects and ends.
Then close the simulation and run your program again: what happens?
The first job of every program is to connect. If sim_connect() does not return 0 the simulation is not running, so the program ends without doing anything (the library also prints Start the simulation first). If it is connected, the message is printed and sim_disconnect() closes the connection. You will use this skeleton in every task from now on.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1; // simulation is not running
printf("Connected to the elevator!\n");
sim_disconnect();
return 0;
}
from elevator import *
if sim_connect() != 0: # simulation is not running
exit(1)
print("Connected to the elevator!")
sim_disconnect()
Show the numbers 1, 2 and 3 one after another on the display in the Cabin Buttons panel. Each number stays on the display for 1 second. When the program ends the display must show 3.
The program runs the commands from top to bottom. Without waits the three commands would run in an instant and you would only see 3. Waiting 1 second after each cabin_display() makes every number visible. No wait is needed after the last one; the display keeps the value until you change it.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
cabin_display(1);
Sleep(1000); // 1000 ms = 1 second
cabin_display(2);
Sleep(1000);
cabin_display(3);
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
cabin_display(1)
time.sleep(1) # 1 second
cabin_display(2)
time.sleep(1)
cabin_display(3)
sim_disconnect()
Turn on the lights of the buttons 1, 2 and 3 in the Cabin Buttons panel (the buttons turn orange), wait 3 seconds and turn all three off.
The three lights are turned on one after another; the commands run so fast that they seem to light up together. After waiting 3 seconds the same function is called with 0 to turn them off. A light does not turn on by itself when its button is pressed; only your program turns it on.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
cabin_button_light(1, 1); // on
cabin_button_light(2, 1);
cabin_button_light(3, 1);
Sleep(3000);
cabin_button_light(1, 0); // off
cabin_button_light(2, 0);
cabin_button_light(3, 0);
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
cabin_button_light(1, 1) # on
cabin_button_light(2, 1)
cabin_button_light(3, 1)
time.sleep(3)
cabin_button_light(1, 0) # off
cabin_button_light(2, 0)
cabin_button_light(3, 0)
sim_disconnect()
Turn on the lights of the call buttons that make sense in a real building: ▲ on floor 1, ▲ and ▼ on floor 2, ▼ on floor 3. After 3 seconds turn them all off. (The ▼ button on floor 1 and the ▲ button on floor 3 must stay dark.)
A floor button light needs three pieces of information: which floor, which direction and on or off. You cannot go down from floor 1 or up from floor 3, so we leave those two buttons alone. DIR_UP and DIR_DOWN are constants from the library (1 and 0); names instead of numbers make the code readable.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
floor_button_light(1, DIR_UP, 1);
floor_button_light(2, DIR_UP, 1);
floor_button_light(2, DIR_DOWN, 1);
floor_button_light(3, DIR_DOWN, 1);
Sleep(3000);
floor_button_light(1, DIR_UP, 0);
floor_button_light(2, DIR_UP, 0);
floor_button_light(2, DIR_DOWN, 0);
floor_button_light(3, DIR_DOWN, 0);
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
floor_button_light(1, DIR_UP, 1)
floor_button_light(2, DIR_UP, 1)
floor_button_light(2, DIR_DOWN, 1)
floor_button_light(3, DIR_DOWN, 1)
time.sleep(3)
floor_button_light(1, DIR_UP, 0)
floor_button_light(2, DIR_UP, 0)
floor_button_light(2, DIR_DOWN, 0)
floor_button_light(3, DIR_DOWN, 0)
sim_disconnect()
First click Restart Sim in the menu (the cabin goes to floor 3). Move the cabin down for 2 seconds and stop it. Wait 1 second, then move it up for 1 second and stop it again. The cabin must stop somewhere between floor 3 and floor 2.
cabin_motor(CABIN_DOWN) starts the motor and returns at once; the waiting time decides how far the cabin goes. In 2 seconds the cabin travels a bit less than half of the way between two floors, so it does not reach floor 2. It goes up for only 1 second, so there is no risk of passing floor 3 (error). If you forget CABIN_STOP the cabin goes all the way down and gives an error there.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
cabin_motor(CABIN_DOWN);
Sleep(2000);
cabin_motor(CABIN_STOP);
Sleep(1000);
cabin_motor(CABIN_UP);
Sleep(1000);
cabin_motor(CABIN_STOP); // never leave the motor running
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
cabin_motor(CABIN_DOWN)
time.sleep(2)
cabin_motor(CABIN_STOP)
time.sleep(1)
cabin_motor(CABIN_UP)
time.sleep(1)
cabin_motor(CABIN_STOP) # never leave the motor running
sim_disconnect()
Click Restart Sim. Open the floor 3 door for 0.3 seconds and stop its motor (the door opens about halfway). Wait 2 seconds, then close the door for 0.3 seconds and stop the motor.
Watch whether the door closes completely and whether ERROR appears in the menu bar. Run it a few times (click Restart Sim before each run).
Like the cabin motor, a door motor runs until you stop it. Opening and closing take the same time, so you expect the door to close completely, but waits on a computer are not exact to the millisecond: sometimes the door stays a little open, sometimes it keeps closing after it is closed and a short ERROR appears. Timing alone does not give an exact result. In Task 19 you will do the same job with sensors and without errors.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
door(3, DOOR_OPEN);
Sleep(300);
door(3, DOOR_STOP);
Sleep(2000);
door(3, DOOR_CLOSE);
Sleep(300);
door(3, DOOR_STOP);
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
door(3, DOOR_OPEN)
time.sleep(0.3)
door(3, DOOR_STOP)
time.sleep(2)
door(3, DOOR_CLOSE)
time.sleep(0.3)
door(3, DOOR_STOP)
sim_disconnect()
Read the three floor sensors, keep each one in its own variable and print the three values (e.g. Floor 1: 0, Floor 2: 0, Floor 3: 1).
Run it after Restart Sim. Then move the cabin down with Down in the Manual Control panel, click Stop between two floors and run the program again. What do you see?
A variable is a box that keeps a value under a name. We read each sensor once and keep it in a variable. When the cabin is at a floor, that floor's sensor is 1 and the others are 0. Between two floors all three sensors are 0: a sensor gives 1 only when the cabin is exactly level with the floor. Stopping exactly at a floor by hand is very hard; programs do it by reading the sensor all the time.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int s1 = cabin_sensor_read(1);
int s2 = cabin_sensor_read(2);
int s3 = cabin_sensor_read(3);
printf("Floor 1: %d\n", s1);
printf("Floor 2: %d\n", s2);
printf("Floor 3: %d\n", s3);
sim_disconnect();
return 0;
}
from elevator import *
if sim_connect() != 0:
exit(1)
s1 = cabin_sensor_read(1)
s2 = cabin_sensor_read(2)
s3 = cabin_sensor_read(3)
print("Floor 1:", s1)
print("Floor 2:", s2)
print("Floor 3:", s3)
sim_disconnect()
Find out at which floor the cabin is. If it is at a floor, show the floor number on the cabin display and print it. If the cabin is between floors, show 0 on the display and print that it is between floors.
The variable floor starts at 0. The if / else if chain tries the sensors one by one and stores the floor number when it finds the one that is 1. Then a single if / else handles the two cases: the floor number if a floor was found, otherwise 0. You will use this 'which floor is the cabin at?' question a lot later.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int floor = 0; // 0 = between floors
if (cabin_sensor_read(1) == 1) floor = 1;
else if (cabin_sensor_read(2) == 1) floor = 2;
else if (cabin_sensor_read(3) == 1) floor = 3;
if (floor != 0)
printf("The cabin is at floor %d\n", floor);
else
printf("The cabin is between floors\n");
cabin_display(floor);
sim_disconnect();
return 0;
}
from elevator import *
if sim_connect() != 0:
exit(1)
floor = 0 # 0 = between floors
if cabin_sensor_read(1) == 1:
floor = 1
elif cabin_sensor_read(2) == 1:
floor = 2
elif cabin_sensor_read(3) == 1:
floor = 3
if floor != 0:
print("The cabin is at floor", floor)
else:
print("The cabin is between floors")
cabin_display(floor)
sim_disconnect()
Print the state of the floor 3 door: fully closed, fully open or partly open.
Try it after Restart Sim (closed) and after clicking Open for floor 3 in Manual Control and then Stop while the door is halfway (partly open). Stopping the door exactly at its limit by hand is very hard (if you are late, ERROR appears); you will see the fully open state from a program in Task 19.
One sensor cannot tell three states apart. First we check whether the door is closed (door_sensor_read is 0). If not, we check whether it is fully open (door_open_read is 1). If neither, the door is somewhere in between. The order of else if matters: the first true condition runs and the rest are skipped.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int notClosed = door_sensor_read(3); // 1 = not fully closed
int fullyOpen = door_open_read(3); // 1 = fully open
if (notClosed == 0)
printf("Door 3 is fully closed\n");
else if (fullyOpen == 1)
printf("Door 3 is fully open\n");
else
printf("Door 3 is partly open\n");
sim_disconnect();
return 0;
}
from elevator import *
if sim_connect() != 0:
exit(1)
not_closed = door_sensor_read(3) # 1 = not fully closed
fully_open = door_open_read(3) # 1 = fully open
if not_closed == 0:
print("Door 3 is fully closed")
elif fully_open == 1:
print("Door 3 is fully open")
else:
print("Door 3 is partly open")
sim_disconnect()
First the program prints a message and then waits 5 seconds. During this time press the button 2 in the Cabin Buttons panel and keep it held down. When the 5 seconds are over the program reads the button: if it is pressed, it turns on the light of button 2 for 2 seconds and prints that it was pressed; otherwise it prints that it was not pressed.
Also try pressing the button and releasing it before the 5 seconds are over. What happens, and why?
A button reads 1 only while it is held down; it becomes 0 as soon as you release it. The program reads the button only once, at the end of the 5 seconds, so a press released earlier is never seen. Not to miss a press, the button must be read all the time; you will do that with loops in Level 3.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
printf("Press and HOLD cabin button 2 ...\n");
Sleep(5000);
if (cabin_button_read(2) == 1)
{
printf("Button 2 is pressed\n");
cabin_button_light(2, 1);
Sleep(2000);
cabin_button_light(2, 0);
}
else
{
printf("Button 2 is not pressed\n");
}
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
print("Press and HOLD cabin button 2 ...")
time.sleep(5)
if cabin_button_read(2) == 1:
print("Button 2 is pressed")
cabin_button_light(2, 1)
time.sleep(2)
cabin_button_light(2, 0)
else:
print("Button 2 is not pressed")
sim_disconnect()
Click Restart Sim; the floor 3 door is closed. Make a mistake on purpose: try to close the closed door for 0.1 seconds. Read the error flag right before you stop the motor, then stop the motor and print the result. Wait 1 second, read the flag again and print it.
Watch ERROR in the menu bar. Click Restart Sim when you are done.
Closing a door that is already closed means pushing it past its limit; the simulation marks this as an error and bounces the door back a little. While the motor runs the error happens again and again, so the first read gives 1. Once the motor stops, the flag clears itself after a short time, so the read 1 second later gives 0. The door may stay a tiny bit open because of the bounce; that is why we click Restart Sim at the end.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
door(3, DOOR_CLOSE); // the door is already closed: a mistake on purpose
Sleep(100);
int err = get_error();
door(3, DOOR_STOP);
if (err == 1)
printf("Error flag is 1: a limit was exceeded!\n");
else
printf("No error\n");
Sleep(1000);
printf("One second later the flag is %d\n", get_error());
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
door(3, DOOR_CLOSE) # the door is already closed: a mistake on purpose
time.sleep(0.1)
err = get_error()
door(3, DOOR_STOP)
if err == 1:
print("Error flag is 1: a limit was exceeded!")
else:
print("No error")
time.sleep(1)
print("One second later the flag is", get_error())
sim_disconnect()
Click Restart Sim. Start the cabin moving down, after 0.5 seconds read the state of the cabin motor and print its meaning (stopped, up or down). Then stop the motor, read the state once more and print it as a number.
The value read is the last command given to the motor (including a command from Manual Control). Comparing it with CABIN_STOP, CABIN_UP and CABIN_DOWN turns the number into a meaningful word. After stopping, the value is 0. In the same way door_motion_read(floor) gives the state of a door motor.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
cabin_motor(CABIN_DOWN);
Sleep(500);
int m = cabin_motion_read();
if (m == CABIN_STOP) printf("Motor: stopped\n");
else if (m == CABIN_UP) printf("Motor: up\n");
else if (m == CABIN_DOWN) printf("Motor: down\n");
cabin_motor(CABIN_STOP);
printf("After stop the value is %d\n", cabin_motion_read());
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
cabin_motor(CABIN_DOWN)
time.sleep(0.5)
m = cabin_motion_read()
if m == CABIN_STOP:
print("Motor: stopped")
elif m == CABIN_UP:
print("Motor: up")
elif m == CABIN_DOWN:
print("Motor: down")
cabin_motor(CABIN_STOP)
print("After stop the value is", cabin_motion_read())
sim_disconnect()
The elevator is ready to move only if the cabin is at a floor AND the door of that floor is fully closed. The program checks both conditions: if both are true it prints that it is ready and the floor number and shows the floor on the display; otherwise it prints that it is not ready.
Try it after Restart Sim, then again after opening the floor 3 door a little with Manual Control (Open, then Stop).
A condition written with && (and) is true when both sides are true. The order matters: floor != 0 is checked first; if it is false the second part is not run at all, so an invalid door 0 is never read. This check is the first version of the safety check we will do before moving the cabin later.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int floor = 0;
if (cabin_sensor_read(1) == 1) floor = 1;
else if (cabin_sensor_read(2) == 1) floor = 2;
else if (cabin_sensor_read(3) == 1) floor = 3;
if (floor != 0 && door_sensor_read(floor) == 0)
{
printf("Ready at floor %d\n", floor);
cabin_display(floor);
}
else
{
printf("Not ready\n");
}
sim_disconnect();
return 0;
}
from elevator import *
if sim_connect() != 0:
exit(1)
floor = 0
if cabin_sensor_read(1) == 1:
floor = 1
elif cabin_sensor_read(2) == 1:
floor = 2
elif cabin_sensor_read(3) == 1:
floor = 3
if floor != 0 and door_sensor_read(floor) == 0:
print("Ready at floor", floor)
cabin_display(floor)
else:
print("Not ready")
sim_disconnect()
Count from 1 to 9 on the cabin display; each number is visible for 0.5 seconds. This time use a loop instead of writing 9 separate commands.
A for loop runs the same commands again and again with a counter. i grows by one each round and is written to the display. In Python range(1, 10) starts at 1 and stops before 10. Thanks to the loop, counting to 99 would take a program of the same length.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
for (int i = 1; i <= 9; i++)
{
cabin_display(i);
Sleep(500);
}
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
for i in range(1, 10):
cabin_display(i)
time.sleep(0.5)
sim_disconnect()
Write a program that runs forever: when the cabin reaches a floor, it shows that floor's number on the display and prints it once. While the cabin is between floors the display keeps showing the last floor. It must not print the same floor again and again.
While the program runs, move the cabin with the Manual Control panel (Up / Stop / Down). Stop the program with Ctrl+C.
While button 2 in the Cabin Buttons panel is held down its light is on; when it is released the light goes off. Send the light command only when the button's state changes, and print pressed / released. Stop with Ctrl+C.
Each round the button is read and compared with old. If nothing changed, nothing is done. If it changed, the new button value is written straight to the light (1 on, 0 off) and old is updated. This 'act only on a change' pattern is used a lot in control programs: it avoids needless commands and a crowded console.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int old = 0; // value in the previous round
while (1)
{
int b = cabin_button_read(2);
if (b != old)
{
cabin_button_light(2, b);
if (b == 1) printf("pressed\n");
else printf("released\n");
old = b;
}
Sleep(10);
}
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
old = 0 # value in the previous round
while True:
b = cabin_button_read(2)
if b != old:
cabin_button_light(2, b)
if b == 1:
print("pressed")
else:
print("released")
old = b
time.sleep(0.01)
When one of the cabin buttons (1, 2, 3) is pressed briefly, its light turns on and stays on. When the cabin reaches that floor the light goes off. Print one line for each request and each arrival.
While the program runs, press the buttons and move the cabin between floors with Manual Control. Stop with Ctrl+C.
A button is 1 only while it is held, so the program must remember the request; the array lit does that. It has 4 elements; we do not use element 0 and use the floor number directly as the index. If the button is pressed and the light is off, the request is stored; if the light is on and the cabin is at that floor, the request is cleared. Thanks to the lit[f] == 0 check a held button is not stored again every round. If you press 3 while the cabin is already at floor 3, the light flashes and goes off: the request was served at once.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int lit[4] = {0, 0, 0, 0}; // lit[1..3]: request stored?
while (1)
{
for (int f = 1; f <= 3; f++)
{
if (cabin_button_read(f) == 1 && lit[f] == 0)
{
lit[f] = 1;
cabin_button_light(f, 1);
printf("Request: floor %d\n", f);
}
if (lit[f] == 1 && cabin_sensor_read(f) == 1)
{
lit[f] = 0;
cabin_button_light(f, 0);
printf("Reached floor %d\n", f);
}
}
Sleep(10);
}
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
lit = [0, 0, 0, 0] # lit[1..3]: request stored?
while True:
for f in (1, 2, 3):
if cabin_button_read(f) == 1 and lit[f] == 0:
lit[f] = 1
cabin_button_light(f, 1)
print("Request: floor", f)
if lit[f] == 1 and cabin_sensor_read(f) == 1:
lit[f] = 0
cabin_button_light(f, 0)
print("Reached floor", f)
time.sleep(0.01)
Click Restart Sim (the cabin is at floor 3). Send the cabin down and wait until the floor 2 sensor is 1; stop the motor at that moment. Show 2 on the display and print that floor 2 was reached. The cabin must stop exactly level with the floor and no ERROR may appear.
After Task 18 the cabin is at floor 2. The program first checks that the cabin is at floor 2; if not, it prints a message and ends. If it is there: open the floor 2 door until it is fully open and stop it, keep it open for 3 seconds, then close it until it is fully closed and stop it. ERROR must never appear (compare with Task 6).
Because we wait for the sensor instead of a time, the door stops exactly at its limit every time: the motor is cut the moment the fully open sensor is 1 or the door sensor is 0. If a door motor is not stopped within one simulation step (about 16 ms) at its limit, an error occurs; the empty loop is fast enough. During the 3 second wait no motor runs, so Sleep is correct there. The check at the start keeps the door from opening onto an empty shaft when the cabin is at another floor.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
if (cabin_sensor_read(2) == 0)
{
printf("The cabin is not at floor 2 (run Task 18 first)\n");
sim_disconnect();
return 0;
}
door(2, DOOR_OPEN);
while (door_open_read(2) == 0) { } // wait: fully open
door(2, DOOR_STOP);
printf("Door 2 is open\n");
Sleep(3000); // no motor runs: Sleep is fine
door(2, DOOR_CLOSE);
while (door_sensor_read(2) == 1) { } // wait: fully closed
door(2, DOOR_STOP);
printf("Door 2 is closed\n");
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
if cabin_sensor_read(2) == 0:
print("The cabin is not at floor 2 (run Task 18 first)")
sim_disconnect()
exit(0)
door(2, DOOR_OPEN)
while door_open_read(2) == 0: # wait: fully open
pass
door(2, DOOR_STOP)
print("Door 2 is open")
time.sleep(3) # no motor runs: sleep is fine
door(2, DOOR_CLOSE)
while door_sensor_read(2) == 1: # wait: fully closed
pass
door(2, DOOR_STOP)
print("Door 2 is closed")
sim_disconnect()
Wherever the cabin is (at floor 3, at floor 2 or between floors), bring it down to floor 1, stop it exactly level and show 1 on the display. If the cabin is already at floor 1, do not start the motor at all; just print that.
Try it a few times after moving the cabin to different places with Manual Control.
Run it while the cabin is at a floor (if it is between floors the program prints a message and ends). The program waits until a cabin button (1, 2 or 3) is pressed. Turn on the pressed button's light, send the cabin in the right direction and stop it at that floor, turn the light off, show the floor on the display and print it. If the pressed floor is the current floor the cabin must not move.
The program combines parts of earlier tasks. The first loop runs until a button is pressed; Sleep(10) is fine there because no motor runs and a person holds a button much longer than 10 ms. The direction is up if the target is above the current floor and down if it is below; if they are equal the motor is never started and the waiting loop ends at once, because that floor's sensor is already 1.
#include "elevator.h"
int main(void)
{
if (sim_connect() != 0) return 1;
int floor = 0;
for (int f = 1; f <= 3; f++)
if (cabin_sensor_read(f) == 1) floor = f;
if (floor == 0)
{
printf("Bring the cabin to a floor first\n");
sim_disconnect();
return 0;
}
printf("Press a cabin button (1, 2 or 3)\n");
int target = 0;
while (target == 0)
{
for (int f = 1; f <= 3; f++)
if (cabin_button_read(f) == 1) target = f;
Sleep(10);
}
cabin_button_light(target, 1);
if (target > floor) cabin_motor(CABIN_UP);
else if (target < floor) cabin_motor(CABIN_DOWN);
while (cabin_sensor_read(target) == 0) { }
cabin_motor(CABIN_STOP);
cabin_button_light(target, 0);
cabin_display(target);
printf("Arrived at floor %d\n", target);
sim_disconnect();
return 0;
}
from elevator import *
import time
if sim_connect() != 0:
exit(1)
floor = 0
for f in (1, 2, 3):
if cabin_sensor_read(f) == 1:
floor = f
if floor == 0:
print("Bring the cabin to a floor first")
sim_disconnect()
exit(0)
print("Press a cabin button (1, 2 or 3)")
target = 0
while target == 0:
for f in (1, 2, 3):
if cabin_button_read(f) == 1:
target = f
time.sleep(0.01)
cabin_button_light(target, 1)
if target > floor:
cabin_motor(CABIN_UP)
elif target < floor:
cabin_motor(CABIN_DOWN)
while cabin_sensor_read(target) == 0:
pass
cabin_motor(CABIN_STOP)
cabin_button_light(target, 0)
cabin_display(target)
print("Arrived at floor", target)
sim_disconnect()
Write two functions: current_floor() returns the floor where the cabin is (0 between floors); go_to_floor(target) takes the cabin to the target floor, stops it, shows the floor on the display and prints it. If the cabin is between floors, go_to_floor first goes down to the floor below and then to the target.
Use them for this tour: go to floor 1, wait 2 seconds, go to floor 3, wait 2 seconds, go to floor 2.
current_floor() tries the sensors one by one and gives back the first floor that is 1 with return; if none is 1 it returns 0. go_to_floor() uses this to choose the direction. Between floors the direction is unknown, so we first go down to a floor (as in Task 20, going down always reaches a floor). The main program is now very short and readable: it says what is done, and how it is done is inside the functions.
#include "elevator.h"
// Floor where the cabin is (1..3), or 0 between floors
int current_floor(void)
{
for (int f = 1; f <= 3; f++)
if (cabin_sensor_read(f) == 1) return f;
return 0;
}
// Takes the cabin to the target floor and stops it there
void go_to_floor(int target)
{
if (current_floor() == 0) // between floors: go down to a floor
{
cabin_motor(CABIN_DOWN);
while (current_floor() == 0) { }
cabin_motor(CABIN_STOP);
}
int now = current_floor();
if (target > now) cabin_motor(CABIN_UP);
else if (target < now) cabin_motor(CABIN_DOWN);
while (cabin_sensor_read(target) == 0) { }
cabin_motor(CABIN_STOP);
cabin_display(target);
printf("At floor %d\n", target);
}
int main(void)
{
if (sim_connect() != 0) return 1;
go_to_floor(1);
Sleep(2000);
go_to_floor(3);
Sleep(2000);
go_to_floor(2);
sim_disconnect();
return 0;
}
from elevator import *
import time
def current_floor():
"""Floor where the cabin is (1..3), or 0 between floors."""
for f in (1, 2, 3):
if cabin_sensor_read(f) == 1:
return f
return 0
def go_to_floor(target):
"""Takes the cabin to the target floor and stops it there."""
if current_floor() == 0: # between floors: go down to a floor
cabin_motor(CABIN_DOWN)
while current_floor() == 0:
pass
cabin_motor(CABIN_STOP)
now = current_floor()
if target > now:
cabin_motor(CABIN_UP)
elif target < now:
cabin_motor(CABIN_DOWN)
while cabin_sensor_read(target) == 0:
pass
cabin_motor(CABIN_STOP)
cabin_display(target)
print("At floor", target)
if sim_connect() != 0:
exit(1)
go_to_floor(1)
time.sleep(2)
go_to_floor(3)
time.sleep(2)
go_to_floor(2)
sim_disconnect()
Add two more functions to those of Task 22: open_door(floor) opens the door until it is fully open and stops it, close_door(floor) closes it until it is fully closed and stops it. If the door is already fully open (or fully closed) the function must not start the motor at all.
Do this tour: go to floors 1, 2 and 3 in order; at each floor open the door, keep it open for 2 seconds and close it.
A real elevator never moves while any floor door is open. Change the go_to_floor function of Task 23: before moving the cabin it checks all three doors; if a door is open (not fully closed) it prints a warning and closes that door first.
Main program: go to floor 1, wait 1 second, go to floor 3. Test: before running, click Open for the floor 2 door in Manual Control and Stop when the door is halfway.
The simulation lets the cabin move while a door is open; the program must take care of safety. Because the check is inside go_to_floor, every call that moves the cabin is safe automatically; it cannot be forgotten in the main program. In C, close_door is now used inside go_to_floor, so it is defined before it.
#include "elevator.h"
int current_floor(void)
{
for (int f = 1; f <= 3; f++)
if (cabin_sensor_read(f) == 1) return f;
return 0;
}
void open_door(int f)
{
if (door_open_read(f) == 1) return;
door(f, DOOR_OPEN);
while (door_open_read(f) == 0) { }
door(f, DOOR_STOP);
}
void close_door(int f)
{
if (door_sensor_read(f) == 0) return;
door(f, DOOR_CLOSE);
while (door_sensor_read(f) == 1) { }
door(f, DOOR_STOP);
}
void go_to_floor(int target)
{
for (int f = 1; f <= 3; f++) // safety: all doors closed
{
if (door_sensor_read(f) == 1)
{
printf("Warning: door %d is open, closing it first\n", f);
close_door(f);
}
}
if (current_floor() == 0)
{
cabin_motor(CABIN_DOWN);
while (current_floor() == 0) { }
cabin_motor(CABIN_STOP);
}
int now = current_floor();
if (target > now) cabin_motor(CABIN_UP);
else if (target < now) cabin_motor(CABIN_DOWN);
while (cabin_sensor_read(target) == 0) { }
cabin_motor(CABIN_STOP);
cabin_display(target);
printf("At floor %d\n", target);
}
int main(void)
{
if (sim_connect() != 0) return 1;
go_to_floor(1);
Sleep(1000);
go_to_floor(3);
sim_disconnect();
return 0;
}
from elevator import *
import time
def current_floor():
for f in (1, 2, 3):
if cabin_sensor_read(f) == 1:
return f
return 0
def open_door(f):
if door_open_read(f) == 1:
return
door(f, DOOR_OPEN)
while door_open_read(f) == 0:
pass
door(f, DOOR_STOP)
def close_door(f):
if door_sensor_read(f) == 0:
return
door(f, DOOR_CLOSE)
while door_sensor_read(f) == 1:
pass
door(f, DOOR_STOP)
def go_to_floor(target):
for f in (1, 2, 3): # safety: all doors closed
if door_sensor_read(f) == 1:
print("Warning: door", f, "is open, closing it first")
close_door(f)
if current_floor() == 0:
cabin_motor(CABIN_DOWN)
while current_floor() == 0:
pass
cabin_motor(CABIN_STOP)
now = current_floor()
if target > now:
cabin_motor(CABIN_UP)
elif target < now:
cabin_motor(CABIN_DOWN)
while cabin_sensor_read(target) == 0:
pass
cabin_motor(CABIN_STOP)
cabin_display(target)
print("At floor", target)
if sim_connect() != 0:
exit(1)
go_to_floor(1)
time.sleep(1)
go_to_floor(3)
sim_disconnect()
Write an elevator that runs forever. When a cabin button or a floor call button (▲ or ▼) is pressed, turn on that button's light, take the cabin to that floor, open the door, keep it open for 3 seconds, close it and turn off all button lights of that floor. Then wait for a new call. Use the functions of Task 24. Stop with Ctrl+C.
Improve the elevator of Task 25 so that several calls can wait at the same time. Store the request of every button in arrays (three arrays: cabin buttons, ▲ buttons, ▼ buttons); the light shows that the request is stored. When the elevator is idle it goes to the waiting call NEAREST to its current floor; when the door opens it clears that floor's requests and lights, and after 3 seconds it closes the door.
Test: while the elevator is idle quickly press 1 and 2 (with the cabin at floor 3 it must go to 2 first). What happens to buttons pressed while the cabin moves?
The arrays remember each button's request, and so its light; even if a button is held down, the light command is sent only once. nearest_call() walks over all floors and picks, among the floors with a request, the one with the smallest distance (the distance is the larger floor number minus the smaller one; in C condition ? a : b is a short if/else, in Python abs() gives the absolute value). Requests are cleared as soon as the door opens. While the cabin moves or the door is open the program is inside waiting loops, so presses made then are still missed; Task 27 will fix that.
#include "elevator.h"
int cab_req[4], up_req[4], dn_req[4]; // index 1..3
int current_floor(void)
{
for (int f = 1; f <= 3; f++)
if (cabin_sensor_read(f) == 1) return f;
return 0;
}
void open_door(int f)
{
if (door_open_read(f) == 1) return;
door(f, DOOR_OPEN);
while (door_open_read(f) == 0) { }
door(f, DOOR_STOP);
}
void close_door(int f)
{
if (door_sensor_read(f) == 0) return;
door(f, DOOR_CLOSE);
while (door_sensor_read(f) == 1) { }
door(f, DOOR_STOP);
}
void go_to_floor(int target)
{
for (int f = 1; f <= 3; f++)
if (door_sensor_read(f) == 1) close_door(f);
if (current_floor() == 0)
{
cabin_motor(CABIN_DOWN);
while (current_floor() == 0) { }
cabin_motor(CABIN_STOP);
}
int now = current_floor();
if (target > now) cabin_motor(CABIN_UP);
else if (target < now) cabin_motor(CABIN_DOWN);
while (cabin_sensor_read(target) == 0) { }
cabin_motor(CABIN_STOP);
cabin_display(target);
}
// Stores new presses and turns their lights on
void scan_buttons(void)
{
for (int f = 1; f <= 3; f++)
{
if (cabin_button_read(f) == 1 && cab_req[f] == 0)
{
cab_req[f] = 1;
cabin_button_light(f, 1);
}
if (floor_button_read(f, DIR_UP) == 1 && up_req[f] == 0)
{
up_req[f] = 1;
floor_button_light(f, DIR_UP, 1);
}
if (floor_button_read(f, DIR_DOWN) == 1 && dn_req[f] == 0)
{
dn_req[f] = 1;
floor_button_light(f, DIR_DOWN, 1);
}
}
}
int has_call(int f)
{
return cab_req[f] == 1 || up_req[f] == 1 || dn_req[f] == 1;
}
void clear_calls(int f)
{
if (cab_req[f] == 1) { cab_req[f] = 0; cabin_button_light(f, 0); }
if (up_req[f] == 1) { up_req[f] = 0; floor_button_light(f, DIR_UP, 0); }
if (dn_req[f] == 1) { dn_req[f] = 0; floor_button_light(f, DIR_DOWN, 0); }
}
// Floor with a call nearest to 'from', or 0 if there is no call
int nearest_call(int from)
{
int best = 0, best_dist = 99;
for (int f = 1; f <= 3; f++)
{
if (has_call(f))
{
int dist = (f > from) ? f - from : from - f;
if (dist < best_dist) { best = f; best_dist = dist; }
}
}
return best;
}
int main(void)
{
if (sim_connect() != 0) return 1;
printf("Waiting for calls. Ctrl+C to stop.\n");
while (1)
{
scan_buttons();
int target = nearest_call(current_floor());
if (target != 0)
{
printf("Going to floor %d\n", target);
go_to_floor(target);
open_door(target);
clear_calls(target);
Sleep(3000);
close_door(target);
}
Sleep(10);
}
sim_disconnect();
return 0;
}
from elevator import *
import time
cab_req = [0, 0, 0, 0] # index 1..3
up_req = [0, 0, 0, 0]
dn_req = [0, 0, 0, 0]
def current_floor():
for f in (1, 2, 3):
if cabin_sensor_read(f) == 1:
return f
return 0
def open_door(f):
if door_open_read(f) == 1:
return
door(f, DOOR_OPEN)
while door_open_read(f) == 0:
pass
door(f, DOOR_STOP)
def close_door(f):
if door_sensor_read(f) == 0:
return
door(f, DOOR_CLOSE)
while door_sensor_read(f) == 1:
pass
door(f, DOOR_STOP)
def go_to_floor(target):
for f in (1, 2, 3):
if door_sensor_read(f) == 1:
close_door(f)
if current_floor() == 0:
cabin_motor(CABIN_DOWN)
while current_floor() == 0:
pass
cabin_motor(CABIN_STOP)
now = current_floor()
if target > now:
cabin_motor(CABIN_UP)
elif target < now:
cabin_motor(CABIN_DOWN)
while cabin_sensor_read(target) == 0:
pass
cabin_motor(CABIN_STOP)
cabin_display(target)
def scan_buttons():
"""Stores new presses and turns their lights on."""
for f in (1, 2, 3):
if cabin_button_read(f) == 1 and cab_req[f] == 0:
cab_req[f] = 1
cabin_button_light(f, 1)
if floor_button_read(f, DIR_UP) == 1 and up_req[f] == 0:
up_req[f] = 1
floor_button_light(f, DIR_UP, 1)
if floor_button_read(f, DIR_DOWN) == 1 and dn_req[f] == 0:
dn_req[f] = 1
floor_button_light(f, DIR_DOWN, 1)
def has_call(f):
return cab_req[f] == 1 or up_req[f] == 1 or dn_req[f] == 1
def clear_calls(f):
if cab_req[f] == 1:
cab_req[f] = 0
cabin_button_light(f, 0)
if up_req[f] == 1:
up_req[f] = 0
floor_button_light(f, DIR_UP, 0)
if dn_req[f] == 1:
dn_req[f] = 0
floor_button_light(f, DIR_DOWN, 0)
def nearest_call(start):
"""Floor with a call nearest to 'start', or 0 if there is no call."""
best = 0
best_dist = 99
for f in (1, 2, 3):
if has_call(f):
dist = abs(f - start)
if dist < best_dist:
best = f
best_dist = dist
return best
if sim_connect() != 0:
exit(1)
print("Waiting for calls. Ctrl+C to stop.")
while True:
scan_buttons()
target = nearest_call(current_floor())
if target != 0:
print("Going to floor", target)
go_to_floor(target)
open_door(target)
clear_calls(target)
time.sleep(3)
close_door(target)
time.sleep(0.01)
In the elevator of Task 26 presses made while the cabin moved or the door was open were lost. Change the program so that buttons are also read during every wait: the pressed button's light turns on at once and the request is served when its turn comes.
Test: while the cabin goes from floor 3 to floor 1, press 2 and the ▼ button of floor 3; both lights must turn on at once and both calls must be served later.
On every floor there are up and down arrow lights that show which way the cabin is going. The library has no ready function for them; their addresses are the constants ADR_GOS_UP1, ADR_GOS_YUK2, ADR_GOS_YUK3 (up) and ADR_GOS_DWN1, ADR_GOS_AS2, ADR_GOS_AS3 (down), and they are written with write_mem(address, value).
Write the function show_arrows(direction): for CABIN_UP it turns on the up arrows on all three floors, for CABIN_DOWN the down arrows, and for CABIN_STOP it turns them all off. Inside go_to_floor of Task 27 the right arrows must be on while the cabin moves and go off when it stops.
write_mem is the lowest-level function of the library: it writes a value straight to an address in shared memory; all other functions actually do this too. With the addresses in arrays we can update the three floors in one for loop. A comparison gives 1 when true and 0 when false, so while going up the up arrows are on and the down arrows off. In Python we turn True / False into 1 / 0 with int().
#include "elevator.h"
int cab_req[4], up_req[4], dn_req[4]; // index 1..3
int up_arrow[4] = {0, ADR_GOS_UP1, ADR_GOS_YUK2, ADR_GOS_YUK3};
int dn_arrow[4] = {0, ADR_GOS_DWN1, ADR_GOS_AS2, ADR_GOS_AS3};
// CABIN_UP: up arrows on, CABIN_DOWN: down arrows on, CABIN_STOP: all off
void show_arrows(int dir)
{
for (int f = 1; f <= 3; f++)
{
write_mem(up_arrow[f], dir == CABIN_UP);
write_mem(dn_arrow[f], dir == CABIN_DOWN);
}
}
void scan_buttons(void)
{
for (int f = 1; f <= 3; f++)
{
if (cabin_button_read(f) == 1 && cab_req[f] == 0)
{
cab_req[f] = 1;
cabin_button_light(f, 1);
}
if (floor_button_read(f, DIR_UP) == 1 && up_req[f] == 0)
{
up_req[f] = 1;
floor_button_light(f, DIR_UP, 1);
}
if (floor_button_read(f, DIR_DOWN) == 1 && dn_req[f] == 0)
{
dn_req[f] = 1;
floor_button_light(f, DIR_DOWN, 1);
}
}
}
int has_call(int f)
{
return cab_req[f] == 1 || up_req[f] == 1 || dn_req[f] == 1;
}
void clear_calls(int f)
{
if (cab_req[f] == 1) { cab_req[f] = 0; cabin_button_light(f, 0); }
if (up_req[f] == 1) { up_req[f] = 0; floor_button_light(f, DIR_UP, 0); }
if (dn_req[f] == 1) { dn_req[f] = 0; floor_button_light(f, DIR_DOWN, 0); }
}
int nearest_call(int from)
{
int best = 0, best_dist = 99;
for (int f = 1; f <= 3; f++)
{
if (has_call(f))
{
int dist = (f > from) ? f - from : from - f;
if (dist < best_dist) { best = f; best_dist = dist; }
}
}
return best;
}
void wait_ms(int ms)
{
DWORD start = GetTickCount(); // clock in milliseconds
while (GetTickCount() - start < (DWORD)ms)
{
scan_buttons();
Sleep(10);
}
}
int current_floor(void)
{
for (int f = 1; f <= 3; f++)
if (cabin_sensor_read(f) == 1) return f;
return 0;
}
void open_door(int f)
{
if (door_open_read(f) == 1) return;
door(f, DOOR_OPEN);
while (door_open_read(f) == 0) scan_buttons();
door(f, DOOR_STOP);
}
void close_door(int f)
{
if (door_sensor_read(f) == 0) return;
door(f, DOOR_CLOSE);
while (door_sensor_read(f) == 1) scan_buttons();
door(f, DOOR_STOP);
}
void go_to_floor(int target)
{
for (int f = 1; f <= 3; f++)
if (door_sensor_read(f) == 1) close_door(f);
if (current_floor() == 0)
{
show_arrows(CABIN_DOWN);
cabin_motor(CABIN_DOWN);
while (current_floor() == 0) scan_buttons();
cabin_motor(CABIN_STOP);
}
int now = current_floor();
if (target > now)
{
show_arrows(CABIN_UP);
cabin_motor(CABIN_UP);
}
else if (target < now)
{
show_arrows(CABIN_DOWN);
cabin_motor(CABIN_DOWN);
}
while (cabin_sensor_read(target) == 0) scan_buttons();
cabin_motor(CABIN_STOP);
show_arrows(CABIN_STOP);
cabin_display(target);
}
int main(void)
{
if (sim_connect() != 0) return 1;
show_arrows(CABIN_STOP);
printf("Waiting for calls. Ctrl+C to stop.\n");
while (1)
{
scan_buttons();
int target = nearest_call(current_floor());
if (target != 0)
{
printf("Going to floor %d\n", target);
go_to_floor(target);
open_door(target);
clear_calls(target);
wait_ms(3000);
close_door(target);
}
Sleep(10);
}
sim_disconnect();
return 0;
}
from elevator import *
import time
cab_req = [0, 0, 0, 0] # index 1..3
up_req = [0, 0, 0, 0]
dn_req = [0, 0, 0, 0]
up_arrow = [0, ADR_GOS_UP1, ADR_GOS_YUK2, ADR_GOS_YUK3]
dn_arrow = [0, ADR_GOS_DWN1, ADR_GOS_AS2, ADR_GOS_AS3]
def show_arrows(direction):
"""CABIN_UP: up arrows on, CABIN_DOWN: down arrows on, CABIN_STOP: all off."""
for f in (1, 2, 3):
write_mem(up_arrow[f], int(direction == CABIN_UP))
write_mem(dn_arrow[f], int(direction == CABIN_DOWN))
def scan_buttons():
for f in (1, 2, 3):
if cabin_button_read(f) == 1 and cab_req[f] == 0:
cab_req[f] = 1
cabin_button_light(f, 1)
if floor_button_read(f, DIR_UP) == 1 and up_req[f] == 0:
up_req[f] = 1
floor_button_light(f, DIR_UP, 1)
if floor_button_read(f, DIR_DOWN) == 1 and dn_req[f] == 0:
dn_req[f] = 1
floor_button_light(f, DIR_DOWN, 1)
def has_call(f):
return cab_req[f] == 1 or up_req[f] == 1 or dn_req[f] == 1
def clear_calls(f):
if cab_req[f] == 1:
cab_req[f] = 0
cabin_button_light(f, 0)
if up_req[f] == 1:
up_req[f] = 0
floor_button_light(f, DIR_UP, 0)
if dn_req[f] == 1:
dn_req[f] = 0
floor_button_light(f, DIR_DOWN, 0)
def nearest_call(start):
best = 0
best_dist = 99
for f in (1, 2, 3):
if has_call(f):
dist = abs(f - start)
if dist < best_dist:
best = f
best_dist = dist
return best
def wait_ms(ms):
start = time.monotonic() # clock in seconds
while time.monotonic() - start < ms / 1000:
scan_buttons()
time.sleep(0.01)
def current_floor():
for f in (1, 2, 3):
if cabin_sensor_read(f) == 1:
return f
return 0
def open_door(f):
if door_open_read(f) == 1:
return
door(f, DOOR_OPEN)
while door_open_read(f) == 0:
scan_buttons()
door(f, DOOR_STOP)
def close_door(f):
if door_sensor_read(f) == 0:
return
door(f, DOOR_CLOSE)
while door_sensor_read(f) == 1:
scan_buttons()
door(f, DOOR_STOP)
def go_to_floor(target):
for f in (1, 2, 3):
if door_sensor_read(f) == 1:
close_door(f)
if current_floor() == 0:
show_arrows(CABIN_DOWN)
cabin_motor(CABIN_DOWN)
while current_floor() == 0:
scan_buttons()
cabin_motor(CABIN_STOP)
now = current_floor()
if target > now:
show_arrows(CABIN_UP)
cabin_motor(CABIN_UP)
elif target < now:
show_arrows(CABIN_DOWN)
cabin_motor(CABIN_DOWN)
while cabin_sensor_read(target) == 0:
scan_buttons()
cabin_motor(CABIN_STOP)
show_arrows(CABIN_STOP)
cabin_display(target)
if sim_connect() != 0:
exit(1)
show_arrows(CABIN_STOP)
print("Waiting for calls. Ctrl+C to stop.")
while True:
scan_buttons()
target = nearest_call(current_floor())
if target != 0:
print("Going to floor", target)
go_to_floor(target)
open_door(target)
clear_calls(target)
wait_ms(3000)
close_door(target)
time.sleep(0.01)
Rewrite the elevator of Task 28 without waiting loops, using a single main loop and a state variable. States: IDLE, MOVING, OPENING (door opening), OPEN (door open), CLOSING (door closing). Each round the main loop reads the buttons and does only the job of the current state. Measure the 3 seconds the door stays open by reading the clock, not with Sleep.
The behaviour must be the same as in Task 28: go to the nearest call, open the door, clear the requests, wait 3 seconds, close; while moving the arrows and the display must be right. At the start, close all doors first and, if the cabin is between floors, bring it down to the floor below.
In a state machine the program never gets stuck waiting; each round it asks 'what must I do in the current state, and should I move to the next state?'. So reading buttons, watching sensors and measuring time all run together in the same loop, and adding a new feature becomes easy. opened_at keeps the moment the door opened; the elapsed time is the difference to the current clock. The loop sleeps 10 ms only in the IDLE and OPEN states; in the moving and door states it watches the sensors without pause. In the MOVING state the display is updated at every floor passed.
#include "elevator.h"
#define IDLE 0
#define MOVING 1
#define OPENING 2
#define OPEN 3
#define CLOSING 4
int cab_req[4], up_req[4], dn_req[4]; // index 1..3
int up_arrow[4] = {0, ADR_GOS_UP1, ADR_GOS_YUK2, ADR_GOS_YUK3};
int dn_arrow[4] = {0, ADR_GOS_DWN1, ADR_GOS_AS2, ADR_GOS_AS3};
void show_arrows(int dir)
{
for (int f = 1; f <= 3; f++)
{
write_mem(up_arrow[f], dir == CABIN_UP);
write_mem(dn_arrow[f], dir == CABIN_DOWN);
}
}
void scan_buttons(void)
{
for (int f = 1; f <= 3; f++)
{
if (cabin_button_read(f) == 1 && cab_req[f] == 0)
{
cab_req[f] = 1;
cabin_button_light(f, 1);
}
if (floor_button_read(f, DIR_UP) == 1 && up_req[f] == 0)
{
up_req[f] = 1;
floor_button_light(f, DIR_UP, 1);
}
if (floor_button_read(f, DIR_DOWN) == 1 && dn_req[f] == 0)
{
dn_req[f] = 1;
floor_button_light(f, DIR_DOWN, 1);
}
}
}
int has_call(int f)
{
return cab_req[f] == 1 || up_req[f] == 1 || dn_req[f] == 1;
}
void clear_calls(int f)
{
if (cab_req[f] == 1) { cab_req[f] = 0; cabin_button_light(f, 0); }
if (up_req[f] == 1) { up_req[f] = 0; floor_button_light(f, DIR_UP, 0); }
if (dn_req[f] == 1) { dn_req[f] = 0; floor_button_light(f, DIR_DOWN, 0); }
}
int nearest_call(int from)
{
int best = 0, best_dist = 99;
for (int f = 1; f <= 3; f++)
{
if (has_call(f))
{
int dist = (f > from) ? f - from : from - f;
if (dist < best_dist) { best = f; best_dist = dist; }
}
}
return best;
}
int current_floor(void)
{
for (int f = 1; f <= 3; f++)
if (cabin_sensor_read(f) == 1) return f;
return 0;
}
void close_door(int f) // used only at start-up
{
if (door_sensor_read(f) == 0) return;
door(f, DOOR_CLOSE);
while (door_sensor_read(f) == 1) { }
door(f, DOOR_STOP);
}
int main(void)
{
if (sim_connect() != 0) return 1;
// Safe start: doors closed, cabin at a floor
cabin_motor(CABIN_STOP);
for (int f = 1; f <= 3; f++) close_door(f);
while (current_floor() == 0) cabin_motor(CABIN_DOWN);
cabin_motor(CABIN_STOP);
show_arrows(CABIN_STOP);
int floor = current_floor();
int target = 0;
int state = IDLE;
DWORD opened_at = 0;
cabin_display(floor);
printf("Running. Ctrl+C to stop.\n");
while (1)
{
scan_buttons();
if (state == IDLE)
{
target = nearest_call(floor);
if (target == floor)
{
door(floor, DOOR_OPEN);
state = OPENING;
}
else if (target > floor)
{
show_arrows(CABIN_UP);
cabin_motor(CABIN_UP);
state = MOVING;
}
else if (target != 0 && target < floor)
{
show_arrows(CABIN_DOWN);
cabin_motor(CABIN_DOWN);
state = MOVING;
}
}
else if (state == MOVING)
{
int f = current_floor();
if (f != 0 && f != floor) // reached a new floor
{
floor = f;
cabin_display(floor);
}
if (floor == target)
{
cabin_motor(CABIN_STOP);
show_arrows(CABIN_STOP);
door(floor, DOOR_OPEN);
state = OPENING;
}
}
else if (state == OPENING)
{
if (door_open_read(floor) == 1)
{
door(floor, DOOR_STOP);
clear_calls(floor);
opened_at = GetTickCount();
state = OPEN;
}
}
else if (state == OPEN)
{
if (GetTickCount() - opened_at >= 3000)
{
door(floor, DOOR_CLOSE);
state = CLOSING;
}
}
else if (state == CLOSING)
{
if (door_sensor_read(floor) == 0)
{
door(floor, DOOR_STOP);
state = IDLE;
}
}
if (state == IDLE || state == OPEN) Sleep(10); // no motor to stop
}
sim_disconnect();
return 0;
}
from elevator import *
import time
IDLE, MOVING, OPENING, OPEN, CLOSING = 0, 1, 2, 3, 4
cab_req = [0, 0, 0, 0] # index 1..3
up_req = [0, 0, 0, 0]
dn_req = [0, 0, 0, 0]
up_arrow = [0, ADR_GOS_UP1, ADR_GOS_YUK2, ADR_GOS_YUK3]
dn_arrow = [0, ADR_GOS_DWN1, ADR_GOS_AS2, ADR_GOS_AS3]
def show_arrows(direction):
for f in (1, 2, 3):
write_mem(up_arrow[f], int(direction == CABIN_UP))
write_mem(dn_arrow[f], int(direction == CABIN_DOWN))
def scan_buttons():
for f in (1, 2, 3):
if cabin_button_read(f) == 1 and cab_req[f] == 0:
cab_req[f] = 1
cabin_button_light(f, 1)
if floor_button_read(f, DIR_UP) == 1 and up_req[f] == 0:
up_req[f] = 1
floor_button_light(f, DIR_UP, 1)
if floor_button_read(f, DIR_DOWN) == 1 and dn_req[f] == 0:
dn_req[f] = 1
floor_button_light(f, DIR_DOWN, 1)
def has_call(f):
return cab_req[f] == 1 or up_req[f] == 1 or dn_req[f] == 1
def clear_calls(f):
if cab_req[f] == 1:
cab_req[f] = 0
cabin_button_light(f, 0)
if up_req[f] == 1:
up_req[f] = 0
floor_button_light(f, DIR_UP, 0)
if dn_req[f] == 1:
dn_req[f] = 0
floor_button_light(f, DIR_DOWN, 0)
def nearest_call(start):
best = 0
best_dist = 99
for f in (1, 2, 3):
if has_call(f):
dist = abs(f - start)
if dist < best_dist:
best = f
best_dist = dist
return best
def current_floor():
for f in (1, 2, 3):
if cabin_sensor_read(f) == 1:
return f
return 0
def close_door(f): # used only at start-up
if door_sensor_read(f) == 0:
return
door(f, DOOR_CLOSE)
while door_sensor_read(f) == 1:
pass
door(f, DOOR_STOP)
if sim_connect() != 0:
exit(1)
# Safe start: doors closed, cabin at a floor
cabin_motor(CABIN_STOP)
for f in (1, 2, 3):
close_door(f)
while current_floor() == 0:
cabin_motor(CABIN_DOWN)
cabin_motor(CABIN_STOP)
show_arrows(CABIN_STOP)
floor = current_floor()
target = 0
state = IDLE
opened_at = 0.0
cabin_display(floor)
print("Running. Ctrl+C to stop.")
while True:
scan_buttons()
if state == IDLE:
target = nearest_call(floor)
if target == floor:
door(floor, DOOR_OPEN)
state = OPENING
elif target > floor:
show_arrows(CABIN_UP)
cabin_motor(CABIN_UP)
state = MOVING
elif target != 0 and target < floor:
show_arrows(CABIN_DOWN)
cabin_motor(CABIN_DOWN)
state = MOVING
elif state == MOVING:
f = current_floor()
if f != 0 and f != floor: # reached a new floor
floor = f
cabin_display(floor)
if floor == target:
cabin_motor(CABIN_STOP)
show_arrows(CABIN_STOP)
door(floor, DOOR_OPEN)
state = OPENING
elif state == OPENING:
if door_open_read(floor) == 1:
door(floor, DOOR_STOP)
clear_calls(floor)
opened_at = time.monotonic()
state = OPEN
elif state == OPEN:
if time.monotonic() - opened_at >= 3.0:
door(floor, DOOR_CLOSE)
state = CLOSING
elif state == CLOSING:
if door_sensor_read(floor) == 0:
door(floor, DOOR_STOP)
state = IDLE
if state == IDLE or state == OPEN: # no motor to stop
time.sleep(0.01)
Make the state machine of Task 29 work like real elevators:
• While the elevator is going in one direction, it keeps that direction as long as there are calls waiting in that direction; when there are none left, it turns to the other direction.
• On the way it stops at every floor it passes that has a call (e.g. going from floor 1 to floor 3 with a call on floor 2, it stops at floor 2 first).
• At every stop it opens the door, clears that floor's requests and closes the door after 3 seconds. The display and the arrows are always right; the cabin never moves with a door open.
Test: with the cabin at floor 1 press cabin button 3; before the cabin reaches floor 2 press the ▼ button on floor 2. The cabin must stop at floor 2 first and then at floor 3.
This is a simple version of the collective control used in real elevators. choose_direction() first checks whether there are calls in the old direction, so the elevator does not drive passengers back and forth. In MOVING a decision is made at every new floor: if it has a call, or if it is floor 1 or 3 (there is nothing beyond), the cabin stops and the state becomes IDLE. IDLE opens the door if there is a call at the current floor; otherwise it chooses a new direction. The door opens only while the cabin stands, and the cabin moves only from IDLE, that is with the door fully closed; safety comes from the structure of the state machine.
#include "elevator.h"
#define IDLE 0
#define MOVING 1
#define OPENING 2
#define OPEN 3
#define CLOSING 4
int cab_req[4], up_req[4], dn_req[4]; // index 1..3
int up_arrow[4] = {0, ADR_GOS_UP1, ADR_GOS_YUK2, ADR_GOS_YUK3};
int dn_arrow[4] = {0, ADR_GOS_DWN1, ADR_GOS_AS2, ADR_GOS_AS3};
void show_arrows(int dir)
{
for (int f = 1; f <= 3; f++)
{
write_mem(up_arrow[f], dir == CABIN_UP);
write_mem(dn_arrow[f], dir == CABIN_DOWN);
}
}
void scan_buttons(void)
{
for (int f = 1; f <= 3; f++)
{
if (cabin_button_read(f) == 1 && cab_req[f] == 0)
{
cab_req[f] = 1;
cabin_button_light(f, 1);
}
if (floor_button_read(f, DIR_UP) == 1 && up_req[f] == 0)
{
up_req[f] = 1;
floor_button_light(f, DIR_UP, 1);
}
if (floor_button_read(f, DIR_DOWN) == 1 && dn_req[f] == 0)
{
dn_req[f] = 1;
floor_button_light(f, DIR_DOWN, 1);
}
}
}
int has_call(int f)
{
return cab_req[f] == 1 || up_req[f] == 1 || dn_req[f] == 1;
}
void clear_calls(int f)
{
if (cab_req[f] == 1) { cab_req[f] = 0; cabin_button_light(f, 0); }
if (up_req[f] == 1) { up_req[f] = 0; floor_button_light(f, DIR_UP, 0); }
if (dn_req[f] == 1) { dn_req[f] = 0; floor_button_light(f, DIR_DOWN, 0); }
}
int calls_above(int floor)
{
for (int f = floor + 1; f <= 3; f++)
if (has_call(f)) return 1;
return 0;
}
int calls_below(int floor)
{
for (int f = floor - 1; f >= 1; f--)
if (has_call(f)) return 1;
return 0;
}
// Keep the old direction while it has calls, else turn, else stop
int choose_direction(int floor, int old_dir)
{
if (old_dir == CABIN_UP && calls_above(floor)) return CABIN_UP;
if (old_dir == CABIN_DOWN && calls_below(floor)) return CABIN_DOWN;
if (calls_above(floor)) return CABIN_UP;
if (calls_below(floor)) return CABIN_DOWN;
return CABIN_STOP;
}
int current_floor(void)
{
for (int f = 1; f <= 3; f++)
if (cabin_sensor_read(f) == 1) return f;
return 0;
}
void close_door(int f) // used only at start-up
{
if (door_sensor_read(f) == 0) return;
door(f, DOOR_CLOSE);
while (door_sensor_read(f) == 1) { }
door(f, DOOR_STOP);
}
int main(void)
{
if (sim_connect() != 0) return 1;
cabin_motor(CABIN_STOP);
for (int f = 1; f <= 3; f++) close_door(f);
while (current_floor() == 0) cabin_motor(CABIN_DOWN);
cabin_motor(CABIN_STOP);
show_arrows(CABIN_STOP);
int floor = current_floor();
int dir = CABIN_STOP; // last travel direction
int state = IDLE;
DWORD opened_at = 0;
cabin_display(floor);
printf("Elevator running. Ctrl+C to stop.\n");
while (1)
{
scan_buttons();
if (state == IDLE)
{
if (has_call(floor))
{
door(floor, DOOR_OPEN);
state = OPENING;
}
else
{
dir = choose_direction(floor, dir);
if (dir != CABIN_STOP)
{
show_arrows(dir);
cabin_motor(dir);
state = MOVING;
}
}
}
else if (state == MOVING)
{
int f = current_floor();
if (f != 0 && f != floor) // reached a new floor
{
floor = f;
cabin_display(floor);
if (has_call(floor) || floor == 1 || floor == 3)
{
cabin_motor(CABIN_STOP);
show_arrows(CABIN_STOP);
state = IDLE; // IDLE opens the door if needed
}
}
}
else if (state == OPENING)
{
if (door_open_read(floor) == 1)
{
door(floor, DOOR_STOP);
clear_calls(floor);
opened_at = GetTickCount();
state = OPEN;
}
}
else if (state == OPEN)
{
if (GetTickCount() - opened_at >= 3000)
{
door(floor, DOOR_CLOSE);
state = CLOSING;
}
}
else if (state == CLOSING)
{
if (door_sensor_read(floor) == 0)
{
door(floor, DOOR_STOP);
state = IDLE;
}
}
if (state == IDLE || state == OPEN) Sleep(10);
}
sim_disconnect();
return 0;
}
from elevator import *
import time
IDLE, MOVING, OPENING, OPEN, CLOSING = 0, 1, 2, 3, 4
cab_req = [0, 0, 0, 0] # index 1..3
up_req = [0, 0, 0, 0]
dn_req = [0, 0, 0, 0]
up_arrow = [0, ADR_GOS_UP1, ADR_GOS_YUK2, ADR_GOS_YUK3]
dn_arrow = [0, ADR_GOS_DWN1, ADR_GOS_AS2, ADR_GOS_AS3]
def show_arrows(direction):
for f in (1, 2, 3):
write_mem(up_arrow[f], int(direction == CABIN_UP))
write_mem(dn_arrow[f], int(direction == CABIN_DOWN))
def scan_buttons():
for f in (1, 2, 3):
if cabin_button_read(f) == 1 and cab_req[f] == 0:
cab_req[f] = 1
cabin_button_light(f, 1)
if floor_button_read(f, DIR_UP) == 1 and up_req[f] == 0:
up_req[f] = 1
floor_button_light(f, DIR_UP, 1)
if floor_button_read(f, DIR_DOWN) == 1 and dn_req[f] == 0:
dn_req[f] = 1
floor_button_light(f, DIR_DOWN, 1)
def has_call(f):
return cab_req[f] == 1 or up_req[f] == 1 or dn_req[f] == 1
def clear_calls(f):
if cab_req[f] == 1:
cab_req[f] = 0
cabin_button_light(f, 0)
if up_req[f] == 1:
up_req[f] = 0
floor_button_light(f, DIR_UP, 0)
if dn_req[f] == 1:
dn_req[f] = 0
floor_button_light(f, DIR_DOWN, 0)
def calls_above(floor):
for f in range(floor + 1, 4):
if has_call(f):
return True
return False
def calls_below(floor):
for f in range(floor - 1, 0, -1):
if has_call(f):
return True
return False
def choose_direction(floor, old_dir):
"""Keep the old direction while it has calls, else turn, else stop."""
if old_dir == CABIN_UP and calls_above(floor):
return CABIN_UP
if old_dir == CABIN_DOWN and calls_below(floor):
return CABIN_DOWN
if calls_above(floor):
return CABIN_UP
if calls_below(floor):
return CABIN_DOWN
return CABIN_STOP
def current_floor():
for f in (1, 2, 3):
if cabin_sensor_read(f) == 1:
return f
return 0
def close_door(f): # used only at start-up
if door_sensor_read(f) == 0:
return
door(f, DOOR_CLOSE)
while door_sensor_read(f) == 1:
pass
door(f, DOOR_STOP)
if sim_connect() != 0:
exit(1)
cabin_motor(CABIN_STOP)
for f in (1, 2, 3):
close_door(f)
while current_floor() == 0:
cabin_motor(CABIN_DOWN)
cabin_motor(CABIN_STOP)
show_arrows(CABIN_STOP)
floor = current_floor()
direction = CABIN_STOP # last travel direction
state = IDLE
opened_at = 0.0
cabin_display(floor)
print("Elevator running. Ctrl+C to stop.")
while True:
scan_buttons()
if state == IDLE:
if has_call(floor):
door(floor, DOOR_OPEN)
state = OPENING
else:
direction = choose_direction(floor, direction)
if direction != CABIN_STOP:
show_arrows(direction)
cabin_motor(direction)
state = MOVING
elif state == MOVING:
f = current_floor()
if f != 0 and f != floor: # reached a new floor
floor = f
cabin_display(floor)
if has_call(floor) or floor == 1 or floor == 3:
cabin_motor(CABIN_STOP)
show_arrows(CABIN_STOP)
state = IDLE # IDLE opens the door if needed
elif state == OPENING:
if door_open_read(floor) == 1:
door(floor, DOOR_STOP)
clear_calls(floor)
opened_at = time.monotonic()
state = OPEN
elif state == OPEN:
if time.monotonic() - opened_at >= 3.0:
door(floor, DOOR_CLOSE)
state = CLOSING
elif state == CLOSING:
if door_sensor_read(floor) == 0:
door(floor, DOOR_STOP)
state = IDLE
if state == IDLE or state == OPEN:
time.sleep(0.01)
In real elevators, if someone presses a button of the same floor while the door is closing, the door opens again. Add this to the controller of Task 30:
• If a button of the current floor is pressed while the door is open (OPEN), the request is cleared at once and the 3 second time starts again.
• If a button of the current floor is pressed while the door is closing (CLOSING), the door starts opening again at once.
Test: while the cabin is closing its door at a floor, press that floor's ▲ button or the cabin button of that floor.
Most people enter the building at the ground floor (floor 1). Add this to the controller of Task 30: if the elevator stays IDLE for 10 seconds without any call and it is not at floor 1, it goes down to floor 1 without opening its door and waits there. If a call comes meanwhile, it is served as usual.
Test: start the program after Restart Sim and press nothing; after about 10 seconds the cabin must go down to floor 1.
A safe controller must notice when something goes wrong and stop everything. Add this to the controller of Task 30:
• At the start, wait until an old error flag (if any) has cleared.
• In every round of the main loop, if get_error() is 1, do an emergency stop.
• If the moving cabin does not reach a new floor within 7 seconds (normally a floor takes about 4.7 seconds), treat it as a fault too and do an emergency stop.
Emergency stop: stop the cabin motor and the three door motors, turn the arrows off, show 0 on the display, print the reason and end the program.
Test 1: while the cabin moves, click Stop for the cabin motor in Manual Control (timeout after 7 seconds). Test 2: while the program runs, click Open in Manual Control for the door of a floor where the cabin is not, and wait (error when the door is fully open and keeps going).
To measure how well an elevator works, add statistics to the controller of Task 30:
• how many stops opened the door (number of services),
• how many floors the cabin travelled in total,
• for each floor, the waiting time from the first button press until the door at that floor is fully open, and the average of these times.
After 5 services, once the door has closed and the elevator is idle, print these three values (the average waiting time in seconds, with one decimal), turn the arrows off and end the program cleanly.