תכנות מיקרו-בקרים בשפת MicroPython · פרק 21 — פרויקט מסכם⌂📚🏠
פרק 21

פרויקט מסכם

Final Project
3 שעות לימוד

עד עכשיו כל פרק עסק בדבר אחד. הפרויקט מחבר אותם, ומתעוררת בו בעיה חדשה: איך בונים משהו גדול בלי ללכת לאיבוד בו? התשובה בפרק הזה פשוטה: כותבים מה שרוצים לפני שמתכנתים, מסדרים את הקבצים, מוסיפים דבר אחד בכל פעם, ורושמים מה עשינו.

בפרק הזה:
  • נכיר את מה שתכנית הלימודים דורשת מפרויקט יחיד ומפרויקט זוגי.
  • נכתוב רעיון, תרשים מלבנים ומפרט שאפשר לבדוק.
  • נבנה דוגמה שלמה משני לוחות, מסודרת בעץ קבצים ברור, צעד אחר צעד.
  • נפריד בין תקלה בחומרה לתקלה בתוכנה, בעזרת רב-מודד ומשקף תנודות.
  • נתעד מה נכתב בעזרת בינה מלאכותית, ונכתוב יומן עבודה וספר פרויקט.

21.1 מה דורשת תכנית הלימודים? (Project Requirements)

סוגהנדרש
פרויקט יחידמיקרו-בקר שמתחבר למערכת אחרת דרך Bluetooth או WiFi או NFC או RFID או ZigBee.
פרויקט זוגישני מיקרו-בקרים לפחות, ואחד מהם מחובר לאינטרנט.

בדוגמה שאנחנו בונים כאן יש שני לוחות, ושניהם מגיעים למתווך MQTT באינטרנט, ולכן היא עומדת בתנאי של פרויקט זוגי. מה שישתנה בפרויקט שלכם הוא החיישנים והמטרה, והדרך אחת.

שאלה 21.1: שני תלמידים בונים שני לוחות שמחליפים הודעות דרך UART בחוט בלבד. האם זה פרויקט זוגי מקובל? ומהו התיקון הקטן ביותר שמתקן אותו?

21.2 תכנון: רעיון, תרשים מלבנים ומפרט (Planning)

הרעיון, במשפט: מפקח על חממה קטנה: לוח בחממה קורא טמפרטורה, לחות ואור ומפעיל מאוורר, ולוח במשרד מציג את המצב ומחליט מתי המאוורר פועל.

DHT22LDRמאווררממסרצומת חיישניםESP32 או Pico Wמתווךבאינטרנטצומת תצוגהESP32 או Pico WLCDI2Cdatadata, statefan/setfan/set
תרשים מלבנים: כל לוח הוא צומת, ואף אחד מהם אינו מכיר את כתובת האחר. המתווך ביניהם, כמו בפרק 16.

מפרט: כל שורה צריכה להיות ניתנת לבדיקה בכן או לא. המשפט "הפרויקט מהיר" אינו מפרט, והמשפט "נשלחת קריאה כל 10 שניות" הוא מפרט:

המפרטאיך בודקים
1צומת החיישנים שולח קריאה כל 10 שניות.קוראים את הנושא במחשב ומודדים את הזמן.
2המסך מציג טמפרטורה, לחות, אור ומצב המאוורר.משווים את המסך לקריאה.
3המאוורר פועל מעל 30 מעלות, ונעצר מתחת ל-27.מחממים את החיישן בקרבת היד ועוקבים.
4אחרי שתיקה של 30 שניות המסך כותב NO DATA.מנתקים את הזנת צומת החיישנים.
5ההודעות חוזרות מעצמן אחרי הפסקה ברשת, בלי ללחוץ על דבר.מכבים את הנתב לדקה ומדליקים אותו.
אזהרה: שני הספים במפרט 3 הם ההיסטרזיס מסעיף 6.8. המתווך כאן ציבורי ופתוח: כל מי שמכיר את שם הנושא קורא ממנו וכותב אליו. זה מספיק למאוורר בתרגול, ואינו מספיק לדבר שעלול לגרום נזק. בפרויקט אמיתי משתמשים במתווך משלנו, כמו בפרק 16.
שאלה 21.2: הפכו את המשפט "המכשיר מגיב מהר ללחיצה על הכפתור" למפרט שאפשר לבדוק.

