| ช่วง | เวลา | สิ่งที่ต้องพูด | ที่คนมักพลาด |
|---|---|---|---|
| 1 · ปัญหา | 2 นาที | ใครเดือดร้อน เดือดร้อนยังไง วันนี้เขาแก้ยังไงอยู่ | เริ่มด้วยการอวดบอร์ดแทนการเล่าปัญหา |
| 2 · สถาปัตยกรรม | 2 นาที | canvas ห้าช่อง + schema ที่ส่งจริง | ไล่อธิบายโค้ดทีละบรรทัด |
| 3 · demo สด | 4 นาที | ทำให้มันเตือนต่อหน้าคนดู + โชว์ข้อความบน broker + ตัดเน็ตให้ดู | ฉายวิดีโอที่อัดไว้แทนของจริง |
| 4 · ข้อจำกัดและก้าวต่อไป | 2 นาที | สิ่งที่ระบบนี้ยังทำไม่ได้ และถ้ามีเวลาอีกสองสัปดาห์จะทำอะไร | บอกว่า "ไม่มีข้อจำกัด" |
เตรียม แผนสำรอง ไว้เสมอ: WiFi ล่มตอนนำเสนอเป็นเรื่องที่เกิดขึ้นจริง ทีมที่ออกแบบ offline mode ไว้ดี จะเปลี่ยนอุบัติเหตุนั้นให้กลายเป็นจุดขายของตัวเองได้ทันที
ซ้อมจับเวลาอย่างน้อยหนึ่งรอบ สิบนาทีสั้นกว่าที่ทุกทีมคิดเสมอ
ช่วงที่ 4 คือช่วงที่กรรมการดูว่าทีมเข้าใจงานตัวเองจริงไหม ห้ามข้าม
| หัวข้อ | ผ่าน | ยังไม่ผ่าน |
|---|---|---|
| ปัญหาชัด | บอกได้ว่าใครเดือดร้อน วัดความเดือดร้อนเป็นเวลาหรือเงินได้ | เล่าแต่ว่าทำอะไร ไม่บอกว่าเพื่อใคร |
| สถาปัตยกรรมอ่านออก | คนฟังวาด canvas ห้าช่องตามได้ | กระโดดเข้าโค้ดทันที |
| schema สมเหตุผล | ทุกฟิลด์อธิบายได้ว่าปลายทางใช้ทำอะไร มีหน่วยครบ | ส่งค่าดิบทุกอย่างเพราะ "เผื่อไว้" |
| demo สดผ่าน | ระบบเตือนได้จริงต่อหน้าคนดู และเห็นข้อความขึ้น broker | เปิดวิดีโอที่อัดไว้ |
| ทดสอบการพัง | ตัดเน็ตให้ดู แล้วอธิบายพฤติกรรมที่ออกแบบไว้ | ไม่เคยลอง |
| รู้ข้อจำกัดตัวเอง | บอกได้อย่างน้อย 2 ข้อ พร้อมเหตุผลเชิงเทคนิค | ตอบว่าใช้งานได้ทุกกรณี |
| ตรงเวลา | จบใน 10 นาที ครบทั้งสี่ช่วง | เกินเวลา หรือรีบข้ามช่วงที่ 4 |
การซ้อมที่ได้ผลที่สุด: ให้เพื่อนอีกทีมฟังก่อนหนึ่งรอบ แล้วให้เขาเล่ากลับมาว่าเข้าใจว่าทีมเราทำอะไร ถ้าเขาเล่าผิด แปลว่าเรายังเล่าไม่ชัด ไม่ใช่เขาฟังไม่เป็น
ตารางนี้อยู่ในบันทึกการเรียนด้วย ติ๊กให้ครบก่อนขึ้นพูดจริง
s12_capstone_starter.py ทำโจทย์ที่ 6 (เตือนการเอียงของนั่งร้าน) จนจบ นี่คือ คำตอบหนึ่งที่เป็นไปได้ ไม่ใช่คำตอบเดียว
base_roll = 0.0
base_pitch = 0.0
for _ in range(10):
r, p = raw_tilt()
base_roll += r / 10.0
base_pitch += p / 10.0
time.sleep_ms(100)
def read_value():
roll, pitch = raw_tilt()
dr = roll - base_roll
dp = pitch - base_pitch
return smooth.update((dr * dr + dp * dp) ** 0.5) # เอียงไปทางไหนก็นับเป็นการเอียง
สองวินาทีแรกของโปรแกรมใช้จำ "ท่าตั้งต้น" ของอุปกรณ์ เพราะไม่มีใครติดตั้งอะไรได้ระนาบ 0 องศาเป๊ะ ถ้าไม่หักค่าตั้งต้น อุปกรณ์ที่ติดเอียง 6 องศาตั้งแต่วันแรกจะเตือนตลอดไปโดยไม่มีอะไรผิด
การรวม roll กับ pitch ด้วยระยะทางแบบพีทาโกรัส ทำให้ "เอียงไปทางไหนก็นับ" — ถ้าใช้แค่ roll ตัวเดียว นั่งร้านที่เอียงไปข้างหน้าจะรอดสายตาไปเฉย ๆ
ค่าที่วัดเทียบกับตัวเองตอนติดตั้ง มีความหมายกว่าค่าที่วัดเทียบกับแรงโน้มถ่วงเสมอ
if level == pending:
streak += 1
else:
pending = level
streak = 1
if streak >= CONFIRM_N and pending != state:
state = pending
if state == "ALERT":
latched = True
beacon(True) # ของจริงขยับเอง ไม่ต้องรอคนกด
ready = last_alert is None or time.ticks_diff(now, last_alert) >= ALERT_GAP_MS
if ready:
if send(value, state, "event"):
last_alert = now
else:
missed += 1
สามกลไกซ้อนกันอยู่ตรงนี้ และแต่ละอันแก้ปัญหาคนละเรื่อง
ยืนยัน 3 รอบ (CONFIRM_N) กันการสั่นวูบเดียวตอนมีคนเดินชน — 3 รอบ × 200 ms คือหกในสิบวินาที นานพอให้ของจริงยังเกินอยู่ แต่สั้นพอที่จะไม่ช้า
ค้างสถานะ (latched) เพราะเหตุการณ์ที่ไม่มีใครเห็นเท่ากับไม่เคยเกิด สถานะจะค้างจนมีคนกดปุ่มรับทราบบนจอ · บรรทัด beacon(True) ทำสองอย่างพร้อมกันในฟังก์ชันเดียว คือ จุดหลอดจริงบนบอร์ดและจุดไฟบนจอ — จอที่บอกว่าไฟติดทั้งที่หลอดดับ คือจอที่โกหก และวิธีเดียวที่กันได้คือให้ทั้งสองอย่างออกจากบรรทัดเดียวกันเสมอ
เว้นระยะขั้นต่ำ (ALERT_GAP_MS) กันการเตือนรัวตอนค่าแกว่งรอบเกณฑ์ ระบบที่เตือนสามสิบครั้งใน 1 นาที จะถูกคนหน้างานปิดเสียงในสัปดาห์แรก
ระบบเตือนภัยที่คนเลือกจะไม่ฟัง แย่กว่าไม่มีระบบเตือนภัย เพราะมันสร้างความมั่นใจปลอม ๆ
if (not online) and time.ticks_diff(now, t_retry) >= RETRY_MS:
online = go_online()
t_retry = now
if online:
if send(value, state, "back"): # กลับมาแล้วบอกฝั่งรับทันที อย่าให้เขาเดา
sent += 1
...
for ev in ui.poll(): # ต้องเรียกทุกลูป ไม่งั้นจอจะซ่อน widget ราวสองวินาที
if ev["type"] != "clicked":
continue
if ev["handle"] == btn_on.id():
beacon(True) # เปิดไม่ต้องถาม ย้อนกลับได้ด้วยปุ่มข้าง ๆ
elif ev["handle"] == btn_off.id() and not asking:
asking = True # ปิดต้องถาม เพราะมันลบการเตือนของจริงทิ้ง
box.show()
...
elif ev["handle"] == btn_yes.id() and asking:
asking = False
beacon(False)
...
elif ev["handle"] == btn_ack.id() and latched:
latched = False
if send(value, state, "ack"):
sent += 1
ปุ่มทั้งห้าตัวอ่านจาก ui.poll() เดียวกัน และไม่มี widget ตัวไหนถูกสร้างในลูปเลย — กล่องยืนยันกับปุ่มคำตอบถูกสร้างพร้อมหน้าจอแล้วซ่อนไว้ตั้งแต่ต้น (สไลด์ "ท่าที่ 3 ต่อ") การสร้างของตอนคนกำลังรอคำตอบ คือการเพิ่มความหน่วงในจังหวะที่แย่ที่สุด · send() คืน False เมื่อสายหลุด จึงนับ sent เฉพาะใบที่ออกไปจริง — ตัวเลขบนจอต้องไม่โกหก
กดรับทราบแล้วส่ง
ack· กลับมาออนไลน์แล้วส่งback— ฝั่งรับต้องไม่ต้องเดาว่าบอร์ดหายไปไหน
ท่า 1 Sense มาก่อน เพราะถ้าค่าที่อ่านยังเชื่อไม่ได้ ทุกอย่างที่สร้างทับบนมันคือการตกแต่งความผิดพลาด
ท่า 2 Decide มาที่สอง เพราะสถานะเป็นสิ่งที่ทั้งจอและ broker ใช้ร่วมกัน ตัดสินให้จบที่เดียวก่อน แล้วอีกสองฝั่งค่อยไปหยิบใช้ — ไม่ใช่ต่างคนต่างคิดแล้วได้คำตอบไม่ตรงกัน
ท่า 3 Show มาก่อน Send เพราะจอไม่พึ่งเน็ต ถ้าเรียงกลับกัน ทีมมักเผลอเขียนโค้ดที่จอจะอัปเดตก็ต่อเมื่อส่งสำเร็จ ซึ่งเป็นบั๊กที่หาเจอยากมากตอนสาย WiFi ดี
ท่า 4 Send มาที่สี่ เพราะมันคือส่วนที่พึ่งพาสิ่งที่เราควบคุมไม่ได้มากที่สุด
ท่า 5 กันเน็ตหลุด มาสุดท้าย เพราะจะเขียนได้ ต้องรู้ก่อนว่ามีอะไรจะพังบ้าง — และมันคือส่วนที่ทีมส่วนใหญ่ไม่มีเวลาเขียน ถ้าไม่วางไว้ในลำดับตั้งแต่ต้น
เรียงตามความน่าเชื่อถือจากมากไปน้อย: เซนเซอร์ → ตรรกะ → จอ → เน็ต
06_sense_decide_act_report.py ต่อห้าท่าที่ฝึกมาให้เป็นวงเดียว และรันได้แม้ไม่มีเน็ต
วัด → ตัดสิน → สั่งของจริง → รายงาน แล้ววนกลับ · บนจอมีแถบสี่ช่องที่สว่างทีละช่องตามขั้นที่กำลังทำ ผู้เรียนจึงเห็นวงจรเดินด้วยตา ไม่ต้องจินตนาการ
ขั้นที่ 1 วัดอุณหภูมิ ผ่าน read_temp() ในไฟล์ — sensors.snapshot() ไม่มีช่องอุณหภูมิบนบอร์ดไหนเลย ไฟล์จึงถามก่อนว่ามี sensors.sht40 ไหม: บน Dev Kit ได้อุณหภูมิห้องจริง ส่วน บน Eva ลูกบิดเล่นบทแทน (0–100 % = 15–45 °C) และ console บอกไว้ว่าค่ามาจากไหน — หมุนข้ามเกณฑ์ได้บนโต๊ะ · ขั้นที่ 3 สั่งของจริง คือหลอดที่เลือกตามชื่อด้วย led_named("RGB_GREEN", "LED2") — เขียวทั้งสองบอร์ด ตรงกับป้าย "เปิด" บนจอ
ขั้นที่ 2 คือขั้นที่ทีมส่วนใหญ่ทำพลาด — ถ้าใช้เกณฑ์ค่าเดียว ค่าที่แกว่งรอบเกณฑ์พอดีจะสั่งเปิดปิดสลับกันหลายครั้งต่อวินาที รีเลย์จริงพังด้วยวิธีนี้ และไม่มี error ให้จับสักตัว ไฟล์นี้จึงใช้สองเกณฑ์ เปิดที่ 27.5 ปิดที่ 26.5 ช่องว่างหนึ่งองศาระหว่างสองค่าคือสิ่งที่กันไว้
ข้อสอบของโครงนี้อยู่ที่ท้ายไฟล์ — เปลี่ยนขั้นที่ 1 จากอ่านอุณหภูมิไปอ่านเสียง mic.level() โดยไม่แตะขั้น 2 ถึง 4 เลย ถ้าแก้ที่เดียวจบ แปลว่าทีมแยกส่วนถูกต้องแล้ว และเปลี่ยนโจทย์ได้โดยไม่ต้องเขียนใหม่ทั้งไฟล์
โครงนี้ใช้กับงานจบของทุกทีมได้ — เปลี่ยนแค่ว่า วัดอะไร · ตัดสินด้วยกฎอะไร · สั่งอะไร
สิ่งที่ติดตัวไปแม้เปลี่ยนบอร์ดเปลี่ยนภาษา: การเริ่มจากปัญหาไม่ใช่จากเทคโนโลยี · การแยกการตัดสินใจออกจากการแสดงผลและการส่งข้อมูล · การออกแบบพฤติกรรมตอนพังตั้งแต่ต้น ไม่ใช่ตอนเจอปัญหา · การสื่อสารคุณค่าให้คนที่ไม่ได้เขียนโค้ดเข้าใจ
ดูเพิ่ม (25 นาที · ดูเป็นการบ้าน): Build Real-Time IoT Dashboard: Node-RED + InfluxDB + Grafana + MQTT — IoT Frontier — 24:54 — ปลายทางของเส้นทางที่ 4 หน้าตาเป็นอย่างไรเมื่อมีอุปกรณ์หลายตัวและต้องเก็บย้อนหลัง
เครื่องมือจะเปลี่ยนทุกสามปี วิธีคิดสี่ข้อข้างบนอยู่กับเราได้ทั้งอาชีพ
ทั้งสี่มุมใช้โครงโปรแกรมเดียวกับที่ทีมกำลังจะเขียนวันนี้ ต่างกันที่โจทย์และเกณฑ์เท่านั้น
เส้นทาง 1 · ยกระดับความปลอดภัยของผลงานตัวเอง
เปลี่ยนจาก mqtt พอร์ต 1883 เป็น tesaiot.connect() พอร์ต 8884 ที่เข้ารหัส TLS (ต้อง provision ตัวตนอุปกรณ์รายทีมก่อน) แล้วอธิบายให้ได้ว่า serverTLS ปกป้องอะไร และไม่ปกป้องอะไร
เส้นทาง 2 · ให้คนหน้างานตั้งค่าเองได้ โดยไม่ต้องแก้โค้ด
ทุกไฟล์ในคอร์สนี้พิมพ์ WIFI_SSID กับรหัสผ่านค้างไว้บนหัวไฟล์ ซึ่งใช้ได้ตอนฝึกบนโต๊ะแต่ใช้ไม่ได้กับของที่ส่งมอบจริง — ช่างที่ไปติดตั้งไม่มีทั้งคอมพิวเตอร์และรหัสผ่าน Wi-Fi ของลูกค้าอยู่ในมือตั้งแต่แรก เส้นทางนี้ใช้ของที่เรียนไปแล้วทั้งหมด: wifi.scan() ไล่ดูว่ามีวงอะไรอยู่แถวนั้น wifi.softap() เปิดวงของบอร์ดเองให้ช่างต่อเข้ามาด้วยมือถือ แล้ว tesaiot.config_set() เก็บสิ่งที่ช่างกรอกลงแฟลช ครั้งต่อไปบอร์ดต่อเองได้เลย นี่คือขั้นตอนที่เครื่องมือช่างและกล้องติดรถทุกยี่ห้อทำกัน และไม่ต้องใช้ API ตัวใหม่แม้แต่ตัวเดียว
เส้นทาง 3 · ทำให้มันอยู่ได้เป็นเดือน
ของที่ติดตั้งจริงต้องคิดเรื่องไฟ การกู้คืนตัวเองเมื่อค้าง การอัปเดตเฟิร์มแวร์จากระยะไกล และการดูแลอุปกรณ์เป็นร้อยตัวพร้อมกัน — นี่คือวิศวกรรมคนละครึ่งของงานที่เราเพิ่งทำ
เส้นทาง 4 · ขยายจากหนึ่งตัวเป็นฝูง
สิบบอร์ดในโรงงานเดียวกันต้องมี topic ที่ออกแบบมาให้ query ได้ มี dashboard รวม และมีวิธีบอกว่าตัวไหนเงียบไปตั้งแต่เมื่อไร ลองออกแบบโครงสร้าง topic สำหรับ 50 อุปกรณ์ดู แล้วจะเห็นว่าทำไมชื่อ topic ถึงสำคัญ
เลือกหนึ่งเส้นทาง เขียนลงบันทึกการเรียน แล้วลงมือสัปดาห์นี้ — ความตั้งใจที่ไม่มีวันเริ่ม จะไม่เคยเริ่ม
ui.SpanGroup
07_spangroup_event_log.py สร้าง เหตุการณ์ในภาพเป็นชุดที่ไฟล์นั้นกำหนดไว้เอง ไม่ใช่เหตุการณ์ที่บอร์ดเจอจริงสไลด์ "ส่งเหตุการณ์ ไม่ใช่สตรีมดิบ" ของชุดบทเรียนนี้บอกว่าอะไรควรส่งขึ้นไป — และของชุดเดียวกันนั้นควรอยู่บนจอด้วย แต่พอเขียนจริง ทุกทีมทำเหมือนกันหมด คือ Label หลายบรรทัดแล้วเปลี่ยนสีข้อความตามความรุนแรง ซึ่งตกเกณฑ์หน้าจอของหลักสูตร ทันที
| วิธีบอกความรุนแรง | ผ่านการทดสอบขาวดำไหม |
|---|---|
| สีข้อความอย่างเดียว | ไม่ผ่าน — แปลงเป็นเกรย์สเกลแล้วสามสถานะกลายเป็นเทาเหมือนกันหมด |
| ขนาดตัวอักษร | ผ่าน |
| เส้นใต้ / เส้นขีดกลาง | ผ่าน |
| สี บวก อย่างใดอย่างหนึ่งข้างบน | ผ่าน และคนตาบอดสีอ่านได้ด้วย |
ลองเอง 30 วินาที — ถ่ายจอด้วยมือถือแล้วเปิดโหมดขาวดำ ถ้ายังแยกสามระดับออก หน้าจอนั้นผ่าน เกณฑ์หน้าจอของหลักสูตร
ui.SpanGroup — ท่อน ปากกา และแฮนเดิลที่ไม่มีui.SpanGroup คือย่อหน้าเดียวที่ประกอบจาก "ท่อน" หลายท่อน แต่ละท่อนมีขนาด สี และเส้นใต้ของตัวเองได้ จึงใส่ทั้งสองช่องทางลงในบรรทัดเดียวได้โดยไม่ต้องเปลือง widget — สามท่อนต่อหนึ่งเหตุการณ์ใน 07_spangroup_event_log.py:
color, decor = LEVEL[level] # decor ของสองระดับบนคือ ui.SPAN_UNDERLINE
log.pen(COL_DIM)
log.add_span("%3d s " % at, 20) # เวลา ตัวเล็ก สีจาง
log.pen(color)
log.add_span(level, 24, decor) # ระดับ สี + เส้นใต้
log.pen(COL_TEXT)
log.add_span(" " + text + "\n", 24)
.pen() เป็นปากกา ไม่ใช่คำสั่งทาสีของที่มีอยู่แล้ว — มีผลกับท่อนที่เติมหลังจากนั้นเท่านั้น ลืม pen ท่อนที่สามแล้วทั้งบรรทัดจะเป็นสีของระดับ
ท่อนไม่มีแฮนเดิล จึงแก้ทีละท่อนไม่ได้ ต้อง .clear_items() แล้วเขียนใหม่ทั้งย่อหน้า ซึ่งบังคับให้ "ความจริง" อยู่ในตัวแปรฝั่ง MicroPython — หลักเดียวกับที่ใช้มาทั้งคอร์ส คือมีที่เดียวที่มีสิทธิ์เปลี่ยนสถานะ
เวลาตัวเล็กสีจาง · ระดับสีตามความรุนแรงพร้อมเส้นใต้ · เนื้อความสีปกติ — สองช่องทางในบรรทัดเดียว คือสิ่งที่
Labelหลายบรรทัดทำไม่ได้
ui.Calendar
08_calendar_sets_the_clock.py สร้าง วันที่ 2015-01-01 บนจอคือค่าที่นาฬิกาของบอร์ดตอบจริงหลังเปิดเครื่อง ไม่ได้พิมพ์ไว้ในสไลด์ย้อนกลับไปที่ฟิลด์ t ใน schema ของโครงเริ่มต้น — สไลด์เขียนไว้ว่า "เวลาบนบอร์ด ใช้เรียงลำดับ" ไม่ได้เขียนว่าวันที่ เพราะมันไม่ใช่ t คือ ticks_ms ซึ่งนับจากตอนเปิดเครื่อง
เหตุผลอยู่ในเฟิร์มแวร์: machine_rtc.c ตั้ง RTC_INIT_YEAR เป็น 15 บนฐานปี 2000 และคอมเมนต์ของ reset เขียนว่า "Resets RTC to 1st Jan' 2015" — ทุกครั้งที่ตัดไฟ นาฬิกาของบอร์ดกลับไปที่ 1 ม.ค. 2015 เสมอ ทีมที่ส่งวันที่ขึ้น platform โดยไม่ได้ตั้งนาฬิกา จะได้ข้อมูลปี 2015 ทั้งชุด และไม่มีใครสังเกตจนกว่าจะเอาไปทำกราฟย้อนหลัง
| ที่มาของวันที่ | มีบนโต๊ะทดลองนี้ไหม |
|---|---|
| NTP ผ่านอินเทอร์เน็ต | ต้องมีเน็ต และตั้งเองไม่ได้จาก MicroPython ในพอร์ตนี้ |
| เวลาที่ platform แนบมากับข้อความ | ได้ แต่เป็นเวลาของปลายทาง ไม่ใช่ของบอร์ด |
| คนบอกบอร์ดผ่านหน้าจอ | ได้ทันที และเป็นทางเดียวที่ไม่ต้องพึ่งใคร |
ปี 2015ในข้อมูลของทีมไหน คือลายเซ็นของนาฬิกาที่ไม่เคยถูกตั้ง — จำหน้าตาของมันไว้
ui.Calendar — สองอย่างที่ผิดคาดและต้องรู้ui.Calendar คือ widget ของงาน "คนบอกบอร์ดผ่านหน้าจอ" — และมันสร้างวันที่ผิดปฏิทินไม่ได้ ต่างจากการวาง Spinbox สามช่องซึ่งยอมให้ป้อน 31 กุมภาพันธ์
min max value ที่นี่ไม่ใช่ช่วงค่า อย่าง Slider หรือ Bar แต่คือ ปี · เดือน · วัน (ui_widget_mgr.c บรรทัด 2117 ใน BENTO-TESAIoT-libraries/claw) ใส่ค่านอกพิสัยแล้วมันเงียบ ๆ ถอยไปใช้ 2026-01-01 ไม่มี errorlv_calendar_get_pressed_date() ที่ตอบไม่ผ่านเมื่อไม่ได้แตะวัน — รู้ได้เฉพาะตอนคนแตะวัน ไม่ใช่ตอนคนพลิกดูเดือน · ค่าที่ส่งมาคือเลขแปดหลัก YYYYMMDD ก้อนเดียว ต้องแกะเอง
machine.RTCมีอยู่จริงทั้ง Eva Kit และ Dev Kit (ตารางmodmachine.cของพอร์ตร่วมใส่ไว้โดยไม่มีเงื่อนไข) ต่างจากmachine.PWMและmachine.ADCที่ไม่มี — เรียกได้เลยโดยไม่ต้องtry/except



