ภาพพื้นหลังปกบทเรียน

บทเรียน 5.3 — สร้างและนำเสนอ AIoT mini-product

Capstone · AIoT Mini-Product ของทีมเรา: จากโจทย์จริง สู่ของที่ใช้งานได้

โมดูล 5 — Capstone: AIoT Mini-Product

ต่อจากบทเรียน 5.2 — โครงตั้งต้น: Sense Decide Show Send

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

MVP checkpoint — ผ่านชุดบทเรียนนี้เมื่อ

demo end-to-end + นำเสนอ 10 นาที (ปัญหา→สถาปัตยกรรม→demo→ข้อจำกัด)

แปลเป็นสิ่งที่ตรวจได้จริง:

  • [ ] canvas ห้าช่องในบันทึกการเรียนกรอกครบ และเล่าโจทย์ได้ใน 30 วินาที
  • [ ] บอร์ดอ่านเซนเซอร์ → ตัดสิน → แสดงบนจอ ครบวงจร รันต่อเนื่องได้อย่างน้อย 10 นาที
  • [ ] มีข้อความขึ้น broker ที่ topic ของคุณ และเปิดให้คนอื่นดูใน MQTT Explorer ได้
  • [ ] payload เป็น JSON ตาม schema ที่ทีมเขียนไว้ในบันทึกการเรียน มีหน่วยและ device id
  • [ ] ตัดเน็ตแล้วจอยังทำงาน ขึ้นสถานะ offline และต่อกลับเองเมื่อเน็ตกลับมา
  • [ ] หน้าจอผ่านสี่ข้อของแผงควบคุม — ค่ามาพร้อมพิสัย · สถานะเป็นไฟไม่ใช่ตัวอักษรสี · ปุ่มเปิดกับปุ่มปิดแยกกัน · คำสั่งที่ทำให้ของจริงขยับมีกล่องยืนยันที่บอกว่าจะเกิดอะไร
  • [ ] นำเสนอ 10 นาทีครบสี่ช่วง และตอบคำถามข้อจำกัดของระบบตัวเองได้อย่างน้อย 2 ข้อ

ข้อที่ห้าถึงเจ็ดคือข้อที่แยกทีมที่ทำ product ออกจากทีมที่ทำ demo

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

กับดักที่เจอบ่อย

อาการ สาเหตุที่แท้จริง วิธีแก้
จอค้าง widget หายไปพักหนึ่ง ลืม ui.poll() ในลูป เรียก ui.poll() ทุกรอบก่อน sleep_ms
ค่าบนจอวิ่งกระตุก เฟรมหาย ลูปเร็วเกินไป time.sleep_ms(200) สำหรับหน้าจอหนัก
ตัวอักษรเล็กใหญ่ผิดคาด เข้าใจว่า value= ของ Label คือค่าที่แสดง value= คือขนาดฟอนต์ (14/16/20/24/28) ใช้ .text() แสดงค่า
เตือนถี่จนไม่มีใครสนใจ ส่งทุกครั้งที่ค่าเกินเกณฑ์ ส่งตอนสถานะ เปลี่ยน + เว้นระยะขั้นต่ำ + ยืนยันหลายรอบ
ข้อความของคนอื่นโผล่มาปนกัน ใช้ topic ซ้ำกัน หรือ client_id ซ้ำ ใช้รหัสที่ไม่ซ้ำใคร (เช่น nok4821) ทั้งใน bento/<รหัส>/... และ client_id เสมอ
บอร์ดหลุดจาก broker สลับไปมา สองบอร์ดใช้ client_id เดียวกัน ตั้ง DEVICE_ID ให้ไม่ซ้ำ
โปรแกรมค้างนานตอนเริ่ม wifi.connect() บล็อกอยู่ (รหัสผิดจะนานมาก) ตรวจรหัสก่อน แล้วรอให้มันตอบ อย่ารันซ้ำทับ
sensors อ่านค่าไม่ขยับหลังใช้ ui การใช้ ui.* ครั้งแรกหยุด sensor auto-task อ่านเซนเซอร์แบบ sync ในลูปเอง (โครงทำให้แล้ว)
ค่ากระโดดเป็นศูนย์ทุกครั้งที่จอวาด เผลอเรียก sensors.scan() ห้ามใช้ sensors.scan() เด็ดขาดในคอร์สนี้
กดปุ่มในกล่อง ui.MsgBox แล้วไม่มีอะไรเกิดขึ้น ปุ่มในตัว MsgBox ยังไม่ส่งเหตุการณ์กลับมาให้ Python เห็น ใช้ ui.Button จริงสองตัวเป็นคำตอบ สร้างแล้ว .hide() ไว้ แล้ว .show() ตอนถาม
ข้อความในกล่องยืนยันขาดครึ่ง ข้อความของ MsgBox เดินทางกับ CREATE ซึ่งพาได้ 95 ไบต์ ไทยตัวละ 3 ไบต์ (เฟิร์มแวร์ 2026-08-20 ขึ้นไปส่งส่วนเกินซ้ำได้ถึง 126 ไบต์ตามซอร์ส ยังต้องยืนยันบนบอร์ด) เขียนให้สั้นกว่า 31 ตัวอักษร แล้วเปิดภาพดูจริงว่าไม่ถูกตัด
เปลี่ยนสีแท่ง ui.Bar แล้วดูเหมือนแท่งเต็มทั้งราง .color() ของ Bar ไปลงที่รางไม่ใช่แถบที่เต็ม อย่าเปลี่ยนสีแท่งตอนรัน ให้ ui.Led หรือสีตัวเลขเป็นคนบอกสถานะ
ป้ายสถานะกับไฟบนจอไม่ตรงกันชั่วครู่ ป้ายถูกกั้นด้วยประตูวินาที แต่ไฟขยับทุกรอบ เขียนป้ายสถานะตอน "เปลี่ยน" ส่วนประตูวินาทีใช้กับตัวเลขเท่านั้น

