| คำถาม | demo ตอบว่า | product ต้องตอบว่า |
|---|---|---|
| ทำไมต้องมีของชิ้นนี้ | เพราะมันเท่ | เพราะมีคนเสียเงินหรือเสียเวลากับปัญหานี้อยู่ |
| ค่าที่เห็นบนจอคืออะไร | ตัวเลขจากเซนเซอร์ | ปริมาณที่มีหน่วย และมีคนตัดสินใจจากมันได้ |
| ส่งอะไรขึ้นไป | ทุกอย่างที่วัดได้ | เฉพาะสิ่งที่ปลายทางใช้จริง |
| เน็ตหลุดแล้วยังไง | ค้าง หรือ error กลางจอ | จอทำงานต่อ บอกสถานะ แล้วต่อเองเมื่อเน็ตกลับมา |
| ใครดูแลมันต่อ | ไม่มีใคร | มี device id แยกตัว มีสถานะให้ตรวจ |
ช่องขวาทั้งห้าข้อ ไม่มีข้อไหนต้องใช้ API ใหม่ที่เรายังไม่เคยเรียนเลยสักตัว
ระยะทางจาก demo ถึง product สั้นกว่าที่คิด แต่ต้องตั้งใจเดิน ไม่ใช่บังเอิญไปถึง

| คำถาม | คำตอบของชุดบทเรียนนี้ | อยู่ช่วงไหน | |
|---|---|---|---|
| Why | ในเมื่อไม่มี API ใหม่สักตัว ทำไมยังต้องมีชุดบทเรียนนี้ | เพราะระยะทางจาก demo ถึง product ไม่ได้วัดด้วยจำนวนบรรทัด มันวัดด้วย การตัดสินใจ — เลือกโจทย์จากความเจ็บปวดจริง ออกแบบข้อมูลให้ฝั่งรับใช้ต่อได้ และตัดสินไว้ล่วงหน้าว่าตอนพังจะให้เกิดอะไร · ของที่ไม่เคยถูกทดสอบตอนเน็ตหลุด ยังไม่ใช่ของที่ส่งมอบได้ | ครึ่งแรก · demo กับ product · canvas · ออกแบบตอนพัง |
| What | มีอะไรให้ใช้บ้าง | ทุกโมดูลที่คอร์สนี้เปิดไปแล้ว — เก้าโมดูล ตั้งแต่ lcd ถึง tesaiot และวันนี้ ไม่มีชื่อใหม่เพิ่มแม้แต่ชื่อเดียว |
สไลด์บัญชีรวมเก้าโมดูล |
| How | ประกอบยังไงให้ใช้งานได้จริง | กรอก canvas ห้าช่อง Sense → Decide → Show → Send → Act ให้ครบก่อนแตะโค้ด แล้วเติมโครง s12_capstone_starter.py ที่จุด "ทีมเขียนเอง" ห้าจุด |
เก้าไฟล์ตัวอย่าง (01–05 ในบทเรียน 5.2 · 06–09 ในบทเรียน 5.3) + โครงตั้งต้น + นำเสนอ 10 นาที |
ปลายทางที่จับต้องได้ — ของหนึ่งชิ้นที่วางบนโต๊ะแล้วเดินครบวงเอง ตั้งแต่วัด ตัดสิน แสดง ถึงรายงานขึ้น broker และทีมอธิบายได้ว่ามันแก้ปัญหาอะไรให้ใคร รวมทั้งบอกได้ว่ามันยังทำอะไรไม่ได้
บทเรียน 4.7–4.9 เราทำให้ส่งอย่างปลอดภัยได้ · ชุดบทเรียนนี้เราตอบคำถามที่มาก่อนหน้านั้น — จะส่งอะไร ให้ใคร และเขาจะเอาไปทำอะไรต่อ
s12_capstone_starter.py จนรัน end-to-end แล้ว นำเสนอ 10 นาทีปลายทางของวันนี้: บอร์ดวางอยู่บนโต๊ะ จอบอกสถานะ ข้อความไหลขึ้น broker และทีมอธิบายได้ว่ามันแก้ปัญหาอะไรให้ใคร
วันนี้เราไม่ได้เรียน API ใหม่ เราเรียน วิธีตัดสินใจ ว่าจะเอา API ที่มีอยู่ไปทำอะไร