21.3 עץ הקבצים (Project Tree)

מחליטים איפה כל דבר נמצא לפני שכותבים. הכלל: מה ששני הלוחות חולקים נמצא ב-shared, מה ששייך ללוח אחד נמצא בתיקייה שלו, והקובץ main.py קצר וקורא לאחרים. בלוח עצמו מעתיקים יחד את קבצי shared ואת קבצי תיקיית הצומת אל התיקייה הראשית, כי התוכנית מחפשת שם את הספריות שלה.

greenhouse/README.mdמהו הפרויקט ואיך מפעילים אותוdocs/logbook.mdיומן העבודהai_log.mdיומן הבינה המלאכותיתproject_book.mdספר הפרויקטshared/מועתק לשני הלוחותboard.pyמפרק 6wifi.pyמפרק 14secrets.pyלעולם אינו נכנס לשיתוףgh_link.pyהנושאים והחיבורnode_sensor/main.pygh_sensor_main.pygh_sense.pyהחיישניםnode_display/main.pygh_display_main.pygh_view.pyהמסךi2c_lcd.pyמפרק 9tools/gh_selftest.pyבדיקת החומרה
עץ הקבצים של פרויקט החממה. בחבילת ההורדה של הספר שני הקבצים main.py נושאים את השמות שכתובים לידם, ולכן משנים את שמם בהעתקה ללוח.

הקובץ המשותף gh_link.py: שמות הנושאים במקום אחד, ולכן אי אפשר שיהיה ביניהם הבדל בין שני הלוחות. החיבור והחיבור מחדש הם מפרק 16.

# gh_link.py - the greenhouse MQTT link, the same on both nodes.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com
import json
import time
from umqtt.simple import MQTTClient
import wifi

BROKER = "broker.hivemq.com"
DATA = b"school/gh1/data"
FAN_SET = b"school/gh1/fan/set"
FAN_STATE = b"school/gh1/fan/state"


class Link:
    def __init__(self, client_id, topics, handler):
        self.client = MQTTClient(client_id, BROKER)
        self.topics = topics
        self.handler = handler
        self.client.set_callback(self._deliver)

    def _deliver(self, topic, msg):
        try:
            obj = json.loads(msg)
        except ValueError:
            print("bad message on", topic)
            return
        self.handler(topic, obj)

    def connect(self, tries=4):
        wifi.connect()
        wait = 1
        for _ in range(tries):
            try:
                self.client.connect()
                for topic in self.topics:
                    self.client.subscribe(topic)
                return True
            except OSError:
                print("no broker, waiting", wait, "s")
                time.sleep(wait)
                wait *= 2
        return False

    def send(self, topic, obj):
        msg = json.dumps(obj).encode()
        try:
            self.client.publish(topic, msg)
        except OSError:
            if self.connect():
                self.client.publish(topic, msg)

    def poll(self):
        try:
            self.client.check_msg()
        except OSError:
            self.connect()
שאלה 21.3: למה כתבנו את שמות הנושאים ב-gh_link.py ולא בכל main.py?

21.4 בנייה צעד אחר צעד (Building Step by Step)

לא כותבים הכול ואז מנסים. מוסיפים מנגנון אחד, מנסים אותו לבדו, ולא מתקדמים עד שהוא עובד. וכשמופיעה תקלה אנחנו יודעים איפה לחפש: במה שהוספנו אחרון.