กับดักครึ่งหนึ่งในตารางนี้เป็นเรื่องการออกแบบจังหวะเวลา ไม่ใช่เรื่องไวยากรณ์ภาษา — และสี่ข้อล่างสุด ไม่มี error ให้จับสักตัว มีแต่จอที่ผิด

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ลงมือทำ — ห้าจุดที่เขียนว่า "ทีมเขียนเอง"

read_value() ปริมาณของทีม เกณฑ์ใน CONFIG ทดสอบด้วยมือจริง on_state_change นับ จดเวลา เปลี่ยนจอ widget + schema งบรวมไม่เกิน 64 ออฟไลน์: ทิ้งหรือเก็บ ทดสอบตอนเน็ตหลุด ลำดับที่แนะนำ — รันโครงเปล่าให้ผ่านก่อนเสมอ ห้ามข้ามขั้นตั้งเกณฑ์ — ทีมที่ตั้งจากการเดาแล้วไม่เคยทดสอบ จะไปเจอตอนนำเสนอว่ามันไม่เตือน เกณฑ์ที่ยังไม่เคยถูกทดสอบด้วยมือ ไม่ใช่เกณฑ์ มันคือความหวัง
    # ทีมเขียนเอง: เปลี่ยนบรรทัดล่างเป็นปริมาณที่โจทย์ของทีมสนใจจริง ๆ
    return smooth.update(abs(roll))

def on_state_change(old, new, value):
    pass  # ทีมเขียนเอง: ตอนสถานะเปลี่ยนให้เกิดอะไร

    # ทีมเขียนเอง: เพิ่ม widget ของทีม (งบของคอร์ส 32 ตัว เพดานเฟิร์มแวร์ 64 · ตอนนี้ใช้ไป 31 เหลือในงบหนึ่งตัว ถ้าต้องการมากกว่านั้นให้ตัดของเดิมที่ไม่จำเป็นออกก่อน)

    # ทีมเขียนเอง: เพิ่มหรือตัดฟิลด์ให้ตรงกับตาราง schema ที่ทีมออกแบบไว้ในบันทึกการเรียน

                pass  # ทีมเขียนเอง: ออฟไลน์แล้วจะ "ทิ้ง" หรือ "เก็บไว้ส่งทีหลัง"

ลำดับที่แนะนำ: หนึ่ง รันโครงเปล่าให้ผ่านก่อน สอง เปลี่ยน read_value() เป็นของทีม สาม ตั้งเกณฑ์ใน CONFIG แล้วทดสอบด้วยมือจริงว่ามันเตือนตอนที่ควรเตือน สี่ แต่งหน้าจอ ห้า ปรับ schema หก ทดสอบตอนเน็ตหลุด

ห้ามข้ามข้อสาม: ทีมที่ตั้งเกณฑ์จากการเดาแล้วไม่เคยทดสอบ จะไปเจอตอนนำเสนอว่ามันไม่เตือน

เกณฑ์ที่ยังไม่เคยถูกทดสอบด้วยมือ ไม่ใช่เกณฑ์ มันคือความหวัง

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

นำเสนอ 10 นาที — เรียงแบบนี้แล้วคนฟังเข้าใจ