s12_capstone_starter.py ของบทเรียน 5.3โครงของหน้าจอ แบ่งเป็นสามการ์ด
ui.Bar วางทับ ui.Scale — ค่ากับพิสัยอยู่ด้วยกัน และมีป้ายบอกคุณภาพของค่าเองว่าสดหรือค้างนี่คือ starter ไม่ใช่เฉลย — สิ่งที่ให้มาคือ มาตรฐานของหน้าจอ ที่ทีมต้องรักษาไว้ ส่วนเนื้อในเปลี่ยนเป็นโจทย์ของทีมได้ทั้งหมด

| บทเรียน | สิ่งที่ได้ | วันนี้เอามาใช้ตรงไหน |
|---|---|---|
| 1 | ทัวร์เมนู · lcd.print() · แนวคิด "ส่งเฉพาะสิ่งที่มีความหมาย" |
ช่อง Show และ Send ของ canvas |
| 2 | wifi.connect() · เลข IP ของบอร์ด · กฎว่าลิงก์แบบไหนเรียกว่าใช้ได้ |
ช่อง Send — ต้องรู้ว่าสายดีจริงก่อนจะเชื่อว่าส่งถึง |
| 3 | gpio · ลูป polling · จังหวะเวลา |
โครงลูปหลักของโปรแกรม |
| 4 | ui.Button/Switch/Label · ui.poll() · กฎเหล็ก 5 ข้อ |
ปุ่มรับทราบและหน้าจอสถานะ |
| 5 | sensors.pot · capsense · dsp.EMA |
การกรองสัญญาณก่อนตัดสิน |
| 6 | bmi270.motion() · dsp.tilt() |
ค่าตัวอย่างในโครงเริ่มต้น |
| 7 | ui.Chart หลาย series · cadence 200 ms |
กราฟย้อนหลังบนหน้าจอผลงาน |
| 8 | Panel การ์ด · งบ 32 widget (เพดาน 64) · เกณฑ์สองระดับกันป้ายกระพริบ · รันยาว 10 นาที | โครงหน้าจอของผลิตภัณฑ์ และช่อง Decide ที่ป้ายไม่สั่น |
| 9 | wifi.connect/is_connected/ip/ping |
การตรวจสายก่อนส่ง |
| 10 | mqtt.connect/publish/subscribe · topic · JSON |
ช่อง Send |
| 11 | tesaiot.* · serverTLS 8884 · ตัวตนอุปกรณ์ |
ทางยกระดับความปลอดภัยของผลงาน |
ไม่มีอะไรในตารางนี้ที่ทีมยังไม่เคยรันเอง วันนี้แค่เอามาต่อกัน
| โมดูล | มีกี่ชื่อ | เปิดที่บทเรียนไหน | งานจบใช้ตรงช่องไหนของ canvas |
|---|---|---|---|
lcd |
4 — print console clear theme (เฟิร์มแวร์ 2026-09-06 ขึ้นไปเพิ่ม backlight() เป็นห้า) |
บทเรียน 1.1–1.3 | Show — ทางที่ง่ายที่สุดตอนยังไม่มีหน้า HMI |
gpio |
18 — ระดับโมดูล 7 · เมธอดของ LED 8 · ของ Button 3 |
บทเรียน 2.1–2.3 | Act — ทางเดียวในคอร์สนี้ที่สั่งของจริงให้ขยับ |
ui |
132 ชื่อบนโมดูล (นับบน Eva Kit ในบทเรียน 2.4) · บวกเมธอดของ Widget อีก 38 — สิบสี่ตัวที่บทเรียน 3.4–3.6 กางไว้คือส่วนที่กราฟกับการจัดหน้าใช้ |
บทเรียน 2.4–2.6 · 7 · 8 | Show — และเป็นทางที่คนสั่งงานเข้ามาด้วย |
sensors |
10 ชื่อระดับโมดูล (Eva: ห้าตอบ ห้าปฏิเสธ · Dev Kit: init() scan() ทำงานได้ แต่กติกาของคอร์สคือไม่ต้องเรียก) · บวกกลุ่มเซนเซอร์ที่บอร์ดมี — Eva สี่กลุ่ม (bmi270 bmm350 capsense pot) · Dev Kit เพิ่ม sht40 dps368 เรดาร์ |
บทเรียน 2.7–2.9 · 6 · 8 | Sense |
dsp |
16 — 8 คลาส 8 ฟังก์ชัน (สเปกตรัมสองตัวเพิ่ม 2026-08-20) | บทเรียน 2.7–2.9 · 6 · 7 | ระหว่าง Sense กับ Decide — กรองก่อนตัดสิน |
mic |
8 — โมดูล Python ที่ฝังมากับเฟิร์มแวร์ | บทเรียน 3.7–3.9 | Sense ทางเลือก สำหรับโจทย์ที่วัดด้วยเสียง |
wifi |
8 | บทเรียน 4.1–4.3 | ตรวจสายให้แน่ก่อนจะเชื่อว่าส่งถึง |
mqtt |
6 | บทเรียน 4.4–4.6 | Send |
tesaiot |
28 — ใช้ได้จริง 9 ทั้งสองบอร์ด · สิบหกตัวข้ามคอร์ไปหาชิปที่ไม่ได้เปิด (ได้ OSError · เวลาที่เสียยังไม่ได้วัด) · protected_update() ห้ามเรียก (บน Dev Kit เขียนลงชิปจริง) |
บทเรียน 4.7–4.9 | Send แบบยกระดับ ผ่านพอร์ต 8884 |
อย่านับซ้ำ — "ตระกูล IMU สิบสี่ชื่อ" ของบทเรียน 3.1–3.3 ไม่ใช่โมดูลที่สิบ มันคือ sensors.bmi270 ห้า + sensors.bmm350 ห้า + dsp อีกสี่ ที่ซ้อนอยู่ในตารางแล้ว
ของที่คอร์สนี้ไม่เคยมีให้ — ไม่มี machine.PWM .ADC .SPI .Timer · dsp มี fft_mag (ตั้งแต่ 2026-08-20) แต่ไม่มี ulab · Eva Kit ไม่มี DPS368 SHT40 เรดาร์ ส่วน Dev Kit มีทั้งสาม (sensors.sht40 อุณหภูมิ/ความชื้น · sensors.dps368 ความกดอากาศ · sensors.radar()) แต่คอร์สไม่ได้เปิดสอน — ทีมบน Dev Kit ใช้ได้ถ้าอ่านซอร์สเอง และต้องมีแผนสำรองบน Eva เสมอ · optiga เป็นการสาธิตท้ายบทเรียน 4.7–4.9 ไม่อยู่ในเกณฑ์ อย่าวางแผนงานจบทับมัน
ถ้าโจทย์ที่ทีมเลือกต้องการชื่อที่ไม่มีในตารางนี้ ให้รู้ตั้งแต่วันนี้ ไม่ใช่รู้ตอนเหลือเวลาชั่วโมงเดียว — ทางออกคือหา ตัวแทนที่ประกาศตัวว่าเป็นตัวแทน ตามสไลด์ "เมื่อบอร์ดวัดสิ่งที่เราอยากรู้ไม่ได้"
วิธีที่คนส่วนใหญ่เริ่ม แล้วจบไม่สวย: "บอร์ดมี IMU กับเข็มทิศ เราจะทำอะไรกับมันดี"
วิธีที่ได้ของที่มีคนใช้: เริ่มจากสามคำถามนี้ตามลำดับ
พอตอบครบสามข้อ ค่อยถามว่า "บอร์ดของเราวัดอะไรที่พอจะบอกเรื่องนั้นได้บ้าง"
ทีมที่เดินลำดับนี้จะได้โจทย์ที่เล่าให้คนนอกฟังได้ใน 30 วินาที ทีมที่เดินย้อนกลับมักจบด้วยของสวยที่ไม่มีใครอยากได้
โจทย์ที่ดีเล่าจบก่อนที่คนฟังจะทันถามว่า "แล้วไง"

