Try to solve the task yourself before looking at the answer. Different solutions that give the same result are also correct.

1 Beginner

Connecting, switching outputs and waiting.
01

Connect to the simulation

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.

sim_connect()sim_disconnect()printf / print
#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()
02

Turn the lamp on

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.

lamp()LAMP_ON
#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()
03

On, wait, off

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.

Sleep / time.sleepLAMP_OFF
#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()
04

First the lamp, then the AC

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.

ac()AC_ONsequence
#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()
05

Open and close the curtain

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.

curtain()CURTAIN_UPCURTAIN_DOWN
#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()
06

Open the curtain halfway

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.

CURTAIN_STOPtiming
#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()
07

Good morning scenario

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.

2 Basic

Reading sensors, printing and deciding with if/else.
08

Read the temperature

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.

read_temp()variable%d / f-string
#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()
09

Room report

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.

read_lux()read_ac()read_lamp()read_curtain()
#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()
10

Lamp on when it is dark

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.

if / else<
#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()
11

AC on when it is hot

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
12

What is the curtain doing?

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.

else if / elif==constants
#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()
13

Lamp wasting energy

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).

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
14

Do not send the same command twice

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.

3 Intermediate

Loops, continuous control and tracking state.
15

Blink the lamp 5 times

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).

for loopcounter
#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()
16

Countdown

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
17

Lowest and highest temperature

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
18

How long does the curtain take to open?

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().

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
19

Automatic night lamp

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.

while (1) / while Truecontinuous controlCtrl+C
#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
20

Send a command only when needed

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.

state changeread_lamp()!=
#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
21

Simple thermostat

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).

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
22

Button press counter

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.

edge detectioncounterwhile
#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()

4 Advanced

Functions, timing, state machines and robustness.
23

Your own function: blink

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.

functionparameter
#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()
24

A function that returns a value: move the curtain to the end

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!.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
25

Thermostat with hysteresis

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?

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
26

Timing without blocking

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, ...

non-blocking timingtick counter% (modulo)
#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
27

Curtain with a state machine

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
28

Robust connection and self test

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.

robustnessretryreturn codeself test
#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()
29

Clean exit with Ctrl+C

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.

Ctrl+Csignal / KeyboardInterruptcleanup
#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!")

5 Project

Complete systems that combine everything.
30

Smart room

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
31

Energy meter

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
32

Manual override

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.
33

The cycle of a day

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.

Your turn

No answer is given for this task. Solve it yourself by combining the ideas from the answers of earlier tasks; your working program is your answer.