run-sheet สิบนาที — ซ้อมจับเวลาอย่างน้อยหนึ่งรอบ 1 · ปัญหา 2 นาที 2 · สถาปัตยกรรม 2 นาที 3 · demo สด + ตัดเน็ตให้ดู 4 นาที — ช่วงที่ยาวที่สุด 4 · ข้อจำกัด 2 นาที พลาดบ่อย: อวดบอร์ดก่อนเล่าปัญหา พลาดบ่อย: ไล่อธิบายโค้ดทีละบรรทัด พลาดบ่อย: ฉายวิดีโอที่อัดไว้แทนของจริง พลาดบ่อย: บอกว่าไม่มีข้อจำกัด
ช่วง เวลา สิ่งที่ต้องพูด ที่คนมักพลาด
1 · ปัญหา 2 นาที ใครเดือดร้อน เดือดร้อนยังไง วันนี้เขาแก้ยังไงอยู่ เริ่มด้วยการอวดบอร์ดแทนการเล่าปัญหา
2 · สถาปัตยกรรม 2 นาที canvas ห้าช่อง + schema ที่ส่งจริง ไล่อธิบายโค้ดทีละบรรทัด
3 · demo สด 4 นาที ทำให้มันเตือนต่อหน้าคนดู + โชว์ข้อความบน broker + ตัดเน็ตให้ดู ฉายวิดีโอที่อัดไว้แทนของจริง
4 · ข้อจำกัดและก้าวต่อไป 2 นาที สิ่งที่ระบบนี้ยังทำไม่ได้ และถ้ามีเวลาอีกสองสัปดาห์จะทำอะไร บอกว่า "ไม่มีข้อจำกัด"

เตรียม แผนสำรอง ไว้เสมอ: WiFi ล่มตอนนำเสนอเป็นเรื่องที่เกิดขึ้นจริง ทีมที่ออกแบบ offline mode ไว้ดี จะเปลี่ยนอุบัติเหตุนั้นให้กลายเป็นจุดขายของตัวเองได้ทันที

ซ้อมจับเวลาอย่างน้อยหนึ่งรอบ สิบนาทีสั้นกว่าที่ทุกทีมคิดเสมอ

ช่วงที่ 4 คือช่วงที่กรรมการดูว่าทีมเข้าใจงานตัวเองจริงไหม ห้ามข้าม

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

เกณฑ์การนำเสนอ — ใช้ตรวจตัวเองก่อนขึ้นพูด

เจ็ดหัวข้อ — ติ๊กให้ครบก่อนขึ้นพูดจริง ปัญหาชัด — วัดความเดือดร้อนเป็นเวลาหรือเงิน สถาปัตยกรรมอ่านออก — คนฟังวาด canvas ตาม schema สมเหตุผล — ทุกฟิลด์อธิบายได้ มีหน่วย demo สดผ่าน — เตือนได้จริงต่อหน้าคนดู ทดสอบการพัง — ตัดเน็ตให้ดู รู้ข้อจำกัดตัวเอง — อย่างน้อย 2 ข้อ ตรงเวลา — จบใน 10 นาที ครบสี่ช่วง การซ้อมที่ได้ผลที่สุด ให้เพื่อนอีกทีมฟัง แล้วให้เขาเล่ากลับมา สองข้อขวาบนคือข้อที่แยกทีมที่ทำ product ออกจากทีมที่ทำ demo
หัวข้อ ผ่าน ยังไม่ผ่าน
ปัญหาชัด บอกได้ว่าใครเดือดร้อน วัดความเดือดร้อนเป็นเวลาหรือเงินได้ เล่าแต่ว่าทำอะไร ไม่บอกว่าเพื่อใคร
สถาปัตยกรรมอ่านออก คนฟังวาด canvas ห้าช่องตามได้ กระโดดเข้าโค้ดทันที
schema สมเหตุผล ทุกฟิลด์อธิบายได้ว่าปลายทางใช้ทำอะไร มีหน่วยครบ ส่งค่าดิบทุกอย่างเพราะ "เผื่อไว้"
demo สดผ่าน ระบบเตือนได้จริงต่อหน้าคนดู และเห็นข้อความขึ้น broker เปิดวิดีโอที่อัดไว้
ทดสอบการพัง ตัดเน็ตให้ดู แล้วอธิบายพฤติกรรมที่ออกแบบไว้ ไม่เคยลอง
รู้ข้อจำกัดตัวเอง บอกได้อย่างน้อย 2 ข้อ พร้อมเหตุผลเชิงเทคนิค ตอบว่าใช้งานได้ทุกกรณี
ตรงเวลา จบใน 10 นาที ครบทั้งสี่ช่วง เกินเวลา หรือรีบข้ามช่วงที่ 4

การซ้อมที่ได้ผลที่สุด: ให้เพื่อนอีกทีมฟังก่อนหนึ่งรอบ แล้วให้เขาเล่ากลับมาว่าเข้าใจว่าทีมเราทำอะไร ถ้าเขาเล่าผิด แปลว่าเรายังเล่าไม่ชัด ไม่ใช่เขาฟังไม่เป็น