canvas นี้อยู่ในบันทึกการเรียน กรอกให้ครบก่อนแตะคีย์บอร์ด
โจทย์ตัวอย่าง: นั่งร้านชั้นสามของไซต์ก่อสร้าง เอียงผิดปกติแล้วไม่มีใครรู้จนเช้า
| ช่อง | คำตอบของทีมตัวอย่าง |
|---|---|
| Sense | sensors.bmi270.motion() → dsp.tilt() ทุก 200 ms กรองด้วย dsp.EMA(alpha=0.2) วัดเทียบท่าตั้งต้นตอนติดตั้ง |
| Decide | เกิน 8° = เฝ้าดู, เกิน 15° = ผิดปกติ ต้องเกินติดกัน 3 รอบจึงเชื่อ ตัดสินบนบอร์ดทั้งหมด |
| Show | ตัวเลของศาตัวใหญ่ + แท่งระดับ + คำว่า OK/WARN/ALERT + สถานะเน็ต + ปุ่มรับทราบ |
| Send | JSON 6 ฟิลด์ ไป bento/team01/telemetry — ส่งตอนสถานะเปลี่ยน + heartbeat ทุก 30 วินาที |
| Act | หัวหน้าไซต์ได้แจ้งเตือน เดินไปดูภายใน 15 นาที ถ้าเป็นของจริงต่อเข้าไลน์กลุ่มหรือ SMS |
สังเกตว่าช่อง Act เขียนเป็น "คนทำอะไร ภายในกี่นาที" ไม่ใช่ "ส่งขึ้นคลาวด์" — ถ้าปลายทางไม่มีใครทำอะไร ข้อมูลนั้นไม่ต้องส่งตั้งแต่แรก
ตัวอย่างนี้คือโจทย์ที่เฉลย
s12_capstone_starter.pyทำจนจบ
| โจทย์ | วัดด้วยอะไรบนบอร์ดนี้ | เกณฑ์ที่ใช้ได้จริง | ข้อจำกัดที่ต้องพูดตรง ๆ |
|---|---|---|---|
| 1 · เฝ้าความสั่นมอเตอร์ปั๊ม | bmi270.motion() แล้วคิดขนาดความเร่งรวม |
ค่าเฉลี่ยเคลื่อนที่สูงกว่าค่าปกติ 2 เท่า ติดกัน 5 รอบ | บอร์ดวัดความสั่นระดับหยาบ ไม่ใช่ระดับ FFT ของเครื่องมือวัดจริง |
| 2 · ห้องเย็นอุณหภูมิหลุดเกณฑ์ | บน Eva Kit ไม่มีเซนเซอร์อุณหภูมิห้อง → ใช้ตัวแทน: ประตูถูกเปิดค้าง (การเอียงของบานประตู) · บน Dev Kit อ่านตรงได้ จาก sensors.sht40.temperature() |
ประตูขยับแล้วไม่กลับที่เดิมใน 60 วินาที (Eva) · อุณหภูมิเกินเกณฑ์ติดกัน N รอบ (Dev Kit) | ตัวแทนบอกได้แค่ "สาเหตุ" ไม่ได้บอก "อุณหภูมิ" ต้องเขียนไว้ในข้อจำกัด · ทีมที่ใช้ SHT40 ต้องบอกว่างานนี้ย้ายไป Eva ไม่ได้โดยไม่เปลี่ยน Sense |
| 3 · ติดตามการเคลื่อนย้ายทรัพย์สิน | bmi270.motion() + bmm350.heading() |
ทิศเปลี่ยนเกิน 30° หรือมีความเร่งเกินเกณฑ์ = ของถูกยก | จับได้ว่า "ขยับ" แต่บอกไม่ได้ว่า "ไปอยู่ที่ไหน" (ไม่มี GPS) |
| 4 · นับเวลาเครื่องจักรเดินเบา | ความสั่นต่ำกว่าเกณฑ์ต่อเนื่อง = idle | สะสมนาที idle ต่อกะ ส่งสรุปทุก 15 นาที | ต้องปรับเกณฑ์กับเครื่องจริงก่อน ค่าจากการทดลองบนโต๊ะใช้ไม่ได้ตรง ๆ |
| 5 · เฝ้าการใช้งานห้องประชุม | การเคลื่อนไหวจาก IMU + capsense เป็นปุ่มเช็กอิน |
มีคนแตะเช็กอิน = ใช้งานอยู่, ไม่มีการขยับ 10 นาที = ห้องว่าง | ตัวแทนที่หยาบ ของจริงใช้ PIR หรือกล้องนับคน |
| 6 · เตือนการเอียงของชั้นวาง/นั่งร้าน | dsp.tilt() เทียบท่าตั้งต้น |
เกิน 15° ติดกัน 3 รอบ | ต้องยึดบอร์ดให้แน่นจริง ไม่งั้นวัดการเอียงของเทปกาว |
เลือกโจทย์ที่ทีมมีคนเคยเจอปัญหานั้นจริง จะเถียงกันเรื่องเกณฑ์ได้สนุกกว่ามาก