examples/usecase/ — ไม่ได้อยู่ในบทเรียนไหนโดยเฉพาะ หยิบไปใช้กับงานจบได้เลย

examples/usecase/ — ไม่ได้อยู่ในบทเรียนไหนโดยเฉพาะ หยิบไปใช้กับงานจบได้เลยสถาปัตยกรรมและโพรโทคอล
บอร์ดและซอฟต์แวร์
TESAIoT_KIT_PSE84_AI-Micropython-BentoClaw, bsps/TARGET_KIT_PSE84_AI/bsp_features.mk: เพิ่ม DPS368 · SHT40 · เรดาร์) — ภาพบอร์ดจริงยังไม่ได้ถ่ายภาพประกอบ
วิดีโอที่ตรวจแล้วว่าเปิดได้
mic อยู่ที่ ports/psoc-edge/freeze/mic.py และถูกฝังเข้าเฟิร์มแวร์ผ่าน boards/KIT_PSE84_EVAL_EPC2/manifest.py:9 (Eva) และ boards/KIT_PSE84_AI/manifest.py:11 (Dev Kit) จึง import mic ได้เลยโดยไม่ต้องคัดลอกไฟล์ขึ้นบอร์ด · มันหุ้ม machine.PDM_PCM ไว้ให้ทั้งชื่อขา ช่อง MONO_RIGHT อัตราขยาย และการหัก DC ออกจากค่าดิบ · วัดจริงบนบอร์ด 14 ส.ค. 2026 ห้องเงียบ rms 30 เปิดโทนใส่ไมค์ 19335 · การคำนวณความดังอยู่ใน PDM_PCM.stats() ซึ่งเป็นภาษา C และทิ้งเสียงที่ค้างในคิวก่อนอ่าน จึงไม่ช้ากว่าความจริง (วัดได้ 0-48 ms เทียบกับ 496-624 ms ตอนไม่ทิ้ง)sensors.init() และ sensors.scan() ถูกปฏิเสธบน Eva Kit เพราะ CM55 เป็นเจ้าของ SCB0 — modsensors.c:542 (มาโคร EVA_SCB0_REFUSE) และ :549 · ค่าทั้งหมดมาจาก sensors.snapshot() ที่ :825 ซึ่งคืนคีย์ bmi270 / capsense / pot · บน Dev Kit snapshot() คืนคีย์ชุดเดียวกัน (IMU จาก CM33 ตรง, CapSense/pot จากคอร์จอ) และ init() ทำงานได้ — แต่ไม่มีคีย์อุณหภูมิบนบอร์ดไหนsensors.bmi270.temperature() โยน OSError บน Eva Kit — modsensors.c:231 · บน Dev Kit อ่านได้ แต่เป็นอุณหภูมิของชิป IMU ไม่ใช่ของห้องsensors.sht40 (modsensors_sht40.c: temperature() humidity() temperature_humidity()) และ sensors.dps368 (modsensors_dps368.c: pressure() temperature() altitude()) — bsps/TARGET_KIT_PSE84_AI/bsp_features.mk ตั้ง BSP_HAS_SHT40=1 BSP_HAS_DPS368=1 BSP_HAS_RADAR=1 · mqtt เปิดได้เฉพาะพอร์ต 1883 ทั้งสองบอร์ด (โมดูลร่วมใน BENTO-TESAIoT-libraries)s09/09 s10/08 s11/07 s12/06 s12/09) อ่านผ่าน read_temp(): sensors.sht40 เมื่อบอร์ดมี ไม่งั้นลูกบิดแทน (0–100 % = 15–45 °C) และบอกบน console — ยังไม่ได้รันบนบอร์ดจริงทั้งสองทาง (ยังรอรอบทดสอบ)ทุกข้อจำกัดข้างบนมี path และเลขบรรทัดกำกับ ไม่ได้มาจากเอกสารฉบับใด ถ้าเจอที่ไม่ตรงกับบอร์ด แจ้ง erratum ได้เลย