ตารางนี้อยู่ในบันทึกการเรียนด้วย ติ๊กให้ครบก่อนขึ้นพูดจริง

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ตัวอย่างที่ทำเสร็จ — ส่วนที่หนึ่ง: Sense ที่เชื่อถือได้

ไม่หักค่าตั้งต้น เกณฑ์ 15 ติดเอียง 6 องศาตั้งแต่วันแรก เตือนตลอดไปโดยไม่มีอะไรผิด ไม่มีใครติดตั้งอะไรได้ระนาบ 0 องศาเป๊ะ จำท่าตั้งต้นสองวินาทีแรก base_roll · base_pitch วัดส่วนต่างจากท่าติดตั้ง รวม roll กับ pitch แบบพีทาโกรัส เอียงไปทางไหนก็นับ ไม่รอดสายตา

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 ตัวเดียว นั่งร้านที่เอียงไปข้างหน้าจะรอดสายตาไปเฉย ๆ

ค่าที่วัดเทียบกับตัวเองตอนติดตั้ง มีความหมายกว่าค่าที่วัดเทียบกับแรงโน้มถ่วงเสมอ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ตัวอย่างที่ทำเสร็จ — ส่วนที่สอง: Decide ที่ไม่หลอน

CONFIRM_N = 3 กันการสั่นวูบเดียวตอนมีคนเดินชน 3 x 200 ms = หกในสิบวินาที latched ALERT ค้างอยู่ รับทราบ เหตุการณ์ที่ไม่มีใครเห็น = ไม่เคยเกิด ALERT_GAP_MS กันการเตือนรัวตอนค่าแกว่งรอบเกณฑ์ เตือน 30 ครั้ง/นาที = ถูกปิดเสียง
    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 นาที จะถูกคนหน้างานปิดเสียงในสัปดาห์แรก

ระบบเตือนภัยที่คนเลือกจะไม่ฟัง แย่กว่าไม่มีระบบเตือนภัย เพราะมันสร้างความมั่นใจปลอม ๆ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ตัวอย่างที่ทำเสร็จ — ส่วนที่สาม: กลับมาออนไลน์ และปุ่มทั้งห้าในลูปเดียว

    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 — ฝั่งรับต้องไม่ต้องเดาว่าบอร์ดหายไปไหน

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ทำไมเรียงห้าท่าแบบนี้

เรียงตามความน่าเชื่อถือจากมากไปน้อย 1 · Sense เชื่อค่าได้ก่อน 2 · Decide ตัดสินจบที่เดียว 3 · Show จอไม่พึ่งเน็ต 4 · Send พึ่งสิ่งที่คุมไม่ได้ 5 · กันเน็ตหลุด ทีมส่วนใหญ่ไม่มีเวลา เส้นที่ยังทำงานเมื่อเน็ตหาย เส้นที่พึ่งเน็ต — ล้มแล้วไม่ลากคนอื่นล้ม เขียน Show ก่อน Send เสมอ ไม่งั้นจะเผลอเขียนโค้ดที่จออัปเดตก็ต่อเมื่อส่งสำเร็จ

ท่า 1 Sense มาก่อน เพราะถ้าค่าที่อ่านยังเชื่อไม่ได้ ทุกอย่างที่สร้างทับบนมันคือการตกแต่งความผิดพลาด

ท่า 2 Decide มาที่สอง เพราะสถานะเป็นสิ่งที่ทั้งจอและ broker ใช้ร่วมกัน ตัดสินให้จบที่เดียวก่อน แล้วอีกสองฝั่งค่อยไปหยิบใช้ — ไม่ใช่ต่างคนต่างคิดแล้วได้คำตอบไม่ตรงกัน

ท่า 3 Show มาก่อน Send เพราะจอไม่พึ่งเน็ต ถ้าเรียงกลับกัน ทีมมักเผลอเขียนโค้ดที่จอจะอัปเดตก็ต่อเมื่อส่งสำเร็จ ซึ่งเป็นบั๊กที่หาเจอยากมากตอนสาย WiFi ดี

ท่า 4 Send มาที่สี่ เพราะมันคือส่วนที่พึ่งพาสิ่งที่เราควบคุมไม่ได้มากที่สุด

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

เรียงตามความน่าเชื่อถือจากมากไปน้อย: เซนเซอร์ → ตรรกะ → จอ → เน็ต

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

โครงของงานจบ — สี่ขั้นที่ทุกผลิตภัณฑ์ AIoT มีเหมือนกัน

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 เลย ถ้าแก้ที่เดียวจบ แปลว่าทีมแยกส่วนถูกต้องแล้ว และเปลี่ยนโจทย์ได้โดยไม่ต้องเขียนใหม่ทั้งไฟล์