หมายเหตุเรื่องเสียง: ไมโครโฟนใช้จาก Python ได้แล้ว โมดูล mic ฝังมากับเฟิร์มแวร์ทั้งสองบอร์ด (boards/KIT_PSE84_EVAL_EPC2/manifest.py และ boards/KIT_PSE84_AI/manifest.py freeze ไฟล์เดียวกัน) และในอีมูเลเตอร์ — mic.start() แล้ว mic.level() คืนความดัง 0-100 ส่วน mic.rms() กับ mic.peak() คืนค่าดิบ 0-32768 · วัดจริงบน Eva Kit 14 ส.ค. 2026 (ตัวเลขบน Dev Kit ยังไม่ได้วัด) ห้องเงียบได้ rms 30 (ระดับ 7) เปิดโทนใส่ไมค์ได้ 19335 (ระดับ 92) ต่างกัน 645 เท่า ซึ่งห่างพอจะตั้งเกณฑ์ตัดสินได้จริง · level() นับเป็นอ็อกเทฟแบบที่หูได้ยิน ไม่ใช่สัดส่วนตรงของสเกลเต็ม จึงขยับตั้งแต่เสียงพูดปกติ
โจทย์ที่ต้องฟังเสียงเริ่มได้เลยวันนี้ — เกณฑ์พลังงานจาก peak() พอสำหรับโจทย์ระดับชุดบทเรียนนี้แล้ว แล้วครอบด้วยกฎ confirm-N เหมือนค่าจากเซนเซอร์ตัวอื่นทุกประการ
ตัวแทนที่ตอบได้ 1 บิต ก็ยังต้องผ่านกฎ confirm-N — สัญญาณเด้งของหน้าสัมผัสคือ "ค่าสั่นวูบเดียว" ในอีกหน้าตาหนึ่ง