KIT_PSE84_EVAL_EPC2-MicroPython-BentoClaw/sim) ซึ่งรัน ipc_ui.c กับ ui_widget_mgr.c ตัวจริงเดียวกับบอร์ด บนพื้นที่วาด 792×398 (ขนาดเดียวกันบน Dev Kit) · แสดงหน้าจอที่ตัวอย่างสร้าง — หน้าจอของ 09_tileview_swipe_only.py · ค่าที่เห็นในภาพมาจากเซนเซอร์จำลองบนเครื่องโฮสต์ ไม่ใช่ผลการวัดของบอร์ดหลายหน้าจอที่ปัดสลับกันได้ เหมาะกับแดชบอร์ดที่มีมากกว่าที่จอเดียวรับไหว
ราคา: สามช่องกิน 4 แฮนเดิล ตั้งแต่ยังไม่มีอะไรอยู่ในนั้น — ตัว Tileview หนึ่ง บวกช่องละหนึ่ง
ข้อจำกัดที่ต้องออกแบบเผื่อ
scroll_endของที่ต้องเห็นตลอดเวลา ห้ามอยู่ในช่องใดช่องหนึ่ง — สัญญาณเตือนที่อยู่ในช่องที่ผู้ใช้ไม่ได้เปิดอยู่ คือสัญญาณเตือนที่ไม่มีใครเห็น วางไว้นอก Tileview เสมอ