โครงนี้ใช้กับงานจบของทุกทีมได้ — เปลี่ยนแค่ว่า วัดอะไร · ตัดสินด้วยกฎอะไร · สั่งอะไร

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

เชื่อมจุดให้เห็นภาพ — สิบสองชุดบทเรียนมาจบตรงนี้

บทเรียน 1.1–1.6 เล่นของที่มีอยู่ รู้ว่าปลายทางหน้าตายังไง บทเรียน 2.1–2.9 สั่งฮาร์ดแวร์เอง ไฟ ปุ่ม จอสัมผัส อนาล็อก บทเรียน 3.1–4.9 อ่านเซนเซอร์ วาดแดชบอร์ด ต่อเน็ต ส่งขึ้นแพลตฟอร์ม วันนี้ · บทเรียน 5.1–5.3 ตัดสินใจว่าจะทำอะไร แล้วส่งมอบให้มีคนใช้ สิบเอ็ดชุดบทเรียนสอนวิธีทำ วันนี้สอนวิธีเลือกว่าจะทำอะไร และวิธีบอกคนอื่นว่าทำไม

สิ่งที่ติดตัวไปแม้เปลี่ยนบอร์ดเปลี่ยนภาษา: การเริ่มจากปัญหาไม่ใช่จากเทคโนโลยี · การแยกการตัดสินใจออกจากการแสดงผลและการส่งข้อมูล · การออกแบบพฤติกรรมตอนพังตั้งแต่ต้น ไม่ใช่ตอนเจอปัญหา · การสื่อสารคุณค่าให้คนที่ไม่ได้เขียนโค้ดเข้าใจ

ดูเพิ่ม (25 นาที · ดูเป็นการบ้าน): Build Real-Time IoT Dashboard: Node-RED + InfluxDB + Grafana + MQTT — IoT Frontier — 24:54 — ปลายทางของเส้นทางที่ 4 หน้าตาเป็นอย่างไรเมื่อมีอุปกรณ์หลายตัวและต้องเก็บย้อนหลัง

เครื่องมือจะเปลี่ยนทุกสามปี วิธีคิดสี่ข้อข้างบนอยู่กับเราได้ทั้งอาชีพ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ใช้จริงที่ไหน — สี่มุมที่ของแบบนี้ทำงานอยู่

โรงงาน · เฝ้าความสั่นเครื่องจักร เกณฑ์บนบอร์ด แจ้งเฉพาะตอนผิดปกติ ค่าติดตั้งจริงมาจากการวัดเครื่องนั้น ๆ ก่อนหนึ่งสัปดาห์ โครงเดียวกับ starter เปลี่ยนแค่ read_value() ก่อสร้าง · เตือนการเอียงของโครงสร้าง นั่งร้าน แบบหล่อ ชั้นวางสูง ตู้คอนเทนเนอร์ ต้องหักค่าตั้งต้นตอนติดตั้ง และค้างสถานะรอรับทราบ คือโจทย์ที่เฉลยของชุดบทเรียนนี้ทำจนจบ คลังสินค้า · ติดตามทรัพย์สิน ของถูกยก ถูกเปิด หรือถูกย้ายนอกเวลางาน IMU + เข็มทิศ บอกได้ว่า "ขยับ" แต่ไม่บอกว่าไปไหน ข้อจำกัดนี้ต้องพูดตอนนำเสนอ ไม่ใช่ซ่อน อาคาร · การใช้งานพื้นที่ ห้องประชุมถูกจองแต่ไม่มีคนใช้จริง นับเวลาใช้งานจริงต่อสัปดาห์ ใช้ต่อรองเรื่องพื้นที่ได้ ข้อมูลจริงหนึ่งเดือน ชนะการเดาสิบปี

ทั้งสี่มุมใช้โครงโปรแกรมเดียวกับที่ทีมกำลังจะเขียนวันนี้ ต่างกันที่โจทย์และเกณฑ์เท่านั้น

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ต่อยอด — หลังจบคอร์สนี้เดินต่อทางไหนได้บ้าง

เส้นทาง 1 · ยกระดับความปลอดภัย mqtt 1883 ไปเป็น tesaiot.connect() TLS 8884 อธิบายได้ว่า serverTLS ปกป้องอะไร ไม่ปกป้องอะไร เส้นทาง 2 · ให้ช่างตั้งค่าเองได้หน้างาน wifi.softap() เปิดวงของบอร์ดเอง ไม่ต้องแก้โค้ด ชื่อวงกับรหัสไม่ควรถูกพิมพ์ค้างไว้ในไฟล์ เส้นทาง 3 · ทำให้มันอยู่ได้เป็นเดือน ไฟ · การกู้คืนตัวเองเมื่อค้าง · อัปเดตจากระยะไกล ดูแลอุปกรณ์เป็นร้อยตัวพร้อมกัน เส้นทาง 4 · จากหนึ่งตัวเป็นฝูง topic ที่ query ได้ · dashboard รวม · ใครเงียบเมื่อไร ลองออกแบบโครงสร้าง topic สำหรับ 50 อุปกรณ์