Eva Kit มี IMU เข็มทิศ ปุ่มสัมผัส ลูกบิด และจอ — ไม่มีเซนเซอร์อุณหภูมิห้อง ความชื้น ก๊าซ หรือกระแสไฟ · TESAIoT Dev Kit มีชุดเดียวกัน และเพิ่ม SHT40 (อุณหภูมิ/ความชื้น) DPS368 (ความกดอากาศ) กับเรดาร์ — แต่ก็ยังไม่มีก๊าซ ไม่มีกระแสไฟ เรื่อง proxy จึงเป็นเรื่องของทั้งสองบอร์ด ต่างกันแค่ว่าอะไรบ้างที่ต้องใช้ตัวแทน
sensors.bmi270.temperature() มีชื่ออยู่ในโมดูลก็จริง แต่บน Eva Kit มัน โยน OSError ทุกครั้ง เพราะค่านี้ไม่ได้อยู่ใน snapshot ของคอร์จอ และถึงอ่านได้ (บน Dev Kit อ่านได้) มันคืออุณหภูมิของชิป IMU ซึ่งอุ่นตามการทำงานของบอร์ด ไม่ใช่ของห้อง — อุณหภูมิห้องบน Dev Kit ต้องมาจาก sensors.sht40
ทางออกที่วิศวกรใช้จริงคือ proxy — วัดสิ่งที่วัดได้ ซึ่งสัมพันธ์กับสิ่งที่อยากรู้: ห้องเย็นอุ่นขึ้นไหม → ประตูเปิดค้างหรือเปล่า · เครื่องทำงานอยู่ไหม → ความสั่นของโครง · มีคนอยู่ในห้องไหม → การเคลื่อนไหวกับการแตะปุ่ม
กติกาข้อเดียวของการใช้ proxy: บอกให้ชัดว่ามันคือตัวแทน ทั้งบนสไลด์นำเสนอและในชื่อฟิลด์ของ schema — ตั้งชื่อ door_open_s ไม่ใช่ temp_c
proxy ที่ประกาศตัวว่าเป็น proxy คืองานวิศวกรรม · proxy ที่แอบอ้างเป็นของจริงคือการหลอกลูกค้า
payload ของเราในโครงเริ่มต้นหน้าตาแบบนี้
{"id": "team01", "v": 17.4, "unit": "deg", "state": "ALERT", "kind": "event", "t": 812340}
หกฟิลด์ และทุกฟิลด์มีเหตุผล
| ฟิลด์ | ทำไมต้องมี |
|---|---|
id |
ปลายทางต้องแยกออกว่าข้อความนี้มาจากบอร์ดตัวไหน — บน broker สาธารณะยิ่งจำเป็น |
v |
ค่าที่ตัดสินใจแล้ว ไม่ใช่ค่าดิบสามแกน ปัดทศนิยมให้พอใช้ ไม่ต้องส่ง 6 ตำแหน่ง |
unit |
ตัวเลขที่ไม่มีหน่วยคือตัวเลขที่ตีความผิดได้ ยานอวกาศเคยตกเพราะเรื่องนี้ |
state |
คำตัดสินของบอร์ด ปลายทางไม่ต้องมาคำนวณซ้ำและได้คำตอบไม่ตรงกัน |
kind |
บอกว่านี่คือ event, heartbeat หรือการรับทราบ — คนละความหมาย คนละการกระทำ |
t |
เวลาบนบอร์ด ใช้เรียงลำดับและดูว่าข้อความมาช้าไปแค่ไหน |
กติกาที่ทำให้ schema อยู่ได้นาน: ชื่อฟิลด์คงที่ตลอดโครงการ · เพิ่มฟิลด์ใหม่ได้ แต่ห้ามเปลี่ยนความหมายของฟิลด์เดิม · payload ขาออกกระชับ ต่ำกว่า 1000 ไบต์เสมอ
ฝั่งรับเขียนโค้ดแกะข้อมูลครั้งเดียว ถ้าเราเปลี่ยนชื่อฟิลด์ทีหลัง เขาต้องแก้ทั้งระบบ
บทเรียน 1.1–1.3 เราพูดไว้ว่าหัวใจของ AIoT คือ ตัดสินใจใกล้จุดเกิดเหตุ แล้วส่งขึ้นไปเฉพาะสิ่งที่มีความหมาย วันนี้ถึงเวลาทำจริง
ลองคิดเลขให้เห็นภาพ อ่านค่าทุก 200 ms แล้วส่งทุกค่า
แบบส่งเหตุการณ์: ส่งตอนสถานะเปลี่ยน + heartbeat ทุก 30 วินาที = 2,883 ข้อความต่อวัน ลดลงร้อยกว่าเท่า และปลายทางอ่านง่ายกว่าเดิม
ทำไมยังต้องมี heartbeat: ถ้าเงียบอย่างเดียว ปลายทางแยกไม่ออกระหว่าง "ทุกอย่างปกติ" กับ "บอร์ดตายไปแล้วเมื่อวาน"
ดูเพิ่ม (6 นาที): MQTT Essentials Part 6 — MQTT Topic Best Practices — HiveMQ — 5:50 — การออกแบบลำดับชั้น topic และ wildcard + # ที่ฝั่งรับใช้ query
ความเงียบไม่ใช่ข่าวดี จนกว่าเราจะออกแบบให้ความเงียบมีความหมาย