צעדהמנגנון החדשהמפרטיודעים שזה עובד כאשר
1החיישנים, בלי רשת—מופיעות קריאות סבירות בחלון ההדפסה.
2פרסום למתווך1הקריאות מופיעות במחשב.
3המסך מציג את מה שמגיע2המסך משתנה עם הטמפרטורה.
4ההחלטה ופקודת המאוורר3המאוורר פועל ומצבו חוזר למסך.
5שתיקה והפסקה ברשת4, 5מנתקים את ההזנה או את הרשת, והמכשיר מתנהג כפי שכתבנו.
הערה: בספר הזה כל לוח רץ לבדו בסימולציה, וכל מה שמגיע מן הלוח האחר הוא הודעות מוכנות בעולם המדומה. לכן הפלטים האלה אינם מוכיחים ששני הלוחות עובדים יחד. את זה בודקים אתם על שני הלוחות האמיתיים.

צעד 1: החיישנים:

אוספים את הקריאות במילון אחד, וכשהחיישן נכשל מחזירים None והתוכנית אינה נעצרת.
# gh_sense.py - the sensors of the greenhouse, as one reading.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com
import dht
from machine import Pin
import board


class Sensors:
    def __init__(self):
        self.air = dht.DHT22(Pin(board.DHT))
        self.ldr = board.analog_in(board.LDR)

    def read(self):
        try:
            self.air.measure()
        except OSError:
            return None
        return {"t": self.air.temperature(),
                "h": self.air.humidity(),
                "light": self.ldr.read_u16() * 100 // 65535}
צעד 1, בלי רשת.
import time
from gh_sense import Sensors

sensors = Sensors()
for _ in range(3):
    print(sensors.read())
    time.sleep(10)
ESP32 + Pico W · MicroPython 1.29 · simulated board
{'t': 24, 'light': 50, 'h': 60}
{'t': 24, 'light': 50, 'h': 60}
{'t': 24, 'light': 60, 'h': 60}

צעדים 2 ו-3: פרסום ותצוגה:

המסך בקובץ משלו: אם מחליפים אותו, אין נוגעים באחר.
# gh_view.py - what the greenhouse display shows, on a 16x2 LCD.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com
import board
from i2c_lcd import LCD


class View:
    def __init__(self):
        self.lcd = LCD(board.i2c())

    def data(self, d, fan):
        self.lcd.line(0, "T %.1fC  H %.0f%%" % (d["t"], d["h"]))
        self.lcd.line(1, "L %d%%  Fan %s"
                      % (d["light"], "ON" if fan else "off"))

    def no_data(self):
        self.lcd.line(0, "NO DATA")
        self.lcd.line(1, "check sensor")
צומת התצוגה בצורתו הראשונה. שתי ההודעות באות מן העולם המדומה.
import time
from gh_view import View
from gh_link import Link, DATA

view = View()


def on_message(topic, obj):
    print("got", obj)
    view.data(obj, False)


link = Link("gh1-display", (DATA,), on_message)
link.connect()
for _ in range(25):
    link.poll()
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
got {'h': 60, 't': 24.5, 'light': 50}
got {'h': 58, 't': 25.0, 'light': 61}
[LCD]
|T 25.0C  H 58%  |
|L 61%  Fan off  |
...

צעדים 4 ו-5: שני הצמתים בצורתם הסופית:

main.py בצומת החיישנים: מפרסם כל 10 שניות, מבצע את הפקודה ומחזיר את המצב. פקודת המאוורר באה מן העולם המדומה.
import time
import machine
from machine import Pin, WDT
import board
from gh_sense import Sensors
from gh_link import Link, DATA, FAN_SET, FAN_STATE

SEND_EVERY_MS = 10000

fan = Pin(board.LED, Pin.OUT)
sensors = Sensors()


def on_message(topic, cmd):
    fan.value(1 if cmd["on"] else 0)
    link.send(FAN_STATE, {"on": fan.value()})
    print("fan:", fan.value())


link = Link("gh1-sensor", (FAN_SET,), on_message)
if not link.connect():
    time.sleep(30)
    machine.reset()

guard = WDT(timeout=8000)
next_send = time.ticks_ms()
while True:
    guard.feed()
    link.poll()
    if time.ticks_diff(time.ticks_ms(), next_send) >= 0:
        next_send = time.ticks_add(next_send, SEND_EVERY_MS)
        data = sensors.read()
        if data:
            link.send(DATA, data)
        else:
            print("sensor failed")
    time.sleep_ms(100)
ESP32 + Pico W · MicroPython 1.29 · simulated board
fan: 1
[mqtt_pub]
school/gh1/data {"t": 31, "light": 60, "h": 60}
school/gh1/data {"t": 31, "light": 60, "h": 60}
school/gh1/fan/state {"on": 1}
school/gh1/data {"t": 31, "light": 60, "h": 60}
...
הערה: את פסק הזמן WDT(timeout=8000) בחרנו כדי שיתאים לשני הלוחות: הפסק המרבי ב-RP2040 לפי התיעוד הוא 8388 ms. יוצרים את כלב השמירה אחרי החיבור הראשון, כי החיבור לוקח יותר מזה, ואי אפשר לעצור כלב שמירה אחרי שהופעל. אם התוכנית נתקעת או שההמתנה למתווך מתארכת, כלב השמירה מאתחל את הלוח, וזה מקובל במכשיר שעובד לבד.

עכשיו main.py בצומת התצוגה. שימו לב בו ל-want ול-have: הראשון הוא מה שאנחנו רוצים, והשני הוא מה שהלוח האחר אומר. כל עוד הם שונים חוזרים על הפקודה עם כל קריאה. לכן אם הודעה אבדה, היא תתוקן אחרי 10 שניות. זה חוג סגור דרך הרשת, ברעיון של פרק 20: מודדים, משווים ומתקנים. בפלטים שלהלן ההודעות שותקות אחרי t = 25, כאילו החיישן התנתק.

import time
from gh_view import View
from gh_link import Link, DATA, FAN_SET, FAN_STATE

FAN_ON = 30.0
FAN_OFF = 27.0
STALE_MS = 30000

view = View()
want = False
have = False
seen = time.ticks_ms()
stale = False


def on_data(d):
    global want, seen, stale
    seen = time.ticks_ms()
    stale = False
    if d["t"] > FAN_ON:
        want = True
    elif d["t"] < FAN_OFF:
        want = False
    if want != have:
        print("T", d["t"], "ask fan", want)
        link.send(FAN_SET, {"on": want})
    view.data(d, have)


def on_message(topic, obj):
    global have
    if topic == DATA:
        on_data(obj)
    elif topic == FAN_STATE:
        have = bool(obj["on"])
        print("fan is", have)


link = Link("gh1-display", (DATA, FAN_STATE), on_message)
link.connect()
while True:
    link.poll()
    late = time.ticks_diff(time.ticks_ms(), seen)
    if late > STALE_MS and not stale:
        stale = True
        print("silent for", late // 1000, "s")
        view.no_data()
    time.sleep_ms(100)
ESP32 + Pico W · MicroPython 1.29 · simulated board
T 31.0 ask fan True
fan is True
T 26.0 ask fan False
fan is False
silent for 30 s
[mqtt_pub]
school/gh1/fan/set {"on": true}
school/gh1/fan/set {"on": false}
[LCD]
|NO DATA         |
|check sensor    |
...
שאלה 21.4: לוח התצוגה קורא טמפרטורה של 28 מעלות, והמאוורר פועל. האם הוא שולח פקודה? ולמה?

21.5 איתור תקלות: חומרה או תוכנה? (Troubleshooting)

השאלה הראשונה היא איפה התקלה, ולא איך מתקנים אותה. מחלקים את השרשרת לשני חצאים, בודקים באמצע, ויודעים איזה חצי תקין. בפרויקט שלנו המתווך הוא האמצע הטבעי: אילו קריאות מגיעות אליו?

במחשב: רואים כל מה ששני הלוחות מפרסמים, בלי לגעת בהם. התו הכללי מפרק 16.
mosquitto_sub -h broker.hivemq.com -t "school/gh1/#" -v

אם הקריאות מופיעות, צומת החיישנים תקין והתקלה בצד התצוגה. אם לא, ההפך. כדי לבדוק את כל החומרה כותבים תוכנית קטנה שאינה יודעת דבר על הפרויקט:

בדיקה עצמית, בעולם מדומה שאין בו מסך ואין רשת, והנגד התלוי אור מנותק.
import board
import dht
from machine import Pin
import wifi


def lcd():
    return 0x27 in board.i2c().scan()


def air():
    s = dht.DHT22(Pin(board.DHT))
    s.measure()
    return s.humidity() > 0


def light():
    return 1000 < board.analog_in(board.LDR).read_u16() < 64500


def net():
    return wifi.connect(5).isconnected()


for name, test in (("lcd", lcd), ("air", air),
                   ("light", light), ("net", net)):
    try:
        ok = test()
    except OSError:
        ok = False
    print("%-6s %s" % (name, "PASS" if ok else "FAIL"))
ESP32 + Pico W · MicroPython 1.29 · simulated board
lcd    FAIL
air    PASS
light  FAIL
net    FAIL

כשהכול מחובר התוכנית נותנת: lcd PASS, air PASS, light PASS, net PASS. כל שורה FAIL פירושה תקלה בחומרה או בחיווט, ולכן לא מחפשים ב-main.py לפני שמתקנים אותה.

כשחושדים בחומרה לוקחים כלי מדידה. הרב-מודד נותן ערך אחד קבוע, ומתאים למה שאינו משתנה מהר. משקף התנודות מצייר את המתח לאורך הזמן, ומתאים למה שמשתנה:

התסמיןבודקים קודםהכלי
הלוח אינו עובד או מתאתחלמתח 3V3 על ההדק, ואיכות ההזנה כשמפעילים עומסרב-מודד, ומשקף תנודות לצניחה רגעית
חיישן שאינו משתנההמתח על ההדק עצמו ולא על החיישן, והמשכיות החוטיםרב-מודד
scan() ריקההזנה, החלפת SDA ו-SCL, ונגדי המשיכה: שני הקווים גבוהים במנוחהרב-מודד, ואחר כך משקף תנודות לפולסים
מאוורר שאינו מסתובב וההדק משתנההמתח על הבסיס, הזנת המנוע, והארקה המשותפתרב-מודד
PWM אינו פועל כמצופההצורה, התדר ומחזור העבודהמשקף תנודות. הרב-מודד מציג את הממוצע בלבד
ההודעה אינה מגיעה והחומרה תקינהשם הנושא, מזהה הלקוח, ו-check_msg() בלולאההתוכנית וההדפסה
דוגמה 21.1
מודדים ברב-מודד את הדק ה-PWM במחזור עבודה של 70% מ-3.3 V. מה הוא מציג? ולמה איננו רואים את המתח 3.3 V?
פתרון:
V = D · VCC
V = 0.7 · 3.3
V = 2.31 V

הוא מציג בקירוב את הממוצע, כי הרב-מודד איטי מן הפולסים. זה אינו אומר שההדק נמצא במתח הזה: משקף התנודות מראה שהוא קופץ בין 0 ל-3.3 V.

V ≈ 2.31 V
טעות נפוצה: משנים דבר אחד בכל ניסוי, ורושמים אותו ביומן. אם שינינו שלושה דברים יחד והמכשיר עבד, איננו יודעים מה תיקן אותו, והתקלה עלולה לחזור.
שאלה 21.5: המסך כותב NO DATA, והפקודה mosquitto_sub מציגה קריאות חדשות. איפה התקלה?

21.6 שימוש בבינה מלאכותית: חובת תיעוד (Using AI)

מותר להשתמש בכלי בינה מלאכותית בפרויקט, אבל תכנית הלימודים מחייבת אותנו (פרק 8 בה) לתעד מה נכתב בעזרתם. אין די לומר בסוף "השתמשנו": רושמים זאת כשזה קורה, ב-docs/ai_log.md.

דוגמה לרישום אחד. דוגמה להמחשה, והשמות והתאריכים אינם אמיתיים.
## 2026-11-14 - gh_link.py, function connect()
Tool and date: (name of the tool), 2026-11-14
Asked: "retry MQTT connect with growing waits in MicroPython"
Took: the loop with wait *= 2
Changed: removed a call that does not exist in umqtt.simple
Checked: ran with the broker off, read every line, can explain it
שאלה 21.6: תלמידה ביקשה מכלי לכתוב את הספרייה gh_view.py כמות שהיא בלי שינוי, והיא עבדה מהפעם הראשונה. מה היא רושמת?

21.7 יומן עבודה וספר פרויקט (Logbook and Project Book)

יומן עבודה כותבים בכל מפגש, בכמה שורות. אחרי חודש לא נזכור למה החלפנו את הנגד ההוא, והיומן זוכר. הוא גם חומר הגלם של ספר הפרויקט.

דוגמה למפגש אחד. דוגמה להמחשה, והשמות אינם אמיתיים.
## 2026-11-12 (2 hours) - Sara, Omar
Goal: stage 3, the display shows a real reading.
Did: wired the LCD, wrote gh_view.py.
Problem: LCD lit, no text. scan() returned [].
Cause: SDA and SCL swapped. Found with scan(), fixed the wires.
Next: stage 4, the fan command.

ספר פרויקט כותבים לקורא שאינו יודע דבר על הפרויקט שלנו. זה סדר מוצע לחלקיו:

החלקמה בומאיפה
הרעיון והמטרההבעיה ומה שהמכשיר עושה21.2
התרשים והמפרטתרשים המלבנים וטבלת המפרט21.2
החומרההשרטוטים, רשימת החלקים וההדקיםהמעגלים שבנינו
התוכנהעץ הקבצים, תפקיד כל קובץ והנושאים21.3
הבדיקהטבלת המפרט ותוצאת כל שורה21.4
תקלות ומה שלמדנוהחשוב שקרה ואיך ניתחנו אותויומן העבודה
שימוש בבינה מלאכותיתסיכום היומןai_log.md
שאלה 21.7: תלמיד כתב בטבלת הבדיקה: "מפרט 4 — עובד". מה חסר לו?

עיקרי הפרק:

פקודות הפרק:

פקודהמה היא עושה
Link(id, topics, handler)חיבור של צומת, מן הספרייה gh_link.py.
link.send(topic, obj)פרסום מילון בתבנית JSON, עם חיבור מחדש בעת הצורך.
link.poll()בדיקת הודעות נכנסות בלי להמתין.
WDT(timeout=8000)כלב שמירה, עם פסק זמן שמתאים לשני הלוחות.
mosquitto_sub -t "a/#" -vקריאת כל מה שמתפרסם תחת נושא, מן המחשב.

מילון מונחים:

עבריתEnglishערבית
מפרטSpecificationالمواصفات
צומתNodeالعقدة
עץ קבציםFile Treeشجرة الملفات
רב-מודדMultimeterالمقياس المتعدّد
משקף תנודותOscilloscopeراسم الذبذبات
יומן עבודהLogbookسجلّ العمل
ספר פרויקטProject Bookكتاب المشروع
חממהGreenhouseالدفيئة
התשובות הסופיות לשאלות הפרק
21.1 לא, כי אין לוח שמחובר לאינטרנט. מספיק שאחד מהם יפרסם את קריאותיו למתווך באינטרנט דרך WiFi.
21.2 למשל: "מצב המסך משתנה תוך שתי שניות מן הלחיצה על הכפתור", ובודקים בשעון עצר.
21.3 כדי שלכל שם יהיה מקום אחד. אילו אות אחת הייתה שונה בין הלוחות, ההודעה לא הייתה מגיעה, ולא הייתה מופיעה שום שגיאה.
21.4 לא. הקריאה בין 27 ל-30, ולכן want אינו משתנה, והוא שווה ל-have.
21.5 בצומת התצוגה או בחיבור שלו, כי צומת החיישנים והמתווך תקינים. מסתכלים בהרשמה שלו ובחיבורו לרשת.
21.6 את הקובץ, הכלי, התאריך והבקשה, שהיא לקחה את כל הקוד בלי שינוי, ואיך בדקה אותו. העובדה שלא שינתה אינה מבטלת את חובת התיעוד.
21.7 איך בדק ומה התוצאה: ניתקנו את ההזנה בשעה כך וכך, ואחרי כמה שניות הופיע NO DATA.

שאלות לתרגול:

רמה ראשונה — בסיסית
1.
אילו מן המשפטים הוא מפרט:
(א) המכשיר יפה.
(ב) הנורה נדלקת תוך שנייה מרגע שחושך.
(ג) הוא צורך מעט אנרגיה.
תשובות: (ב) בלבד. אפשר למדוד אותו ולהכריע בכן או לא.
2.
באיזו תיקייה נשים קובץ ששני הלוחות זקוקים לו? ובאיזו תיקייה נשים את קובץ המסך?
תשובות: הראשון ב-shared, והשני ב-node_display.
3.
הכלי נתן לנו פונקציה שעובדת, ושינינו רק את שמה. האם רושמים זאת?
תשובות: כן, כי הקוד נכתב בעזרתו, ומציינים את השינוי.
רמה שנייה — יישום
4.
הוסיפו לקריאה של צומת החיישנים שדה "up" ובו מספר השניות מאז ההפעלה. איזה קובץ משנים? ולמה gh_view.py נשאר כמו שהוא?
תשובות: משנים רק את gh_sense.py, בהוספת time.ticks_ms() // 1000. המסך קורא את השדות שהוא מכיר, ושדה חדש אינו משפיע עליו.
5.
רוצים שהמאוורר ייעצר מתחת ל-25 מעלות ולא מתחת ל-27. איזו שורה משנים? ומה קורה לתנודת הטמפרטורה ולמספר ההחלפות?
תשובות: FAN_OFF ב-gh_display_main.py. תחום ההיסטרזיס גדל, ולכן התנודה גדלה וההחלפות פוחתות.
6.
לכלב השמירה פסק זמן של 8 s. כמה זמן נמשך connect() אם הוא נכשל כולו? ומה קורה אם המתווך מתנתק באמצע העבודה?
תשובות: ההמתנה היא 1 + 2 + 4 + 8 = 15 s ויותר. זה ארוך מפסק הזמן, ולכן הלוח מאתחל את עצמו, ואז מנסה שוב מן ההתחלה. מכיוון שזה מקובל כאן השארנו זאת כך, אבל זה צריך להופיע בספר הפרויקט.
רמה שלישית — העמקה
7.
טמפרטורת החממה משתנה לאט, והקריאה נשלחת כל 10 שניות.
(א) האם צריך קריאה מהירה יותר לפי פרק 20?
(ב) מה מונע מאיתנו לקרוא כל שנייה בחיישן DHT22?
תשובות: (א) לא. קבוע זמן גדול אינו צריך דגימה מהירה. (ב) החיישן צריך מרווח בין הקריאות, כשתי שניות לפי גיליון הנתונים שלו.
8.
(א) איך לוח התצוגה יודע שצומת החיישנים התנתק לפני שעברו 30 שניות?
(ב) איזה מנגנון מפרק 16 מתאים?
תשובות: (א) הוא יודע רק מן השתיקה, אם המתווך לא אמר לו. (ב) set_last_will: המתווך שולח הודעה בשם הלוח כשהחיבור שלו נפסק, ולא מיד, אלא אחרי שפג פסק הזמן של keepalive.
9.
תכננו פרויקט יחיד מתוך מרכיבי הפרק: מספיק לוח אחד. מה מוחקים? והאם MQTT עדיין הכרחי?
תשובות: מוחקים את הצומת השני, ומאחדים מסך, חיישנים והחלטה בלוח אחד. נשאר התנאי של חיבור למערכת אחרת, כמו WiFi למתווך שטלפון קורא ממנו. אם נמחק אותו, הפרויקט כבר אינו מקובל.