เส้นทาง 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 ถึงสำคัญ

เลือกหนึ่งเส้นทาง เขียนลงบันทึกการเรียน แล้วลงมือสัปดาห์นี้ — ความตั้งใจที่ไม่มีวันเริ่ม จะไม่เคยเริ่ม

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

บันทึกเหตุการณ์ที่ยังอ่านออกตอนถ่ายเอกสารขาวดำ — ui.SpanGroup

หน้าจอของ 07_spangroup_event_log.py: บันทึกเหตุการณ์บนจอ ที่ยังอ่านออกตอนถ่ายเอกสารขาวดำ

ภาพจากตัวจำลอง bento_sim ซึ่งเรนเดอร์ด้วยโค้ด CM55 ชุดเดียวกับที่รันบนบอร์ด ที่ 800x480 เท่าจอของทั้งสองบอร์ด — แสดงหน้าจอที่ 07_spangroup_event_log.py สร้าง เหตุการณ์ในภาพเป็นชุดที่ไฟล์นั้นกำหนดไว้เอง ไม่ใช่เหตุการณ์ที่บอร์ดเจอจริง

สไลด์ "ส่งเหตุการณ์ ไม่ใช่สตรีมดิบ" ของชุดบทเรียนนี้บอกว่าอะไรควรส่งขึ้นไป — และของชุดเดียวกันนั้นควรอยู่บนจอด้วย แต่พอเขียนจริง ทุกทีมทำเหมือนกันหมด คือ Label หลายบรรทัดแล้วเปลี่ยนสีข้อความตามความรุนแรง ซึ่งตกเกณฑ์หน้าจอของหลักสูตร ทันที

วิธีบอกความรุนแรง ผ่านการทดสอบขาวดำไหม
สีข้อความอย่างเดียว ไม่ผ่าน — แปลงเป็นเกรย์สเกลแล้วสามสถานะกลายเป็นเทาเหมือนกันหมด
ขนาดตัวอักษร ผ่าน
เส้นใต้ / เส้นขีดกลาง ผ่าน
สี บวก อย่างใดอย่างหนึ่งข้างบน ผ่าน และคนตาบอดสีอ่านได้ด้วย

ลองเอง 30 วินาที — ถ่ายจอด้วยมือถือแล้วเปิดโหมดขาวดำ ถ้ายังแยกสามระดับออก หน้าจอนั้นผ่าน เกณฑ์หน้าจอของหลักสูตร

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

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 หลายบรรทัดทำไม่ได้

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

บอร์ดไม่รู้ว่าวันนี้วันที่เท่าไร แล้วใครบอกมัน — ui.Calendar

หน้าจอของ 08_calendar_sets_the_clock.py: บอร์ดไม่รู้ว่าวันนี้วันที่เท่าไร แล้วใครบอกมัน

ภาพจากตัวจำลอง bento_sim ซึ่งเรนเดอร์ด้วยโค้ด CM55 ชุดเดียวกับที่รันบนบอร์ด — แสดงหน้าจอที่ 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 ในข้อมูลของทีมไหน คือลายเซ็นของนาฬิกาที่ไม่เคยถูกตั้ง — จำหน้าตาของมันไว้

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ui.Calendar — สองอย่างที่ผิดคาดและต้องรู้

ui.Calendar คือ widget ของงาน "คนบอกบอร์ดผ่านหน้าจอ" — และมันสร้างวันที่ผิดปฏิทินไม่ได้ ต่างจากการวาง Spinbox สามช่องซึ่งยอมให้ป้อน 31 กุมภาพันธ์

  • min max value ที่นี่ไม่ใช่ช่วงค่า อย่าง Slider หรือ Bar แต่คือ ปี · เดือน · วัน (ui_widget_mgr.c บรรทัด 2117 ใน BENTO-TESAIoT-libraries/claw) ใส่ค่านอกพิสัยแล้วมันเงียบ ๆ ถอยไปใช้ 2026-01-01 ไม่มี error
  • ลูกศรเปลี่ยนเดือนที่หัวปฏิทินไม่ส่ง event เฟิร์มแวร์กรองทิ้งด้วย lv_calendar_get_pressed_date() ที่ตอบไม่ผ่านเมื่อไม่ได้แตะวัน — รู้ได้เฉพาะตอนคนแตะวัน ไม่ใช่ตอนคนพลิกดูเดือน · ค่าที่ส่งมาคือเลขแปดหลัก YYYYMMDD ก้อนเดียว ต้องแกะเอง