ดูเพิ่ม (5 นาที): MQTT Essentials Part 10 — Last Will and Testament — HiveMQ — 5:27 — broker ประกาศแทนเราเมื่อเราหายไปแบบไม่บอกกล่าว ซึ่งคือกลไกที่ตอบคำถาม "บอร์ดเงียบไป แปลว่าปกติหรือตาย"
MQTT ถูกออกแบบตั้งแต่ปี 1999 โดยวิศวกรสองคน (Andy Stanford-Clark จาก IBM และ Arlen Nipper) สำหรับงานที่ฟังดูไม่น่าเกี่ยวกับเราเลย: ตรวจวัดท่อส่งน้ำมันกลางทะเลทราย ที่เชื่อมโลกด้วยสัญญาณดาวเทียมซึ่งทั้งช้า ทั้งแพง ทั้งหลุดบ่อย
ข้อจำกัดนั้นเองที่ทำให้โพรโทคอลนี้หน้าตาแบบที่เป็น — ส่วนหัวเล็กมาก มีระดับการรับประกันการส่งให้เลือก และมีแนวคิด "last will" คือข้อความที่ broker จะประกาศแทนเราเมื่อเราหายไปแบบไม่บอกกล่าว
เชื่อมกับวันนี้: ทีมที่ออกแบบ payload ให้เล็กและส่งเป็นเหตุการณ์ กำลังใช้โพรโทคอลตรงตามเจตนาของคนออกแบบเมื่อยี่สิบกว่าปีก่อน ส่วนทีมที่ยิงค่าดิบทุก 200 ms กำลังใช้มันผิดวิธี และจะเจอปัญหาเดียวกับที่ท่อน้ำมันเจอ
เน็ตหลุดกลางงานไม่ใช่กรณีพิเศษ มันคือสภาพปกติของอุปกรณ์ที่ติดตั้งจริง
| สถานการณ์ | demo ทำ | product ต้องทำ |
|---|---|---|
| WiFi หลุด | โปรแกรมตาย หรือค้างรอ | จอวาดต่อ ขึ้นคำว่า offline แล้วนัดลองใหม่ทุก 10 วินาที |
| broker ไม่ตอบ | publish เงียบ ไม่มีใครรู้ |
นับจำนวนครั้งที่ส่งไม่สำเร็จ แล้วโชว์บนจอ |
| เน็ตกลับมา | ต้องรีเซ็ตบอร์ดเอง | ต่อเอง แล้วส่งข้อความบอกว่ากลับมาแล้ว |
| ค่าเซนเซอร์กระโดดวูบเดียว | เตือนทันที คนวิ่งมาดูแล้วไม่เจออะไร | ต้องเกินเกณฑ์ติดกันหลายรอบจึงเปลี่ยนสถานะ |
| เตือนแล้วไม่มีใครอยู่หน้าจอ | ข้อความแวบเดียวแล้วหาย | ค้างสถานะไว้จนมีคนกดรับทราบ |
การตัดสินใจที่ต้องเลือกให้ชัดตั้งแต่วันนี้: ตอนออฟไลน์ ทีมจะ ทิ้ง ข้อมูลหรือ เก็บไว้ส่งทีหลัง
โครงเริ่มต้นเลือกทิ้งแล้วนับไว้ เพราะค่าความเอียงเมื่อสิบนาทีที่แล้วไม่มีประโยชน์กับคนที่กำลังยืนอยู่ใต้ที่นั่งร้าน ถ้าทีมทำเครื่องนับจำนวนชิ้นงาน คำตอบอาจกลับกัน — เก็บไว้ให้ครบสำคัญกว่าความสด
ทั้งสองคำตอบถูกได้ ที่ผิดคือไม่เคยตัดสินใจ แล้วปล่อยให้พฤติกรรมเป็นไปตามบังเอิญ

ตารางที่แล้วเรียงตาม "บทเรียน" ตารางนี้เรียงตาม "โมดูล" — เพราะตอนออกแบบงานจบ คำถามไม่ใช่ "บทเรียนไหนสอนอะไร" แต่คือ "ของที่มีอยู่ในมือทั้งหมดมีอะไรบ้าง"