Write a program that connects to the simulation, prints Hello room! and closes the connection.
Start the simulation first, then run the program. In the console you should also see the library messages [SIM] Connected and [SIM] Disconnected.
Then close the simulation and run the program again: it cannot connect, so it must end with an error message.
Every program starts with the same skeleton: include the library, connect with sim_connect(), do the work, leave with sim_disconnect().
If the simulation is not running, sim_connect() returns 1. There is no point in going on, so the program ends with code 1. You will use this skeleton in all the following tasks.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1; // simulation not running
printf("Hello room!\n");
sim_disconnect();
return 0;
}
from room import *
if sim_connect() != 0:
exit(1) # simulation not running
print("Hello room!")
sim_disconnect()
Write a program that turns the room lamp on. The program ends right after that.
Does the lamp stay on after the program has ended? Observe it. Then turn the lamp off with the Light Off button in the manual control panel.
lamp(LAMP_ON) writes 1 into the shared memory; the simulation keeps reading this value and draws the light. The value is not erased when the program ends, so the lamp stays on. The buttons of the manual control panel write into the same memory.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
lamp(LAMP_ON); // the light appears in the room
sim_disconnect();
return 0;
}
from room import *
if sim_connect() != 0:
exit(1)
lamp(LAMP_ON) # the light appears in the room
sim_disconnect()
Turn the lamp on, wait 2 seconds and turn it off. The lamp should be visible for 2 seconds and stay off when the program ends.
The computer runs commands very fast. Without a wait, the lamp would go on and off within microseconds and you would see nothing. Sleep(2000) / time.sleep(2) pauses the program for 2 seconds; the lamp stays on meanwhile.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
lamp(LAMP_ON);
Sleep(2000); // 2000 ms = 2 s
lamp(LAMP_OFF);
sim_disconnect();
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
lamp(LAMP_ON)
time.sleep(2) # 2 seconds
lamp(LAMP_OFF)
sim_disconnect()
Write a program that follows this order:
1. Turn the lamp on.
2. After 3 seconds turn the AC on (the AC wind appears in the room).
3. After 5 more seconds turn both the lamp and the AC off.
Watch the temperature display while the AC is on: it starts dropping after about 1 second.
The program runs line by line from top to bottom; this is called sequential flow. ac(AC_ON) runs 3 seconds after the lamp because it is not reached until the Sleep(3000) before it has finished.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
lamp(LAMP_ON);
Sleep(3000);
ac(AC_ON); // wind appears, room starts cooling
Sleep(5000);
lamp(LAMP_OFF);
ac(AC_OFF);
sim_disconnect();
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
lamp(LAMP_ON)
time.sleep(3)
ac(AC_ON) # wind appears, room starts cooling
time.sleep(5)
lamp(LAMP_OFF)
ac(AC_OFF)
sim_disconnect()
Start with the curtain closed (at the bottom). Run the curtain up, wait 6 seconds, then run it down and end the program.
The curtain must stop by itself at the top; after the program ends it must keep moving down and stop at the bottom.
curtain() does not move the curtain instantly; it runs the motor in one direction. The motor turns until the curtain reaches the end and then stops by itself. Because the command stays in memory, the curtain keeps going down after the program ends. The 6 second wait is long enough for the 4.5 second full opening.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
curtain(CURTAIN_UP); // motor stops by itself at the top
Sleep(6000);
curtain(CURTAIN_DOWN); // keeps moving after the program ends
sim_disconnect();
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
curtain(CURTAIN_UP) # motor stops by itself at the top
time.sleep(6)
curtain(CURTAIN_DOWN) # keeps moving after the program ends
sim_disconnect()
Start with the curtain closed. Run the curtain up for 2 seconds and stop it. The curtain should stay at about half of the window.
Then try 1 second and 3 seconds and compare (close the curtain with the Curtain Down button before each try).
We cannot read the curtain position, but we decide how long the motor runs. A full opening takes about 4.5 seconds, so 2 seconds is roughly half. Setting a position by time is simple but not exact.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
curtain(CURTAIN_UP);
Sleep(2000); // about half of the 4.5 s full travel
curtain(CURTAIN_STOP);
sim_disconnect();
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
curtain(CURTAIN_UP)
time.sleep(2) # about half of the 4.5 s full travel
curtain(CURTAIN_STOP)
sim_disconnect()
Start with the curtain closed. Write a morning scenario that prints what it does before each step:
1. Print Good morning!, turn the lamp on, wait 2 seconds.
2. Print Opening the curtain, run the curtain up, wait 5 seconds.
3. Print Fresh air, turn the lamp off, turn the AC on, wait 3 seconds.
4. Turn the AC off and print Ready for the day.
Read the room temperature into a variable and print it as Temperature: 24 C.
Run the program a few times and compare the value with the temperature display in the simulation. Then press AC On and run it again.
A variable is a named box that stores a value. read_temp() returns the current temperature and we put it into the variable t. In C %d places an integer inside the text; in Python {t} does the same. Because the temperature keeps changing, you may see a different value on each run.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
int t = read_temp();
printf("Temperature: %d C\n", t);
sim_disconnect();
return 0;
}
from room import *
if sim_connect() != 0:
exit(1)
t = read_temp()
print(f"Temperature: {t} C")
sim_disconnect()
Write a report program that reads all values of the room and prints them one per line:
Temperature: .., Lux: .., AC: .., Lamp: .., Curtain motor: ..
In the manual control panel turn the lamp on, set the Change Daylight slider to 500, run the curtain up and start the program while the curtain is moving. The numbers in the report must show your changes.
Temperature and lux are sensor values measured by the simulation. read_ac(), read_lamp() and read_curtain() show what was last written to the outputs (by your program or by the panel buttons). If the lamp is on you see 1; if the curtain is moving up 1, if it is stopped 0.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
printf("Temperature: %d C\n", read_temp());
printf("Lux: %d\n", read_lux());
printf("AC: %d\n", read_ac());
printf("Lamp: %d\n", read_lamp());
printf("Curtain motor: %d\n", read_curtain());
sim_disconnect();
return 0;
}
from room import *
if sim_connect() != 0:
exit(1)
print(f"Temperature: {read_temp()} C")
print(f"Lux: {read_lux()}")
print(f"AC: {read_ac()}")
print(f"Lamp: {read_lamp()}")
print(f"Curtain motor: {read_curtain()}")
sim_disconnect()
Read the outdoor light. If it is less than 100 lux, turn the lamp on and print Dark (.. lux): lamp on; otherwise turn the lamp off and print Bright (.. lux): lamp off.
Test: set the Change Daylight slider to 50 and run the program, then set it to 800 and run it again.
if lets the program decide: if the condition is true the first block runs, otherwise the else block; never both. 100 lux is a threshold; change it to decide when the lamp turns on.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
int lux = read_lux();
if (lux < 100) {
lamp(LAMP_ON);
printf("Dark (%d lux): lamp on\n", lux);
} else {
lamp(LAMP_OFF);
printf("Bright (%d lux): lamp off\n", lux);
}
sim_disconnect();
return 0;
}
from room import *
if sim_connect() != 0:
exit(1)
lux = read_lux()
if lux < 100:
lamp(LAMP_ON)
print(f"Dark ({lux} lux): lamp on")
else:
lamp(LAMP_OFF)
print(f"Bright ({lux} lux): lamp off")
sim_disconnect()
Read the temperature. If it is higher than 26 °C, turn the AC on and print Hot (.. C): AC on; otherwise turn the AC off and print Not hot (.. C): AC off.
Test: with the AC off the room heats up within a few seconds; run the program and the AC must turn on. Then wait 5 seconds (the room cools) and run it again: the AC must turn off.
Read the curtain motor state and show it as text instead of a number: 0 → Curtain stopped, 1 → Curtain moving up, 2 → Curtain moving down, any other value → Unknown value.
Test: run it while the curtain is still; then press Curtain Up (or Curtain Down) and run it right away while the curtain is moving.
An else if chain tries the conditions from top to bottom and runs the first true one; if none is true, the final else runs. Constants such as CURTAIN_UP make the code readable: c == CURTAIN_UP says what you mean, unlike c == 1. = is assignment, == is comparison.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
int c = read_curtain();
if (c == CURTAIN_STOP) {
printf("Curtain stopped\n");
} else if (c == CURTAIN_UP) {
printf("Curtain moving up\n");
} else if (c == CURTAIN_DOWN) {
printf("Curtain moving down\n");
} else {
printf("Unknown value: %d\n", c);
}
sim_disconnect();
return 0;
}
from room import *
if sim_connect() != 0:
exit(1)
c = read_curtain()
if c == CURTAIN_STOP:
print("Curtain stopped")
elif c == CURTAIN_UP:
print("Curtain moving up")
elif c == CURTAIN_DOWN:
print("Curtain moving down")
else:
print(f"Unknown value: {c}")
sim_disconnect()
If the lamp is on AND it is brighter than 600 lux outside, turn the lamp off and print Lamp was on in daylight: turned off. Otherwise send no command and print Nothing to do.
Test: press Light On, set the light slider to 800 and run the program (the lamp must go off). Then set the slider to 300, turn the lamp on and run it again (the lamp must stay on).
Improve Task 11: before sending a command, check the AC state with read_ac().
• If the temperature is higher than 26 °C: if the AC is already on print AC already on, otherwise turn it on and print AC turned on.
• Otherwise: if the AC is already off print AC already off, otherwise turn it off and print AC turned off.
Test: set Animation Speed in the menu to the lowest value. While the room is hot, run the program twice in a row: first you should see AC turned on, then AC already on.
Blink the lamp 5 times: each time 1 second on, 1 second off. On each blink print Blink 1, Blink 2 ... Blink 5.
Use a loop instead of copying the code 5 times.
The block inside the for loop runs once for each value of the counter i from 1 to 5. To change the number of blinks you change one number. In Python range(1, 6) gives 1, 2, 3, 4, 5 (6 is not included).
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
for (int i = 1; i <= 5; i++) {
printf("Blink %d\n", i);
lamp(LAMP_ON);
Sleep(1000);
lamp(LAMP_OFF);
Sleep(1000);
}
sim_disconnect();
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
for i in range(1, 6):
print(f"Blink {i}")
lamp(LAMP_ON)
time.sleep(1)
lamp(LAMP_OFF)
time.sleep(1)
sim_disconnect()
Start with the curtain closed. Count down from 10 to 1, printing one number every second. When the count ends, print Go!, turn the lamp on and run the curtain up.
Let the room heat up with the AC off, then run the program. The program turns the AC on, takes 20 readings every 200 ms and prints each as 1: 44 C. At the end it turns the AC off and prints the lowest and highest reading: Lowest: .. C, highest: .. C.
First run the curtain down and wait until the motor stops (the curtain is fully closed). Then run the curtain up and increase a counter every 100 ms until the motor stops by itself. At the end print Opened in about .. ms (you should see about 4500 ms).
Do not wait a fixed time; find out that the motor has stopped with read_curtain().
Put Task 10 into a loop that never ends: every 100 ms read the outdoor light; if it is less than 100 lux turn the lamp on, otherwise turn it off.
Run the program and move the light slider between 50 and 500; the lamp must follow by itself. Watch the console as well: what do you see? Stop the program with Ctrl+C.
Control programs are usually endless loops: read → decide → act → wait a little → again. The 100 ms wait does not waste the CPU but still reacts quickly to changes.
The console fills with a [SIM] Lamp -> ... line every 100 ms: the program sends the command again and again even when the lamp is already right. We fix this in Task 20.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
while (1) { // stop with Ctrl+C
if (read_lux() < 100) {
lamp(LAMP_ON);
} else {
lamp(LAMP_OFF);
}
Sleep(100);
}
sim_disconnect(); // not reached, see Task 29
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
while True: # stop with Ctrl+C
if read_lux() < 100:
lamp(LAMP_ON)
else:
lamp(LAMP_OFF)
time.sleep(0.1)
sim_disconnect() # not reached, see Task 29
Fix Task 19: send a command to the lamp only when its state has to change. Each time the lamp changes, print one line such as Lux 85: lamp on or Lux 120: lamp off.
Move the slider: the console should now show a line only at the moments of change.
want is the state the lamp should have. A command is sent only if want differs from the current state. So instead of 10 commands per second, one command is sent per real change. We will use this "send on change" pattern in all following control tasks.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
while (1) { // stop with Ctrl+C
int lux = read_lux();
int want = LAMP_OFF;
if (lux < 100) want = LAMP_ON;
if (want != read_lamp()) { // only on change
lamp(want);
if (want == LAMP_ON) printf("Lux %d: lamp on\n", lux);
else printf("Lux %d: lamp off\n", lux);
}
Sleep(100);
}
sim_disconnect(); // not reached, see Task 29
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
while True: # stop with Ctrl+C
lux = read_lux()
want = LAMP_OFF
if lux < 100:
want = LAMP_ON
if want != read_lamp(): # only on change
lamp(want)
if want == LAMP_ON:
print(f"Lux {lux}: lamp on")
else:
print(f"Lux {lux}: lamp off")
time.sleep(0.1)
sim_disconnect() # not reached, see Task 29
Write a thermostat that runs all the time: every 100 ms read the temperature; above 26 °C the AC must be on, otherwise off. Like in Task 20, send a command only on change and print 27 C: AC on / 26 C: AC off.
While it runs, watch how often the AC switches (lower the Animation Speed to watch comfortably).
Use the lamp buttons of the manual control panel as an input. The program counts how many times the lamp is switched on: each time the lamp goes from off to on, it prints Press 1, Press 2 ... After the 5th time it turns the AC on, prints 5 presses: AC on and ends.
Test: start the program with the lamp off, then press Light On and Light Off one after the other.
If we only asked "is the lamp on?", we would count on every loop while it stays on. By comparing the previous and the current value we catch only the moment of change (from 0 to 1, a "rising edge"). The loop condition depends on the counter, so the loop ends by itself after the 5th press.
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
int presses = 0;
int prev = read_lamp();
printf("Press Light On / Light Off in the panel\n");
while (presses < 5) {
int now = read_lamp();
if (prev == LAMP_OFF && now == LAMP_ON) { // rising edge
presses++;
printf("Press %d\n", presses);
}
prev = now;
Sleep(50);
}
ac(AC_ON);
printf("5 presses: AC on\n");
sim_disconnect();
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
presses = 0
prev = read_lamp()
print("Press Light On / Light Off in the panel")
while presses < 5:
now = read_lamp()
if prev == LAMP_OFF and now == LAMP_ON: # rising edge
presses += 1
print(f"Press {presses}")
prev = now
time.sleep(0.05)
ac(AC_ON)
print("5 presses: AC on")
sim_disconnect()
Write a function blink(times, ms) that blinks the lamp times times, each time ms milliseconds on and ms milliseconds off.
In the main program call blink(3, 200) (fast), pause 1 second, then call blink(3, 1000) (slow).
A function gives a job a name so we can reuse it with different values. The parameters (times, ms) can differ on every call. The main program is now short and readable: it says what is done, the how is inside the function.
#include "room.h"
void blink(int times, int ms) {
for (int i = 0; i < times; i++) {
lamp(LAMP_ON);
Sleep(ms);
lamp(LAMP_OFF);
Sleep(ms);
}
}
int main(void) {
if (sim_connect() != 0) return 1;
blink(3, 200); // fast
Sleep(1000);
blink(3, 1000); // slow
sim_disconnect();
return 0;
}
from room import *
import time
def blink(times, ms):
for i in range(times):
lamp(LAMP_ON)
time.sleep(ms / 1000)
lamp(LAMP_OFF)
time.sleep(ms / 1000)
if sim_connect() != 0:
exit(1)
blink(3, 200) # fast
time.sleep(1)
blink(3, 1000) # slow
sim_disconnect()
Write a function move_curtain(dir): it runs the curtain in the given direction and waits until the motor stops. If the motor stops within 8 seconds it returns 1. If not, it stops the motor and returns 0 (timeout).
Main program: close the curtain and print Closed, wait 2 seconds, open the curtain; if the result is 1 print Opened, if 0 print Timeout!.
To test the timeout, press Stop Sim in the menu while the curtain is opening: after 8 seconds you should see Timeout!.
Write the thermostat of Task 21 with two thresholds: turn the AC on when the temperature goes above 28 °C, off when it goes below 20 °C; do nothing in between. Count how many times the AC is switched on and print each change like 29 C: AC on (switch 3).
Compare with Task 21: how often does the AC switch now?
The thermostat from Task 25 keeps running every 100 ms. Add a warning light to the same loop: while the curtain is moving, the lamp changes state every half second (0.5 s on, 0.5 s off); when the curtain stops, the lamp goes off.
Test: press Curtain Up or Curtain Down. While the lamp blinks, the thermostat must keep controlling the AC.
Do not use Sleep(500); the thermostat would also wait half a second.
While Sleep waits, the program cannot do anything else ("blocking" wait). Instead, the loop always turns every 100 ms and each job checks with a counter whether its time has come. So the thermostat and the warning light run at the same time without holding each other up. % gives the remainder: ticks % 5 cycles 0, 1, 2, 3, 4, 0, ...
#include "room.h"
int main(void) {
if (sim_connect() != 0) return 1;
int ticks = 0;
while (1) { // stop with Ctrl+C
// job 1: thermostat (every tick)
int t = read_temp();
if (t > 28 && read_ac() == AC_OFF) ac(AC_ON);
else if (t < 20 && read_ac() == AC_ON) ac(AC_OFF);
// job 2: warning light (every 5 ticks = 0.5 s)
if (read_curtain() != CURTAIN_STOP) {
if (ticks % 5 == 0) {
if (read_lamp() == LAMP_ON) lamp(LAMP_OFF);
else lamp(LAMP_ON);
}
} else if (read_lamp() == LAMP_ON) {
lamp(LAMP_OFF); // curtain stopped
}
ticks++;
Sleep(100); // one tick = 100 ms
}
sim_disconnect(); // not reached, see Task 29
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
ticks = 0
while True: # stop with Ctrl+C
# job 1: thermostat (every tick)
t = read_temp()
if t > 28 and read_ac() == AC_OFF:
ac(AC_ON)
elif t < 20 and read_ac() == AC_ON:
ac(AC_OFF)
# job 2: warning light (every 5 ticks = 0.5 s)
if read_curtain() != CURTAIN_STOP:
if ticks % 5 == 0:
if read_lamp() == LAMP_ON:
lamp(LAMP_OFF)
else:
lamp(LAMP_ON)
elif read_lamp() == LAMP_ON:
lamp(LAMP_OFF) # curtain stopped
ticks += 1
time.sleep(0.1) # one tick = 100 ms
sim_disconnect() # not reached, see Task 29
Control the curtain automatically by daylight. The curtain has four states: CLOSED, OPENING, OPEN, CLOSING.
• At the start, close the curtain fully (the function from Task 24) and begin in state CLOSED.
• In CLOSED, if lux goes above 600, run the curtain up → OPENING.
• In OPENING, when the motor stops → OPEN.
• In OPEN, if lux goes below 300, run the curtain down → CLOSING.
• In CLOSING, when the motor stops → CLOSED.
Print the name of the new state at every change. Test: move the light slider between 800 and 100.
If the simulation is not open yet, the program should not give up at once: let it try to connect 10 times, 1 second apart (Attempt 1/10 failed...). If all 10 attempts fail, it prints Could not connect, giving up. and ends with code 1.
After connecting it runs a short self test: it turns the lamp and the AC on, waits 200 ms, checks with read_lamp() and read_ac() that the commands were really written and prints Lamp OK / Lamp FAILED and AC OK / AC FAILED. Then it turns both off and closes the connection.
Test: run the program first, then start the simulation a few seconds later.
A robust program expects errors: the simulation may start late, a command may not arrive. Retrying tolerates temporary problems, and a limited number of attempts avoids waiting forever. The exit code (0 success, 1 error) ends the program in a way other programs or scripts can understand. Reading the state back after a command verifies that it really took effect.
#include "room.h"
int connect_retry(int tries) {
for (int i = 1; i <= tries; i++) {
if (sim_connect() == 0) return 0;
printf("Attempt %d/%d failed, retrying in 1 s\n", i, tries);
Sleep(1000);
}
return 1;
}
int main(void) {
if (connect_retry(10) != 0) {
printf("Could not connect, giving up.\n");
return 1;
}
lamp(LAMP_ON);
ac(AC_ON);
Sleep(200);
if (read_lamp() == LAMP_ON) printf("Lamp OK\n");
else printf("Lamp FAILED\n");
if (read_ac() == AC_ON) printf("AC OK\n");
else printf("AC FAILED\n");
lamp(LAMP_OFF);
ac(AC_OFF);
sim_disconnect();
return 0;
}
from room import *
import time
def connect_retry(tries):
for i in range(1, tries + 1):
if sim_connect() == 0:
return 0
print(f"Attempt {i}/{tries} failed, retrying in 1 s")
time.sleep(1)
return 1
if connect_retry(10) != 0:
print("Could not connect, giving up.")
exit(1)
lamp(LAMP_ON)
ac(AC_ON)
time.sleep(0.2)
print("Lamp OK" if read_lamp() == LAMP_ON else "Lamp FAILED")
print("AC OK" if read_ac() == AC_ON else "AC FAILED")
lamp(LAMP_OFF)
ac(AC_OFF)
sim_disconnect()
Until now we interrupted endless programs with Ctrl+C; the lamp, the AC and the curtain stayed as they were and sim_disconnect() never ran. Change the night lamp of Task 20 so that on Ctrl+C it leaves the loop cleanly, turns the lamp and the AC off, stops the curtain, calls sim_disconnect() and prints Bye!.
Ctrl+C sends an interrupt signal to the program. Python raises it as a KeyboardInterrupt error; we catch it and go to the cleanup code. In C the signal function only changes a flag; on the next turn the loop sees the flag and ends by itself. So the program always leaves the room in a safe state. We will use this pattern in the following projects.
#include "room.h"
#include <signal.h>
static volatile sig_atomic_t running = 1;
static void on_ctrl_c(int sig) {
(void)sig;
running = 0; // only set a flag here
}
int main(void) {
if (sim_connect() != 0) return 1;
signal(SIGINT, on_ctrl_c);
while (running) {
int want = LAMP_OFF;
if (read_lux() < 100) want = LAMP_ON;
if (want != read_lamp()) lamp(want);
Sleep(100);
}
// cleanup: leave the room in a safe state
lamp(LAMP_OFF);
ac(AC_OFF);
curtain(CURTAIN_STOP);
sim_disconnect();
printf("Bye!\n");
return 0;
}
from room import *
import time
if sim_connect() != 0:
exit(1)
try:
while True:
want = LAMP_OFF
if read_lux() < 100:
want = LAMP_ON
if want != read_lamp():
lamp(want)
time.sleep(0.1)
except KeyboardInterrupt: # Ctrl+C
pass
# cleanup: leave the room in a safe state
lamp(LAMP_OFF)
ac(AC_OFF)
curtain(CURTAIN_STOP)
sim_disconnect()
print("Bye!")
Write a program that runs the room fully automatically. Each subsystem is in its own function and all of them run in the same 100 ms loop:
• Lamp: on if the outdoor light is below 100 lux, otherwise off.
• AC: on above 28 °C, off below 20 °C (Task 25).
• Curtain: the state machine of Task 27 (open above 600 lux, close below 300 lux).
• Commands are sent only on change and every change is printed.
• Clean exit with Ctrl+C (Task 29).
Test: set the light slider to 50, 400, 800 and back to 50; watch what the lamp and the curtain do at each step. Or just wait: the outdoor light changes by itself anyway.
Write an energy meter that controls nothing, it only watches. Switch the lamp and the AC with the manual control panel (or with another program running at the same time); the meter measures:
• how many seconds in total the lamp and the AC were on,
• how many times the AC was switched on.
Every 5 seconds it prints a line such as Lamp 12.3 s, AC 4.0 s, AC started 2 times. On Ctrl+C it calculates the energy used: lamp 60 W, AC 1500 W. Energy (Wh) = power (W) × time (s) / 3600. Example: Energy: lamp 0.21 Wh, AC 1.67 Wh.
Automatic systems should listen to the user. The lamp (on below 100 lux) and the AC (28/20 °C hysteresis) run automatically. But if the user changes the lamp or the AC with the manual control panel, the program must not touch that device for 30 seconds and prints Lamp: manual for 30 s (or AC: ...). When the 30 seconds are over, it prints Lamp: back to automatic and returns to automatic mode. Clean exit with Ctrl+C.
Test: set the light slider to 50 (the lamp turns on), then press Light Off: the lamp must stay off for 30 seconds, then turn on again.
Write a state machine that runs one day of the room. States: NIGHT, DAY, EVENING.
• Start: if lux is 300 or more, open the curtain fully and start in DAY; otherwise close it fully and start in NIGHT.
• NIGHT → when lux goes above 300, the morning routine: print Good morning!, blink the lamp 3 times quickly (300 ms), open the curtain step by step (1 s up, wait 2 s, 1 s up, wait 2 s, then all the way), go to DAY.
• DAY → when lux goes below 100: print Good evening!, run the curtain down, turn the lamp on, go to EVENING.
• EVENING → after 10 seconds turn the lamp off, print Good night!, go to NIGHT. The loop must not block while waiting these 10 seconds (tick counter).
• Clean exit with Ctrl+C.
Test: speed up the day with the light slider (50 → 500 → 50), or wait about 3 minutes and watch the daylight change by itself.