machine.RTC มีอยู่จริงทั้ง Eva Kit และ Dev Kit (ตาราง modmachine.c ของพอร์ตร่วมใส่ไว้โดยไม่มีเงื่อนไข) ต่างจาก machine.PWM และ machine.ADC ที่ไม่มี — เรียกได้เลยโดยไม่ต้อง try/except

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

หน้าจอของทุกไฟล์ในชุดบทเรียนนี้ (1/2)

หน้าจอของ 01_state_machine.py: สามสถานะ และเส้นแบ่งที่ต้องตัดสินใจไว้ล่วงหน้า หน้าจอของ 02_confirm_n.py: ต้องเห็นติดกันกี่รอบถึงจะเชื่อ หน้าจอของ 03_reconnect_backoff.py: ต่อใหม่แบบถอยห่างขึ้นเรื่อย ๆ ไม่ใช่รัวติดกัน

01 สามสถานะ และเส้นแบ่งที่ต้องตัดสินใจไว้ล่วงหน้า · 02 ต้องเห็นติดกันกี่รอบถึงจะเชื่อ · 03 ต่อใหม่แบบถอยห่างขึ้นเรื่อย ๆ ไม่ใช่รัวติดกัน
ภาพจากตัวจำลอง bento_sim ซึ่งเรนเดอร์ด้วยโค้ด CM55 ชุดเดียวกับที่รันบนบอร์ด ที่ 800x480 เท่าจอของทั้งสองบอร์ด — แสดงหน้าจอที่ตัวอย่างสร้าง ค่าจากเซนเซอร์ WiFi และไมค์เป็นค่าแทนบนเครื่องโฮสต์ ไม่ใช่ผลการวัดของบอร์ด
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

หน้าจอของทุกไฟล์ในชุดบทเรียนนี้ (2/2)

หน้าจอของ 04_heartbeat_and_alert.py: ข้อความสองชนิด สองจังหวะ คนละหน้าที่ หน้าจอของ 05_hmi_survives_offline.py: เน็ตหลุดแล้วจอต้องยังทำงาน หน้าจอของ 06_sense_decide_act_report.py: วงจรเต็มสี่ขั้นในไฟล์เดียว

04 ข้อความสองชนิด สองจังหวะ คนละหน้าที่ · 05 เน็ตหลุดแล้วจอต้องยังทำงาน · 06 วงจรเต็มสี่ขั้นในไฟล์เดียว
ภาพจากตัวจำลอง bento_sim ซึ่งเรนเดอร์ด้วยโค้ด CM55 ชุดเดียวกับที่รันบนบอร์ด ที่ 800x480 เท่าจอของทั้งสองบอร์ด — แสดงหน้าจอที่ตัวอย่างสร้าง ค่าจากเซนเซอร์ WiFi และไมค์เป็นค่าแทนบนเครื่องโฮสต์ ไม่ใช่ผลการวัดของบอร์ด
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

คลังตัวอย่างประยุกต์ — เอาไปต่อยอดเองได้ (1/2)

หน้าจอของ 01_andon_severity_lamp.py: เสาไฟสถานะแบบโรงงาน (andon light) หน้าจอของ 02_heartbeat_liveness.py: ไฟหัวใจเต้นบอกว่าลูปยังไม่ตาย หน้าจอของ 05_short_long_press.py: ปุ่มเดียว สองความหมาย

01 เสาไฟสถานะแบบโรงงาน (andon light) · 02 ไฟหัวใจเต้น บอกว่าลูปยังไม่ตาย · 05 ปุ่มเดียว สองความหมาย
ไฟล์อยู่ที่ examples/usecase/ — ไม่ได้อยู่ในบทเรียนไหนโดยเฉพาะ หยิบไปใช้กับงานจบได้เลย
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

คลังตัวอย่างประยุกต์ — เอาไปต่อยอดเองได้ (2/2)

หน้าจอของ 07_hold_to_confirm.py: กดค้างเพื่อยืนยันคำสั่งที่ย้อนกลับไม่ได้ หน้าจอของ 14_hard_iron_calibration.py: การคาลิเบรตเข็มทิศเป็นสิ่งที่วัดได้

07 กดค้างเพื่อยืนยันคำสั่งที่ย้อนกลับไม่ได้ · 14 การคาลิเบรตเข็มทิศ เป็นสิ่งที่วัดได้
ไฟล์อยู่ที่ examples/usecase/ — ไม่ได้อยู่ในบทเรียนไหนโดยเฉพาะ หยิบไปใช้กับงานจบได้เลย
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

