bento-aiot/<ทีม>/telemetry (บอร์ดส่งค่า) · bento-aiot/<ทีม>/event (เหตุการณ์ เริ่มใช้บทเรียน 2.1–2.3) · bento-aiot/<ทีม>/cmd (คำสั่งเข้าบอร์ด) ผู้เรียนคนอื่นก็ใช้ broker และหัวข้อชุดนี้ จึงตั้ง TEAM เป็นรหัสที่ไม่ซ้ำใคร a-z 0-9 ยาว 4–16 ตัว เช่นชื่อเล่นต่อด้วยเลขสุ่ม 4 หลัก (nok4821) · ถ้าเรียนเป็นกลุ่ม ผู้จัดอาจแจก team01 ถึง team19# เครื่องหมายนี้แปลว่าทุกหัวข้อบน broker ข้อความของคนแปลกหน้าทั้งโลกจะไหลเข้ามา และกล่องรับของบอร์ดมีช่องเดียว ใบใหม่ทับใบเก่า · หน้าเว็บของทีมฟัง bento-aiot/team03/# หน้ารวมของผู้สอนฟัง bento-aiot/+/telemetryDEVICE_ID = "bento-aiot-" + TEAM ยาว 17 ตัวอักษร (เฟิร์มแวร์ตัดที่ 31) ส่วนหน้าเว็บสุ่ม web- ตามด้วยเลขสุ่มทุกครั้ง จึงไม่เตะบอร์ดใครก็ส่งเข้า
bento-aiot/team03/cmdได้ ไม่ใช่แค่หน้าเว็บของเรา — นี่คือเหตุผลที่ไฟล์ 06 ไม่เชื่อคนส่งเลยสักบรรทัด และคือเหตุผลที่บทเรียน 4.4–4.6 กับ 4.7–4.9 ต้องมีเรื่องสิทธิ์และการเข้ารหัส
05_value_leaves_the_board.py — บันไดสามขั้นที่ห้ามสลับ linked = mqtt.connect(BROKER, port=1883, client_id=DEVICE_ID, keepalive=60)
...
payload = {"id": TEAM, # คีย์สั้นตัวเล็ก มี id กับ n เสมอ
"n": i,
"knob": knob,
"az": az,
"uptime_s": time.ticks_ms() // 1000}
body = json.dumps(payload) # dict ของเรา -> ข้อความที่ทุกภาษาอ่านออก
...
try:
ok = mqtt.publish(TOPIC, body)
except OSError: # สายหลุดแล้ว publish ไม่คืน False มันโยน OSError
...
ป้ายสามขั้นบนจอไล่เปลี่ยนจากเทาเป็นเขียวตามลำดับ ขั้นไหนไม่ผ่านจะเป็นแดงพร้อมบอกเหตุผล แล้วโปรแกรมจบตรงนั้นอย่างสุภาพ ไม่ค้างรอ นี่คือแบบที่โปรแกรมหน้างานต้องเขียน ทุกทางออกของไฟล์นี้เขียนบนจอไว้เสมอว่าไปติดที่ขั้นไหน
json.dumps() แปลง dict ของเราเป็นข้อความ ทำให้เครื่องปลายทางจะเขียนด้วยภาษาอะไรก็อ่านออก — นี่คือเหตุผลที่ IoT ทั้งโลกส่ง JSON กันไปมา ไม่ใช่ส่งโครงสร้างข้อมูลของภาษาใดภาษาหนึ่ง
mqtt.connect()ใช้ชื่อusername=ไม่ใช่user=ใส่ผิดได้TypeErrorทันที · และclient_idที่ซ้ำกับใครก็ตามบน broker เดียวกัน ทำให้ผลัดกันเตะกันออกโดยไม่มีข้อความเตือน · บน broker สาธารณะ "ใครก็ตาม" คือทั้งอินเทอร์เน็ต
knob = -1 # -1 = รอบนี้อ่านลูกบิดไม่ได้
az = -99.0 # -99 = อ่านค่าเอียงไม่ได้
try:
s = sensors.snapshot() # ของเดิมจาก m01-ui-application/l03-inside-the-box/examples/12_every_sense_at_once.py
if "pot" in s: # ถามด้วย in ก่อนหยิบ รอบที่ไม่มีคีย์จะไม่ตาย
knob = int(s["pot"]["percent"])
if "bmi270" in s:
az = round(s["bmi270"]["az"], 2) # m/s^2 วางราบราว 9.8
except OSError:
pass # อ่านไม่ได้รอบนี้ ไม่ใช่เหตุให้หยุดส่ง ค่าข้างบนบอกให้รู้แล้ว
ไฟล์นี้ส่งค่าจริงสองค่า ลูกบิด (Eva Kit: ลูกบิดสีน้ำเงิน · Dev Kit: VR1 — sensors.pot อ่านตัวนี้ตัวเดียว) กับค่าเอียงแกน z จาก BMI270 ทั้งสองบอร์ดมีทั้งคู่ คนหนึ่งหมุน อีกคนเอียง คนที่นั่งดูอีกฝั่งจึงพิสูจน์ได้ด้วยมือตัวเองว่าขยับที่นี่แล้วเลขที่โน่นขยับตาม ไม่ใช่ตัวเลขสุ่มที่ใครก็เถียงได้
mqtt ของบอร์ดไม่เข้ารหัส และ broker วันนี้เป็นของสาธารณะ ห้ามส่งของที่เป็นความลับ (บทเรียน 4.7–4.9 ค่อยย้ายไปทางเข้ารหัส) · เฟิร์มแวร์ส่งแบบ retain ไม่ได้ หน้าเว็บที่เปิดช้าเห็นแค่ใบถัดไป ไฟล์จึงส่ง 60 ใบ ใบละ 2 วินาที
ตาคุณ อยู่ท้ายไฟล์ เปิดหน้าเว็บของทีม หมุนลูกบิดหรือเอียงบอร์ด จดว่าเห็นใบแรกที่
nเท่าไร แล้วลองตั้งTEAMชนกับทีมข้าง ๆ ชั่วคราว ดูว่าใครถูกเตะออก
my_first_reader.htmlลิงก์ของหน้าอ่านค่า เปลี่ยน team05 ท้ายลิงก์เป็นรหัสของคุณ · หน้านี้บนเว็บ AIC รับเฉพาะรหัสรูป teamNN ถ้าใช้รหัสของตัวเองอย่าง nok4821 ให้ดาวน์โหลด my_first_reader.html ของรีโพนี้ไปเปิดในเบราว์เซอร์แทน
// ถ้าดาวน์โหลดไฟล์ไปเปิดเอง แก้บรรทัดนี้แทนการต่อท้ายลิงก์
let TEAM = "teamXX"; // รหัสเดียวกับ TEAM ในไฟล์ Python เช่น "nok4821" (เรียนเป็นกลุ่มใช้เลขที่ผู้จัดแจก เช่น "team05")
bento-aiot/<TEAM>/# แล้ววาดหนึ่งกล่องต่อหนึ่งคีย์ของ JSON (knob az n ...) และมีปุ่มส่งเสียงกับเปิดปิดไฟ LED 0 ที่ส่งเข้า .../cmd ให้ไฟล์ 06bento-aiot/+/telemetry กับ bento-aiot/+/event เห็นทุกทีมเป็นการ์ด และส่งคำสั่งเข้า bento-aiot/<ทีม>/cmd ได้-emu ท้าย client_id เอง จึงไม่เตะบอร์ดจริงของทีม · ถ้าต่อไม่ได้ภายใน 5 วินาที มันถอยไปใช้ broker จำลองในตัวและบอกใน Consoleไม่มีไฟล์หน้าเว็บของเรา ใช้หน้าทดลองของผู้ให้บริการแทนได้ https://www.hivemq.com/demos/websocket-client/ ตั้ง host
broker.hivemq.comport8884เปิด SSL แล้ว subscribebento-aiot/<ทีมของคุณ>/#
06_command_comes_back.py — คำสั่งเดินทางกลับมาif not mqtt.subscribe(TOPIC_CMD): # ต้องมาหลัง connect() เสมอ และต้องทำซ้ำถ้าสายหลุดแล้วต่อใหม่
stop_here("subscribe ไม่ผ่าน", "broker ไม่ยอมให้ฟังหัวข้อ " + TOPIC_CMD)
...
msg = mqtt.get_message() # ไม่บล็อก คืน None ทันทีเมื่อยังไม่มีอะไรมา
...
if msg is not None:
...
topic, raw = msg # topic เป็น str ส่วน payload เป็น bytes
...
cmd = json.loads(raw.decode()) # ต้อง .decode() ก่อนเสมอ
...
action = cmd.get("cmd", "")
...
elif action == "led":
...
led = gpio.led(n) # ของบนโต๊ะเราขยับ เพราะคนที่อยู่คนละที่พิมพ์มา
if on:
led.on()
ไฟล์ 05 ส่งออกอย่างเดียว ไฟล์นี้เติมทางกลับให้ครบวง คำสั่งที่บอร์ดรู้จักมีสาม beep ให้ร้อง led ให้ไฟติดหรือดับ และ say ให้ขึ้นข้อความบนจอ ทุกคำสั่งมาในรูป JSON เช่น {"cmd":"led","n":0} — n คือดัชนีดวงตาม gpio.board_info()["led_names"] ของบอร์ดนั้น ถ้าสั่งดวง 0 แล้วมองไม่เห็นบนบอร์ดของทีม (บน Dev Kit ดวง 0 คือ LED1 บนโมดูล) ให้ลอง n ของดวงที่ชื่อขึ้นต้นด้วย RGB_ จากช่อง LED ของหน้ารวม mqtt_dashboard.html เพราะหน้าอ่านค่าของทีมมีปุ่มแค่ดวง 0 · วันนี้คนส่งคือปุ่มบนหน้าเว็บ my_first_reader.html ซึ่งส่งเข้า bento-aiot/<ทีม>/cmd
ครึ่งหนึ่งของไฟล์นี้คือ การไม่เชื่อคนส่ง เพราะบน broker สาธารณะ ใครที่รู้ชื่อหัวข้อก็ส่งเข้ามาได้ ข้อความจึงมาจากคนที่เราไม่รู้จักได้จริง เขาอาจพิมพ์ผิด อาจส่งของที่ไม่ใช่ JSON หรืออาจสั่งดวงไฟที่บอร์ดนี้ไม่มี ทั้งสามกรณีต้องจบลงที่ข้อความบนจอ ไม่ใช่ที่โปรแกรมตาย · ก่อนจบ ไฟล์นี้ปิดไฟทุกดวงเสมอ ไม่ทิ้งบอร์ดไว้ในสถานะที่คำสั่งสุดท้ายบังเอิญตั้งไว้ — เป็นนิสัยที่ระบบซึ่งต้องแขวนอยู่หน้างานหกเดือนขาดไม่ได้
นี่คือคำตอบของเกมต้นบทเรียน ทั้งห้องส่งพร้อมกัน แต่บอร์ดถามกล่องทุก 100 ms และกล่องมีช่องเดียว ใบที่มาถึงระหว่างนั้นทับกันเอง · ตาคุณ อยู่ท้ายไฟล์ ตั้ง
POLL_MS = 3000แล้วกดปุ่มบนหน้าเว็บสามครั้งรวดในวินาทีเดียว แล้วนับว่าตัวเลขบนจอขึ้นกี่ใบ เทียบกับสามใบที่ส่งไปจริง
ชุดบทเรียนก่อนหน้าเราเห็นบอร์ดอ่านของจริงและวาดขึ้นจอได้ วันนี้เราเห็นว่าของจริงตัวเดียวกันนั้นเดินทางออกไปได้ไกลแค่ไหน และเดินกลับมาสั่งงานได้ด้วย โดยที่โค้ดฝั่งเรายังเป็นภาษาไพทอนไม่กี่สิบบรรทัดเหมือนเดิม
ไม่มีบรรทัดไหนในสองไฟล์นี้ที่ต้องเขียนเรื่องคลื่นวิทยุ เรื่องโปรโตคอล หรือเรื่องการต่อ TCP เลย เพราะเฟิร์มแวร์ทำให้หมดแล้ว งานที่เหลือของเราคือ ตัดสินใจว่าจะส่งอะไรออกไป และจะยอมรับคำสั่งอะไรกลับมา
อีกสิบชุดบทเรียนข้างหน้าไม่มีอะไรใหญ่กว่าภาพนี้ มีแต่การทำให้มันปลอดภัยขึ้น เร็วขึ้น และฉลาดขึ้น — โครงยังเป็นภาพเดิม
07_platform_in_one_call.py — อ่านนอกเวลา วันนี้ยังต่อไม่ได้cfg = tesaiot.config() # คลังค่าตั้งอยู่บนแฟลชของบอร์ด อ่านได้โดยไม่ต้องมีเน็ต
...
if PLATFORM_BROKER == "": # วันนี้ยังว่าง: แสดงคลังค่าตั้งแล้วจบ ไม่เขียนแฟลช
...
raise SystemExit
tesaiot.config_set("broker", PLATFORM_BROKER) # รับข้อความทั้งสองอาร์กิวเมนต์
...
tesaiot.connect() # TLS พอร์ต 8883/8884 เสมอ ไม่อ่านคีย์ "port"
# สั่งให้เริ่มต่อ แล้วคืนค่าทันที ไม่ได้รอให้ต่อเสร็จ
...
if tesaiot.is_connected(): # จึงต้องวนถามเอง
...
if waited >= WAIT_MS: # และต้องมีกำหนดเวลาเลิกรอเสมอ
break
...
if tesaiot.publish(body): # ไม่ต้องบอกหัวข้อ เฟิร์มแวร์ประกอบให้จาก device_id
ไฟล์นี้ต่อ broker.hivemq.com ไม่ได้ และไม่ได้ตั้งใจให้ต่อ tesaiot.connect() ต่อแบบเข้ารหัส TLS เสมอ ที่พอร์ต 8883 หรือ 8884 ตามโหมด และไม่อ่านคีย์ port ในคลังค่าตั้งเลย (mqtt_client_config.c:233-242 ในไลบรารีร่วม) ส่วน broker ของวันนี้รับบอร์ดที่ 1883 แบบไม่เข้ารหัส ไฟล์นี้จึงเป็นการอ่านนอกเวลาที่ชี้ไปบทเรียน 4.4–4.6 กับ 4.7–4.9 ถ้ารันวันนี้ มันแสดงคลังค่าตั้งแล้วจบ ไม่เขียนอะไรลงแฟลช
สิ่งที่มันสอนยังใช้ได้: ไฟล์ 05 กับ 06 ต้องพิมพ์ชื่อ broker ลงในโค้ดตรง ๆ ส่วนคลังค่าตั้งนี้ ตั้งครั้งเดียว เก็บบนแฟลชของบอร์ด ถอดไฟแล้วยังอยู่ โปรแกรมตัวถัดไปถาม config() เอาได้เลย
tesaiot.connect() ต่างจาก wifi.connect() ตรงที่มัน ไม่บล็อก ค่าที่คืนมาแปลว่า "รับคำสั่งไปแล้ว" ไม่ได้แปลว่า "ต่อได้แล้ว" ใครเขียน if tesaiot.connect(): แล้วส่งต่อทันทีจะได้ OSError เพราะสายยังไม่ขึ้น WAIT_MS คือกำหนดเวลาเลิกรอ ซึ่งแยกโปรแกรมที่ล้มเหลวอย่างสุภาพ ออกจากโปรแกรมที่ค้างจนคนดูต้องถอดไฟ
ตาคุณ อยู่ท้ายไฟล์ วันนี้จดค่า
portกับtls_modeที่จอแสดง แล้วตอบว่าทำไมportที่เห็นอาจไม่ใช่พอร์ตที่connect()ใช้จริง · วันที่ได้ชื่อ broker ของแพลตฟอร์มในบทเรียน 4.4–4.6 ค่อยทำข้อถอดสาย USB แล้วดูว่าค่ายังอยู่ไหม
03_your_link_rule.py — กฎที่คุณเขียนเอง บอร์ดตรวจให้NEED = 3 # ต้องเสียติดกันกี่รอบถึงจะรายงาน
WANT_REPORTS = 2 # เทปชุดนี้มีปัญหาจริงอยู่สองช่วง
...
# ----- เติมส่วนนี้เอง (งานของคุณ) -----
def is_usable(online, ip):
... # docstring ในไฟล์บอกว่า online กับ ip คืออะไร และใบ้วิธีเทียบสตริงไว้แล้ว
return online # ตอนนี้มันดูแค่ครึ่งเดียวของโจทย์
# ----- จบส่วนที่ต้องเติม -----
ไฟล์นี้ ไม่ต้องต่อเน็ตจริงและไม่ต้องไปปิดเราเตอร์ เพราะมันเล่นเทปผลตรวจลิงก์ที่บันทึกไว้แล้วจากบอร์ดจริง ระหว่างที่มีคนเดินถือมันออกนอกห้องแล้วเดินกลับมา รันซ้ำกี่รอบก็ได้ผลเดิม ซึ่งเป็นสิ่งที่การทดลองกับเน็ตจริงให้ไม่ได้ — กฎที่ทดสอบซ้ำไม่ได้ คือกฎที่ยังไม่รู้ว่าถูก
โจทย์คือลิงก์ที่ใช้ได้จริงต้องครบสองอย่างพร้อมกัน ต่ออยู่ และ มีเลข IP ที่ใช้ได้ — ซึ่งคือกับดัก "0.0.0.0" จากไฟล์ 01 ที่กลับมาอีกครั้ง คราวนี้มาในรูปของโจทย์ และหลังจากรันไฟล์ 05 มาแล้ว ทุกคนจะรู้ว่าทำไมมันสำคัญ เพราะลิงก์ที่ไม่มี IP คือลิงก์ที่ publish() ไปไม่ถึงไหน
จอบนบอร์ดคือเฉลย แก้แล้วรันใหม่จนเลขสองตัวตรงกัน ไม่ต้องรอผู้สอน · ตาคุณ อยู่ท้ายไฟล์ ลองเปลี่ยน
NEEDเป็น 5 แล้วดูว่าเราพลาดอะไรไป
ต้องทำในบทเรียน · เปิดตามลำดับนี้ ทั้งชุดราว 135 นาที โดยสไลด์แทรกอยู่ระหว่างการรันแต่ละไฟล์
| ลำดับ · เวลา | ไฟล์ | ลงมือทำอะไร แล้วจะเข้าใจอะไร |
|---|---|---|
| 1 · 25 นาที | 01_wifi_first_connect.py |
ต่อเน็ตให้ได้ จับเวลาทั้งรหัสถูกและรหัสผิด · รู้ว่าจอที่นิ่งไม่ได้แปลว่าแฮงก์ และ "0.0.0.0" หลอกเราได้ |
| 2 · 15 นาที | 04_scan_the_room.py |
ให้บอร์ดฟังคลื่นทั้งห้อง · แยก "ไม่ได้ยินวงนั้นเลย" ออกจาก "ได้ยินแต่รหัสผิด" ได้ด้วยตัวเอง |
| 3 · 25 นาที | 02_link_uptime.py |
ประกอบสี่โมดูลเป็นจอเฝ้าลิงก์หนึ่งใบ แล้วเดินถือบอร์ดออกไปให้มันหลุดจริง |
| 4 · 25 นาที | 05_value_leaves_the_board.py |
ส่งค่าลูกบิดจริงออกจากบอร์ด · เห็นบันไดสามขั้นและรู้ว่าตัวเองไปหยุดที่ขั้นไหน |
| 5 · 25 นาที | 06_command_comes_back.py |
รับคำสั่งจากที่ไกลมาสั่งไฟบนบอร์ด · เข้าใจว่ากล่องรับมีช่องเดียวแปลว่าอะไร |
| 6 · 20 นาที | 03_your_link_rule.py |
เขียนกฎ is_usable() เองจนเลขสองตัวบนจอตรงกัน · "ต่ออยู่" ไม่ได้แปลว่า "ส่งข้อมูลออกได้" |
| อ่านนอกเวลา | 07_platform_in_one_call.py |
คลังค่าตั้งบนแฟลช · วันนี้อ่านอย่างเดียว tesaiot.connect() เป็น TLS เสมอ ต่อจริงบทเรียน 4.4–4.9 |
ทุกไฟล์จบด้วยบล็อก ตาคุณ — อ่านแล้วรันแล้วยังไม่จบ ต้องแก้แล้วรันซ้ำถึงจะจบ
สิ่งที่เฟิร์มแวร์ทำให้แล้ว (70%)
คุมชิปวิทยุ · จับมือกับเราเตอร์ · ขอและต่ออายุเลข IP · ต่อ TCP ไปหา broker · พูดโปรโตคอล MQTT ทั้ง publish และ subscribe · ต่อ WiFi ใหม่เองเมื่อหลุด (ส่วน MQTT client ที่หลุดหรือถูกเตะไม่ต่อใหม่เอง) · ซิงก์นาฬิกาจากอินเทอร์เน็ต · วาดไอคอนบนแถบบนสุด
สิ่งที่เป็นงานของเรา (30%)
ตัดสินใจว่าจะส่ง ค่าอะไร ออกไป และในรูปแบบไหน · ยอมรับคำสั่งอะไรบ้าง และจะทำอย่างไรกับคำสั่งที่พิมพ์มาผิด · นิยามว่า "ลิงก์ใช้ได้" แปลว่าอะไรสำหรับงานของเรา · แล้วรายงานออกมาให้คนที่เดินผ่านหน้าจออ่านรู้เรื่องภายในสายตาเดียว

สี่เรื่องนั้นไม่มีคำตอบสำเร็จรูปในเอกสารของผู้ผลิต เพราะมันขึ้นกับว่าเราจะเอาระบบนี้ไปทำอะไร
mqtt.publish()ใครก็เรียกได้ แต่การตอบว่า "ตกลงเราควรส่งอะไรออกไปให้คนอื่นเห็น" คือส่วนที่ไม่มีใครทำแทนเราได้

examples/ ของบทเรียน 1.4–1.6 แก้ WIFI_SSID กับ WIFI_PASS ให้ตรงกับ Hotspot มือถือของทีม และในไฟล์ 05 กับ 06 แก้ TEAM จาก teamXX เป็นเลขทีมที่ผู้สอนแจก ถ้าลืม ไฟล์จะหยุดตั้งแต่ขั้นแรกพร้อมบอกบนจอ เพราะชื่อทีมซ้ำจะเตะบอร์ดของทีมอื่นหลุด (BROKER ตั้งไว้แล้ว ไม่ต้องแก้) แล้วกด Program to Device
พื้นที่วาดของเรายังเป็น 792 x 398 พิกเซล เท่าเดิม งบ widget ยังเป็น งบของคอร์ส 32 ตัว (เพดานเฟิร์มแวร์ 64) และมุมขวาล่างราว 100x58 ยังเป็นของปุ่ม Console ที่เฟิร์มแวร์จองไว้
ข้อควรระวังเรื่องเครือข่ายของห้อง: เครือข่ายที่ต้อง login หรือกดยอมรับเงื่อนไขบนหน้าเว็บก่อน เช่น WiFi ขององค์กร จะทำให้ connect() คืน True และได้เลข IP ตามปกติ แต่นาฬิกาไม่ขึ้น และ mqtt.connect() จะไปไม่ถึง broker — นั่นคือกรณี "ติดข้อแรก ไม่ผ่านข้อสอง" · เน็ตที่กันพอร์ต 1883 ขาออกก็ไปไม่ถึงเช่นกัน
ชุดบทเรียนก่อนหน้าเราเห็นแล้วว่า CM33 รันโค้ดของเรา ส่วน CM55 วาดจอ วันนี้มีข้อเท็จจริงเพิ่มมาข้อเดียว: ทั้งวิทยุ WiFi และงาน MQTT อยู่ฝั่ง CM33 เหมือนกัน
นั่นอธิบายทุกอย่างที่เราเจอวันนี้ — wifi.connect() บล็อกได้ทั้งโปรแกรม เพราะมันบล็อกคอร์เดียวกับที่รัน Python ของเรา และระหว่างที่มันบล็อก ไม่มีใครเหลือไปเคาะกล่องจดหมายให้ CM55 วาดอะไรใหม่ ส่วน mqtt.get_message() ตรงกันข้าม มันไม่บล็อกเลย จึงต้องเป็นเราที่วนถามเอง
ถ้าเข้าใจสไลด์นี้ จะไม่มีวันเขียนป้ายบอกสถานะไว้หลังบรรทัดที่บล็อกอีกเลย
ผู้สอน: ครึ่งหนึ่งของโมดูลนี้เป็นเรื่องชิปนิรภัย OPTIGA — ทั้งสองบอร์ดคอมไพล์โมดูล `optiga` มาเหมือนกัน ที่ต่างคือสวิตช์บิลด์ `ENABLE_OPTIGA_CLM` ฝั่ง CM33 ซึ่ง Dev Kit เปิดเป็นค่าปริยาย ส่วน Eva Kit ปิดไว้ และ Makefile ของ Eva เขียนกำกับว่ายังไม่ได้พิสูจน์บนฮาร์ดแวร์ · ส่วน `ENABLE_OPTIGA ?= 0` ที่หัวไฟล์ 07 พูดถึงเป็นอีกสวิตช์หนึ่งของฝั่ง CM55 ซึ่งปิดไว้ทั้งสองบอร์ด ไฟล์นี้จึงไม่แตะฝั่งนั้นเลยสักตัวไม่ว่าบอร์ดไหน เรื่องความปลอดภัยของจริงเป็นงานของบทเรียน 4.7–4.9
ทีมละหนึ่งบอร์ด สลับกันเป็นคนพิมพ์ทุกช่วง คนที่ไม่ได้พิมพ์รับหน้าที่จับเวลาและจดตัวเลข