อ้างอิงและเครดิต

สถาปัตยกรรมและโพรโทคอล

บอร์ดและซอฟต์แวร์

  • KIT_PSE84_EVAL PSOC™ Edge E84 Evaluation Kit guide, Infineon 002-39007 Rev.*B
  • TESAIoT Dev Kit — SoM ของ KIT_PSE84_AI บนฐาน QWA309 (TESAIoT_KIT_PSE84_AI-Micropython-BentoClaw, bsps/TARGET_KIT_PSE84_AI/bsp_features.mk: เพิ่ม DPS368 · SHT40 · เรดาร์) — ภาพบอร์ดจริงยังไม่ได้ถ่าย
  • BMI270 datasheet — Bosch Sensortec — https://www.bosch-sensortec.com/products/motion-sensors/imus/bmi270/
  • MicroPython documentation — https://docs.micropython.org/

ภาพประกอบ

  • IoT-Enabled Smart City Framework: National Institute of Standards and Technology / Wikimedia Commons — สาธารณสมบัติ
  • สัญญาณความสั่นลูกปืนปกติเทียบกับลูกปืนเสีย: Kolok P. et al., Sensors 25(21):6610 (2025) — CC BY 4.0 — https://pmc.ncbi.nlm.nih.gov/articles/PMC12609400/
  • Envelope spectrum ของลูกปืน: Mika D., Józwik J., Ruggiero A., Sensors 25(23):7371 (2025) — CC BY 4.0 — https://pmc.ncbi.nlm.nih.gov/articles/PMC12694681/
  • สถาปัตยกรรม MQTT pub/sub: Chine3me / Wikimedia Commons — CC0 1.0
  • Edge computing: Psenda38 / Wikimedia Commons — CC0 1.0
  • ไดอะแกรมอื่นทุกภาพในเด็คนี้วาดขึ้นใหม่สำหรับหลักสูตรนี้
  • ภาพผังสถาปัตยกรรม IIoT ใช้สัญญาอนุญาต CC BY-SA 4.0 ของเจ้าของภาพ (ดูเครดิตบนสไลด์และใน credits.yaml) ภาพนี้ใช้ทั้งภาพโดยไม่ดัดแปลง เนื้อหาสไลด์ชุดนี้จึงยังเผยแพร่ภายใต้ CC BY 4.0

วิดีโอที่ตรวจแล้วว่าเปิดได้

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

อ้างอิง (ต่อ) — ข้อเท็จจริงของเฟิร์มแวร์ในเด็คนี้ ตรวจจากซอร์สโดยตรง

  • ไมโครโฟนใช้จาก Python ได้: โมดูล 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 ไม่ใช่ของห้อง
  • Eva Kit ไม่มีเซนเซอร์อุณหภูมิห้องหรือความชื้น · Dev Kit มี 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)
  • ห้าไฟล์ของบทเรียน 4.1–5.3 ที่ส่งอุณหภูมิ (s09/09 s10/08 s11/07 s12/06 s12/09) อ่านผ่าน read_temp(): sensors.sht40 เมื่อบอร์ดมี ไม่งั้นลูกบิดแทน (0–100 % = 15–45 °C) และบอกบน console — ยังไม่ได้รันบนบอร์ดจริงทั้งสองทาง (ยังรอรอบทดสอบ)

ทุกข้อจำกัดข้างบนมี path และเลขบรรทัดกำกับ ไม่ได้มาจากเอกสารฉบับใด ถ้าเจอที่ไม่ตรงกับบอร์ด แจ้ง erratum ได้เลย

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

Tileview — นิ้วสั่งได้ โปรแกรมสั่งไม่ได้

หน้าจอของ 09_tileview_swipe_only.py: จอที่นิ้วพาไปได้ แต่โปรแกรมพาไปไม่ได้

ภาพจากตัวจำลอง Eva Kit (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 หนึ่ง บวกช่องละหนึ่ง

ข้อจำกัดที่ต้องออกแบบเผื่อ

  • โปรแกรมสั่งเลื่อนไปช่องที่ต้องการเองไม่ได้ — ไม่มี property สำหรับเลือกช่อง
  • ถามว่า "ตอนนี้อยู่ช่องไหน" ตรง ๆ ไม่ได้ ต้องอนุมานจาก scroll_end

ของที่ต้องเห็นตลอดเวลา ห้ามอยู่ในช่องใดช่องหนึ่ง — สัญญาณเตือนที่อยู่ในช่องที่ผู้ใช้ไม่ได้เปิดอยู่ คือสัญญาณเตือนที่ไม่มีใครเห็น วางไว้นอก Tileview เสมอ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0