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

บทเรียน 4.3 — ลงมือทำ: หน้าสถานะเครือข่ายของทีม

WiFi และเครือข่ายพื้นฐาน · บอร์ดของเราออกจากโต๊ะทำงาน แล้วไปมีที่อยู่ในเครือข่าย

โมดูล 4 — เชื่อมต่อแพลตฟอร์ม IoT

ต่อจากบทเรียน 4.2 — จอสถานะเครือข่าย: แกะโค้ดโมดูล wifi

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

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

หน้าจอ network status ของทีม (SSID, IP, ping ms) อัปเดตสดบน HMI ที่ต่อยอดจากบทเรียน 3.7–3.9

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

  • [ ] ตารางซ้าย ขึ้นชื่อเครือข่ายอย่างน้อย 3 วง เรียงจากแรงไปอ่อน ครบสี่คอลัมน์ และ ไม่มีช่องไหนตัดบรรทัด (ช่องที่ตัดบรรทัดทำให้แถวสูงสองเท่า แถวสุดท้ายจะตกขอบจอ)
  • [ ] แผงขวาแสดง SSID ที่โปรแกรมส่งเข้า connect() เอง และ เลข IP ที่ได้จาก DHCP — เลข IP ต้องไม่ใช่ค่าที่พิมพ์ไว้ในโค้ด ส่วน SSID บอร์ดตอบเองไม่ได้ (wifi.status()["ssid"] เป็นสตริงว่างเสมอ) ทีมจึงต้องจำสตริงที่ตัวเองส่งไป
  • [ ] ไฟสถานะลิงก์ ติดถูกดวง และมาตรวัด dBm ขยับตามวงของทีมจริง พร้อมพิสัย -90 ถึง -40 กำกับ
  • [ ] กด สแกนใหม่ แล้วตารางถูกล้างและเทใหม่ ไม่ใช่เขียนทับซ้อนของเดิม
  • [ ] บรรทัดเกตเวย์และอินเทอร์เน็ตแสดงเวลาเป็น ms และ อัปเดตซ้ำทุก 3 วินาที ต่อเนื่องอย่างน้อย 2 นาที
  • [ ] ทีมอธิบายได้ว่าถ้า gateway ผ่านแต่ internet timeout แปลว่าปัญหาอยู่ที่ไหน
  • [ ] ทีมตอบได้ว่าทำไม wifi.scan() ต้องอ่านด้วย net[1] ไม่ใช่ net['rssi']
  • [ ] ถ่ายรูปหน้าจอตอนทำงานแนบในบันทึกการเรียน

เกณฑ์ข้อที่หกกับเจ็ดตอบด้วยปากเปล่าได้ — ชุดบทเรียนนี้วัดการวินิจฉัย ไม่ได้วัดความยาวโค้ด

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

ลงมือทำ — เติมช่องว่างในไฟล์ฝึก

s09_network_status.py pass ท่า 2 — nets = wifi.scan() (ใน rescan) pass ท่า 2 — nets.sort(key=..., reverse=True) pass ท่า 3 — แกะ tuple สี่ช่อง (ในลูปของ rescan) pass ท่า 4 — ok = wifi.connect(...) (นอกสุด) pass ท่า 5 — events = ui.poll() (ต้นลูป) pass ท่า 5 — ms_gw = wifi.ping(...) (ใน try) รวม 6 จุด สังเกตระดับ การเยื้อง

เปิด s09_network_status.py มีช่องว่างให้เติม 6 จุด — จุดเดียวที่อยู่ระดับนอกสุดคือ wifi.connect() ที่เหลือเยื้องเข้าไปอยู่ในฟังก์ชัน rescan() ในลูป หรือใน try ให้ดูตำแหน่งซ้าย-ขวาของกล่องเป็นตัวช่วยจำระดับ · หน้าจอถูกวางไว้ให้ครบแล้ว ไม่ต้องแก้ งานของเราคือทำให้ข้อมูลจริงไหลเข้าไปในนั้น

def rescan():
    nets = []
    # เติม: nets = wifi.scan()
    pass
    ...
    for i in range(min(TOP_N, len(nets))):
        # เติม: ssid, rssi, security, channel = nets[i]
        pass

ลำดับที่แนะนำ: เติมท่า 2 กับท่า 3 ก่อนรันครั้งแรก (เติมแค่ท่า 2 แล้วรัน ลูปแถวจะพังที่ ssid ซึ่งท่า 3 เป็นคนกำหนด) แล้วดูว่า print("found", len(nets), "networks") ขึ้นกี่วงและแถวขึ้นจอครบไหม จากนั้นจึงไปต่อท่า 4 และ 5

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

ตัวอย่างของบทเรียน 4.1–4.3 — สามไฟล์แรกคือชุดที่พาหน้าจอสถานะเครือข่ายขึ้นจนครบ

ต้องทำในบทเรียน · เปิดตามลำดับนี้ ทั้งชุดราว 29 นาที

ลำดับ · เรื่อง · เวลา ไฟล์ ลงมือทำอะไร แล้วจะเข้าใจอะไร
1 · scan() คืน tuple ไม่ใช่ dict · 6 นาที 01_scan_tuples.py แกะสี่ช่องของแต่ละวงได้ถูกตั้งแต่บรรทัดแรก แทนที่จะเสียครึ่งบทเรียนกับ TypeError · จะเข้าใจว่าผลของ scan() เป็น tuple ไม่ใช่ dict จึงต้องอ่านด้วยลำดับช่อง ไม่ใช่ด้วยชื่อคีย์
2 · ป้ายสถานะขึ้นก่อนบรรทัดที่บล็อก · 8 นาที 03_connect_says_first.py เขียนลำดับที่ทำให้จอไม่ดูเหมือนเครื่องค้าง ตอน connect() กินเวลาเป็นนาที · จะเข้าใจว่าป้ายสถานะต้องขึ้นก่อนบรรทัดที่บล็อกเสมอ ไม่ใช่หลังจากมันคืนค่า
3 · หน้าจอสถานะลิงก์ที่วัดมาจริง · 15 นาที 06_link_panel_hmi.py ประกอบ MVP ของชุดบทเรียนนี้ได้ครบ — SSID · IP · ping ms พร้อมสามสถานะที่แยกออกจากกัน · จะเห็นว่าหน้าจอสถานะลิงก์ที่เชื่อได้ ทุกตัวเลขบนนั้นต้องวัดมาจริง

ไฟล์ที่ 3 ไม่มีแท่งความแรงสัญญาณ และนั่นตั้งใจ แท่งบนจอนั้นคือเวลา ping — ยาว = ช้า ตรงข้ามกับมาตรวัดความแรงในเฉลยที่มาจาก scan() ซึ่งเป็นคนละคำถามกัน เหตุผลอยู่ในหัวไฟล์ พร้อม path ของซอร์สที่ hardcode ค่าไว้

ติดตรงไหน เปิดอันนี้

อาการที่เจอ ไฟล์ที่ตอบอาการนั้น
ต่อติดแล้ว ได้ IP แล้ว แต่เปิดอะไรไม่ได้เลย 04_ping_two_targets.py — วัดเกตเวย์กับ 8.8.8.8 พร้อมกัน แล้วบอกได้ว่าขาดตรงไหน
เรียก status() หลายครั้งในรอบเดียว แล้วได้ภาพที่ไม่เคยเกิดจริง 05_status_dict.py — อ่านครั้งเดียวต่อรอบ แล้วใช้ค่าชุดนั้นทั้งรอบ
ไม่รู้ว่าวงไหนอยู่ใกล้ที่สุด 02_rank_by_rssi.py — เรียงจากแรงไปอ่อนด้วย dBm จริงจาก scan()

อ่านเสริมนอกเวลา — เรื่องนี้อยู่นอกเกณฑ์ผ่านของบทเรียน 4.1–4.3 เพราะเช็กพอยต์ของชุดบทเรียนนี้วัดแค่ว่าหน้าจอสถานะอัปเดตสดได้ ยังไม่ได้วัดการฟื้นตัว: 03_reconnect_backoff.py ตอบคำถามถัดไปว่าลิงก์หลุดแล้วจะต่อใหม่อย่างไร ไม่ให้สามสิบบอร์ดถล่มเราเตอร์พร้อมกัน

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

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

อาการ สาเหตุที่แท้จริง วิธีแก้
TypeError: tuple indices เขียน net['ssid'] หรือ net['rssi'] ตามตัวอย่างที่มากับเครื่อง ใช้ net[0] และ net[1] — scan() คืน tuple
ต่อไม่ติดทั้งที่ SSID ถูกและไม่มีรหัสผ่าน connect() ล็อกโหมดเป็น WPA3/WPA2 ตายตัว ต่อวงแบบเปิดไม่ได้ ตั้งรหัสผ่านให้ Hotspot หรือเราเตอร์ (อย่างน้อย 8 ตัว)
จอนิ่งไปเป็นนาทีหลังกด Program connect() บล็อกได้ถึง ~85 วินาทีเมื่อรหัสผิด รอจนขึ้นผล แล้วตรวจรหัสทีละตัวอักษร ไม่ต้องกดซ้ำ
ValueError ตอนเรียก ping ใส่ชื่อโฮสต์ เช่น "google.com" wifi.ping() รับเฉพาะเลข IP
แถวเครือข่ายขึ้นแล้วหายไปใน 2 วิ ลืม ui.poll() ในลูป หรือหน่วงยาวเป็นก้อนเดียว เรียก ui.poll() ทุกรอบ และให้ลูปเดินรอบละ 200 ms
ตารางสูงเกินจอ แถวสุดท้ายหาย ข้อความในช่องยาวเกิน col_width แล้วถูกตัดบรรทัด แถวนั้นสูงเป็นสองเท่า ขยาย col_width ของคอลัมน์นั้น หรือย่อข้อความก่อนใส่ (เฉลยตัด SSID ที่ 12 ตัวอักษร)
กดสแกนใหม่แล้วแถวเก่ายังค้าง ลืม tbl.clear_items() ก่อนเทแถวชุดใหม่ ล้างก่อนเสมอ แล้วใส่หัวตารางใหม่ทุกครั้ง เพราะการล้างลบหัวตารางไปด้วย
เปลี่ยนสีแท่งแล้วแท่งดูเต็มทั้งราง .color() ของ ui.Bar ไปลงที่ราง ไม่ใช่แถบที่เต็ม อย่าเปลี่ยนสีแท่งตอนรัน ให้เปลี่ยนสีตัวเลขหรือใช้ ui.Led บอกสถานะแทน
wifi.status() คืน ssid ว่าง rssi = 0 ทั้งสองช่องเป็นค่าตายตัวในเฟิร์มแวร์ อ่านความแรงจาก wifi.scan() และเก็บ SSID ที่เราสั่งต่อไว้เอง
gateway timeout ทั้งที่ IP ขึ้นปกติ เดาเลขเกตเวย์เป็น .1 แต่วงนี้ไม่ได้ใช้ .1 ดูเลข gateway จริงจากโน้ตบุ๊กที่ต่อวงเดียวกัน (ipconfig / ip route) แล้วแก้ค่าในโค้ด
IndexError ตอนวาดแถวท้าย ๆ สแกนเจอน้อยกว่า TOP_N ใช้ min(TOP_N, len(nets)) เป็นขอบเขตลูป
ping เน็ต timeout ทุกครั้ง แต่ gateway ปกติ ทางออกอินเทอร์เน็ตของวงมีปัญหา หรือถูกกรองไว้ ไม่ใช่บั๊กของเรา — จดผลลงบันทึกการเรียนแล้วทำงานต่อได้

เจ็ดในสิบสองข้อนี้คือ ข้อจำกัดที่รู้ได้ล่วงหน้า ไม่ใช่ความผิดพลาดของโค้ด — สามข้อกลางตารางเป็นเรื่องของ ui.Table โดยเฉพาะ และทั้งสามข้อ ไม่มี error ให้จับ มีแต่จอที่ดูผิด

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

เฉลย s09_network_status.py — ส่วนที่หนึ่ง

อ่านให้เข้าใจ แล้วพิมพ์เอง อย่าคัดลอกวาง

WIFI_SSID = "my-hotspot"     # ชื่อ WiFi บ้านหรือ Hotspot มือถือของคุณ
WIFI_PASS = "changeme"
NET_TEST_IP = "8.8.8.8"      # ปลายทางฝั่งอินเทอร์เน็ต
PING_TIMEOUT_MS = 1500
TOP_N = 3                    # ตารางสูง 288 = หัวตารางบวกสามแถว แถวละ 72 พิกเซล
PING_EVERY_MS = 3000
RSSI_FLOOR, RSSI_CEIL = -90, -40   # พิสัยของมาตรวัด

def signal_of(rssi):
    pct = (rssi - RSSI_FLOOR) * 100 // (RSSI_CEIL - RSSI_FLOOR)
    pct = 0 if pct < 0 else (100 if pct > 100 else pct)
    col = COL_RUN if rssi >= -60 else (COL_WARN if rssi >= -75 else COL_BAD)
    return pct, col
สามชั้นนี้เรียงจากบนลงล่างในไฟล์เสมอ — ค่าที่แก้บ่อยอยู่บนสุด ค่าที่ทีมต้องแก้ SSID · รหัสผ่าน อยู่บนสุด หาเจอทันที ค่าที่ปรับได้ทีหลัง timeout · จำนวนแถว ไม่ต้องไล่หาในลูป ตรรกะที่ใช้ซ้ำ signal_of() · ms_text() แก้ที่เดียว เปลี่ยนทั้งจอ

เกณฑ์สี −60 / −75 หลวมกว่าเกณฑ์ห้าขีดของหน้าจอบอร์ด เพราะการ์ดของเรามีสามระดับ — เลือกเกณฑ์ให้เข้ากับสิ่งที่จะแสดง ไม่ใช่ลอกมาทั้งชุด · ระดับ "ปกติ" คืนสีฟ้า COL_RUN ไม่ใช่เขียว สถานะปกติต้องเงียบ สีจัดสงวนไว้ให้เรื่องผิดปกติ — จอที่ระบายเขียวทั้งจอฝึกให้ตามองข้ามสี แล้ววันที่มีเรื่องจริง สีแดงก็กลืนไปกับพื้นหลัง

ค่าที่ต้องแก้บ่อยอยู่บนสุด ตรรกะอยู่กลาง หน้าจออยู่ล่าง — โครงนี้ใช้ได้กับทุกสคริปต์ตั้งแต่บทเรียน 1.1–1.3

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

เฉลย — หน้าจอ: ตาราง ไฟสถานะ และมาตรวัด dBm

tbl = ui.Table(x=24, y=104, w=480, h=288, cols=4)   # หัวตาราง + 3 แถว แถวละ 72 px
tbl.col_width(0, 144)        # ช่องแคบกว่าข้อความ = ตัดบรรทัด = แถวสูงสองเท่า = แถวท้ายตกจอ
tbl.col_width(1, 96)
tbl.col_width(2, 80)
tbl.col_width(3, 128)
led_up = ui.Led(x=536, y=112, w=48, h=48, color=COL_OK, value=0)     # ต่ออยู่
led_down = ui.Led(x=536, y=168, w=48, h=48, color=COL_BAD, value=1)  # ยังไม่ต่อ
bar_rssi = ui.Bar(x=536, y=320, w=152, h=12, color=COL_RUN,
                  min=RSSI_FLOOR, max=RSSI_CEIL, value=RSSI_FLOOR)
sc_rssi = ui.Scale(x=536, y=340, w=152, h=44, color=COL_TEXT,
                   min=RSSI_FLOOR, max=RSSI_CEIL)
sc_rssi.ticks(11, 5)      # 11 ขีด ป้ายเลขทุกขีดที่ 5 = เหลือสามป้าย ไม่ทับกัน

สามอย่างนี้แทนของเดิมทีละอย่าง และแต่ละอย่างมีเหตุผลที่วัดได้

ของเดิม ของใหม่ เพราะ
ui.Label เรียงกันห้าบรรทัด ui.Table สี่คอลัมน์ ตารางจัดคอลัมน์ให้เอง เราไม่ต้องนับพิกเซลทุกครั้งที่ข้อความเปลี่ยนความยาว
ตัวอักษรสีเขียว/แดงบอกว่าต่ออยู่ไหม ui.Led สองดวง แปลงภาพหน้าจอเป็นขาวดำแล้วสีตัวอักษรหายหมด ส่วนไฟติดกับไฟหรี่ยังแยกออก
ตัวเลข dBm ลอย ๆ ui.Bar ทับ ui.Scale ค่ากับพิสัยอยู่ด้วยกัน คนอ่านตอบได้ทันทีว่า −67 นี่ดีหรือแย่

ui.Scale ไม่รับ .value() มันคือไม้บรรทัด ตัวที่ขยับคือ ui.Bar ที่วางทับ (แบบวงกลมมีเข็มจริง — บทเรียน 3.1–3.3) · ui.Led สั่ง .value(0) แล้ว หรี่ ไม่ใช่หาย — ไฟที่หายไปตอนดับ ทำให้คนดูแยกไม่ออกว่าดับหรือจอเสีย

ทั้งสามตัวเป็นของใหม่ที่เพิ่มเข้าไลบรารีเมื่อ 15 ส.ค. 2026 — 09_scale_led_spinbox.py สอนทั้งสามตัวแยกกันทีละตัว

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

เฉลย — ส่วนที่สอง: สแกน เรียง แล้วเทลงตาราง

def rescan():                                 # ปุ่ม "สแกนใหม่" เรียกซ้ำได้ทั้งชุด
    nets = wifi.scan()
    nets.sort(key=lambda net: net[1], reverse=True)
    print("found", len(nets), "networks")
    tbl.clear_items()                         # ล้างก่อน ไม่งั้นแถวเก่าค้างใต้แถวใหม่
    tbl.add_row("SSID", "dBm", "ช่อง", "รหัส")
    for i in range(min(TOP_N, len(nets))):
        ssid, rssi, security, channel = nets[i]
        tbl.add_row(ssid[:12], str(rssi), str(channel),
                    "เปิด" if security == 0 else "มีรหัส")
        ui.poll()
ทั้งสองกล่องต่างกันแค่คำเดียว แต่ผลบนจอกลับด้านกันทั้งแผง ui.poll() อยู่ในลูป (ถูก) for i in range(...): tbl.add_row(...) ui.poll() เรียงกลับด้าน (ผิดที่พบบ่อย) nets.sort(key=lambda n: n[1]) ไม่ใส่ reverse=True → -86 มาก่อน -48 สามแถวบนจอกลายเป็นวงที่อ่อนที่สุด

security == 0 คือเครือข่ายแบบเปิด ค่าอื่นคือมีการเข้ารหัส — เขียนแค่ เปิด กับ มีรหัส เพราะโมดูลยังไม่มีค่าคงที่ให้เทียบ WPA2/WPA3 และเขียนเป็น คำ ไม่ใช่ระบายสี ภาพขาวดำก็ยังอ่านออก · ssid[:12] ตัดชื่อที่ยาวเกินคอลัมน์โดยตั้งใจ ราคาคือสองวงที่ขึ้นต้นเหมือนกันจะดูเหมือนกัน — ชื่อเต็มยังอยู่ที่ Console ทุกการตัดข้อมูลบนจอต้องรู้ตัวว่าตัดอะไร และต้องมีที่ให้ดูของเต็ม

reverse=True คือหนึ่งคำที่เปลี่ยนความหมายของทั้งหน้าจอ — ตรวจผลด้วยตาทุกครั้งหลังเรียงข้อมูล

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

เฉลย — ส่วนที่สาม และทำไมต้องเรียงห้าท่าแบบนี้

nets = rescan()                       # ท่า 2 + 3 จบในบรรทัดเดียว และเรียกซ้ำได้
...
ok = wifi.connect(WIFI_SSID, WIFI_PASS)
...
ip = wifi.ip()
gw = gateway_of(ip)                   # เดาว่าเกตเวย์เป็น .1 ของวงเดียวกัน
led_up.value(1)
led_down.value(0)
...
while True:
    now = time.ticks_ms()
    events = ui.poll()                # ท่า 5 — รับนิ้วทุก 200 ms
    ...
    if wifi.is_connected():
        ...
                ms_gw = wifi.ping(gw, PING_TIMEOUT_MS)      # ยิงทุก 3 วิ ตามนาฬิกาของมันเอง
                ms_net = wifi.ping(NET_TEST_IP, PING_TIMEOUT_MS)
ถ้าท่าที่ 3 พัง เรารู้แน่ว่าไม่เกี่ยวกับเครือข่าย เพราะท่า 2 ผ่านไปแล้ว 1 · จอใช้ได้ 2 · วิทยุใช้ได้ 3 · แปลข้อมูลถูก 4 · มีที่อยู่แล้ว 5 · คุยกับคนอื่นได้ แต่ละท่าพิสูจน์ท่าก่อนหน้า — พังตรงไหนรู้ทันที

ท่า 2 สแกน มาก่อนต่อ เพราะสแกนไม่ต้องใช้รหัสผ่าน — เจอวงแปลว่าวิทยุทำงาน · ท่า 3 แปลข้อมูล มาก่อนต่อเน็ต เพราะบั๊ก tuple/dict โผล่ตรงนี้ · ท่า 2 กับ 3 ถูกมัดเป็นฟังก์ชัน rescan() ตัวเดียว เพราะปุ่มบนจอต้องเรียกทั้งชุดซ้ำได้ — นั่นคือความต่างระหว่าง "สคริปต์ที่รันครั้งเดียว" กับ "หน้าจอที่คนใช้งานได้" · gateway_of() เดาเลขเกตเวย์เหมือนหน้าจอ C แต่เราไม่หยุดแค่เดา — ยิง ping เพื่อพิสูจน์

ไล่จากสิ่งที่พึ่งพาคนอื่นน้อยที่สุดไปหามากที่สุด — เป็นลำดับการดีบักมาตรฐานของงานฝังตัว

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

เชื่อมโยงรากฐาน · สรุปบทเรียน · ชุดบทเรียนถัดไป

ฝั่งสมองกลฝังตัว คลื่นวิทยุ · ชั้นโพรโทคอล บรรทัดที่บล็อกและวิธีรับมือ ฝั่ง Python tuple unpacking · sort(key=) try/except · lambda ฝั่งออกแบบระบบ วัดสองจุดเพื่อระบุตำแหน่งปัญหา แยก "เดา" ออกจาก "พิสูจน์แล้ว" บทเรียน 1.1–1.6 เล่นของจริง บทเรียน 2.1–3.9 สั่งฮาร์ดแวร์ + จอ บทเรียน 4.1–4.3 ออกสู่เครือข่าย บทเรียน 4.4–5.3 ขึ้นแพลตฟอร์ม วันนี้เราอยู่ตรงนี้ — บอร์ดมีที่อยู่แล้ว แต่ยังไม่ได้ส่งอะไรให้ใคร

วันนี้เราได้: อ่าน RSSI เป็น dBm และเทียบกำลังได้ · แกะ tuple จาก wifi.scan() · ต่อเครือข่ายด้วย wifi.connect() โดยรู้ว่ามันบล็อก · วินิจฉัยลิงก์ด้วย ping สองปลายทาง · ประกอบทั้งหมดเป็นการ์ดสถานะบนหน้าจอเดิม

การบ้านของทีม: เลือกทำ 1 ข้อจากสี่ข้อในสไลด์ต่อยอด จดลงบันทึกการเรียน

ชุดบทเรียนถัดไป: บอร์ดจะเริ่ม ส่งข้อมูลออกไปจริง ๆ ด้วย MQTT — เปิดคอมพิวเตอร์อีกเครื่องแล้วเห็นค่าจากบอร์ดวิ่งขึ้นบนหน้าจอนั้น และสั่ง LED กลับมาที่บอร์ดได้

เก็บโค้ดวันนี้ไว้ให้ดี บทเรียน 4.4–4.6 เริ่มจากไฟล์นี้ต่อโดยตรง — เพิ่ม mqtt ทับลงบนลิงก์ที่เราเพิ่งทำให้ทำงาน

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

ใช้จริงที่ไหน — สี่มุมที่งานนี้ไปโผล่

โรงงาน · สำรวจสัญญาณก่อนติดตั้ง ก่อนติดเซนเซอร์ 200 ตัวในโรงงาน ต้องเดินวัด RSSI ทุกจุดก่อน จุดไหนต่ำกว่า −75 ต้องเพิ่ม AP เครื่องมือที่ใช้ = สิ่งที่เราเพิ่งเขียนวันนี้ อาคาร · เฝ้าสุขภาพลิงก์ของอุปกรณ์ อุปกรณ์ในตึกรายงาน RSSI และ ping ของตัวเอง ทีมช่างเห็นตัวไหนกำลังจะหลุด ก่อนที่มันจะหลุด เฝ้าลิงก์ ไม่ใช่เฝ้าแค่ข้อมูล เกษตร · งบสัญญาณกลางแจ้ง โรงเรือนห่างจากบ้าน 300 เมตร ใช้ 2.4 GHz เพราะ 5 GHz ไปไม่ถึง — คำนวณจาก FSPL ได้ก่อนซื้อ เลือกย่านความถี่คือการตัดสินใจทางวิศวกรรม ไอที · แยกปัญหาให้ถูกฝ่าย ping เกตเวย์ผ่าน แต่ออกเน็ตไม่ได้ = ไม่ใช่ปัญหาของอุปกรณ์ ส่งเรื่องให้ผู้ให้บริการ ประหยัดเวลาทั้งทีมได้เป็นวัน

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

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

ดูเพิ่มเติมนอกเวลา — คลิปที่ตรวจแล้วว่าเปิดได้

สองคลิปนี้เป็นการบ้านแบบสมัครใจ เนื้อหาในบทเรียนเข้าใจได้ครบโดยไม่ต้องดู

DHCP DORA Process Explained
Sikandar Shaik CCIEx3 · 6:04 · อังกฤษ
ไล่ DORA ทีละขั้น เห็นภาพว่าเลข IP มาจากไหน
Wi-Fi 4-Way Handshake In Depth
Tall Paul Tech · 6:13 · อังกฤษ
สิ่งที่เกิดขึ้นระหว่างที่บอร์ด "กำลังต่อ" — การแลกกุญแจ

อ่านต่อสำหรับคนอยากรู้ลึก — ทำไมเลข dBm ถึงติดลบ https://dongknows.com/wi-fi-signal-strength-dbm-explained/ · RSSI ต่างจาก dBm อย่างไร https://www.oscium.com/training/resources/understanding-rssi/ · 2.4 กับ 5 GHz ภาษาไทย https://www.asus.com/th/support/faq/1044838/

คลิปทั้งสองไม่อยู่ในเกณฑ์ผ่าน แต่คนที่ดูจะเข้าใจว่าทำไม connect() ถึงใช้เวลาหลายวินาที

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

ต่อยอด — คิดต่อเอง (เลือกทำ 1 ข้อ)

ทั้งสี่ข้อใช้โค้ดวันนี้เป็นฐาน เพิ่มไม่เกินสิบบรรทัดต่อข้อ 1 · แผนที่สัญญาณ เดินวัด 5 จุดในห้อง แล้วเทียบกับ FSPL 2 · คุณภาพลิงก์ ping 100 ครั้ง หา min/avg/max + %หาย 3 · ล่าเกตเวย์ตัวจริง พิสูจน์ว่า .1 ถูกไหม แล้วเสนอวิธีเลิกเดา 4 · เฝ้าลิงก์ 10 นาที นับครั้งที่ timeout แล้วสรุปว่าเสถียรไหม

ข้อ 1 · แผนที่สัญญาณในบ้านหรือที่ทำงาน เดินถือบอร์ดไปห้าจุดที่ห่างจากเราเตอร์ต่างกัน จด RSSI ของ SSID เดียวกันพร้อมระยะโดยประมาณ วาดกราฟระยะเทียบ dBm แล้วอธิบายว่าทำไมของจริงแย่กว่าสูตร FSPL

ข้อ 2 · เครื่องวัดคุณภาพลิงก์ ยิง ping ไปที่เกตเวย์ 100 ครั้ง เก็บใน list แล้วหาค่าต่ำสุด เฉลี่ย สูงสุด และเปอร์เซ็นต์ที่ตอบไม่กลับ แสดงสี่ตัวเลขบนจอ · ทำไม "ค่าเฉลี่ยอย่างเดียว" ถึงหลอกเราได้

ข้อ 3 · ล่าเกตเวย์ตัวจริง ทดลอง ping .1, .254, .100 ของวงเดียวกันแล้วสรุปว่าอันไหนตอบ · เขียนสั้น ๆ ว่าถ้าเฟิร์มแวร์เปิด wifi.ifconfig() ให้ (ตอนนี้ยังไม่มี) โค้ดเราจะเปลี่ยนไปอย่างไร

ข้อ 4 · เฝ้าลิงก์สิบนาที ปล่อยโปรแกรมรันสิบนาทีโดยไม่แตะ นับครั้งที่ ping timeout และจดว่า RSSI เปลี่ยนไปกี่ dB แล้วสรุปว่าเครือข่ายนี้เชื่อถือได้แค่ไหน

เขียนคำตอบลงบันทึกการเรียน แล้วเอามาเล่าให้เพื่อนฟังต้นชุดบทเรียนถัดไป

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

ปิดวงจร — ลิงก์มีไว้พาของออกไป ไม่ได้มีไว้ดูเล่น

แปดไฟล์ที่ผ่านมาดูแต่ตัวลิงก์ — สแกน ต่อ หลุด ต่อใหม่ 09_link_gates_a_real_reading.py เอาเซนเซอร์จริงมาต่อท้าย ให้เห็นว่าอะไรคือของที่รอส่ง และอะไรคือคนตัดสินว่าส่งได้หรือยัง

ค่าที่รอส่งคือ อุณหภูมิ อ่านผ่าน read_temp() ในไฟล์ — sensors.snapshot() ไม่มีช่องอุณหภูมิบนบอร์ดไหนเลย ไฟล์จึงถามก่อนว่าบอร์ดมี sensors.sht40 ไหม: บน Dev Kit ได้อุณหภูมิห้องจริง ส่วน บน Eva ไม่มีเซนเซอร์อุณหภูมิ ลูกบิดจึงเล่นบทแทน (0–100 % = 15–45 °C) และ console บอกไว้ตั้งแต่รอบแรกว่าค่ามาจากไหน — บทเรียนเรื่องคิวกับลิงก์เหมือนกันทั้งสองบอร์ด

กติกาข้อเดียวที่ต้องจำ — วัดกับส่งต้องแยกขาดจากกัน

ทำอะไร เมื่อไร เพราะอะไร
วัด ทุกรอบ ไม่ว่าเน็ตจะเป็นยังไง เซนเซอร์ไม่ได้พังตอนเน็ตหลุด
เก็บใส่คิว ทุกครั้งที่วัดได้ ค่าที่วัดได้ตอนออฟไลน์ยังมีค่า
ส่ง เมื่อลิงก์กลับมา และปล่อยทีละค่า ปล่อยรวดเดียวคือการยิงรัวใส่ปลายทาง

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

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

หน้าจอของ 09_link_gates_a_real_reading.py: ค่าจริงรออยู่ ลิงก์เป็นคนบอกว่าไปได้หรือยัง

ภาพจากตัวจำลอง bento_sim — โค้ด CM55 ชุดเดียวกับที่รันบนบอร์ด ที่ 800x480 เท่าจอของทั้งสองบอร์ด · ค่าอุณหภูมิและสถานะลิงก์เป็นค่าแทนบนเครื่องโฮสต์ ไม่ใช่ผลการวัดของบอร์ด

แถบบนคือลิงก์ (ออนไลน์พร้อม IP หรือหลุด) · การ์ดซ้ายคืออุณหภูมิที่วัดได้รอบล่าสุด · การ์ดขวาคือ จำนวนค่าที่ค้างอยู่ในคิว — ตัวเลขที่เดินขึ้นตอนลิงก์หลุด และไหลออกทีละค่าเมื่อลิงก์กลับมา

ลองบนโต๊ะ: ปิด Hotspot หรือ WiFi ที่เราเตอร์สักครู่ แล้วดูเลขคิวเดินขึ้น เสียบกลับแล้วดูมันไหลออก

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

รหัสผ่าน WiFi ไม่ควรอยู่ในโค้ด — ui.Keyboard

หน้าจอของ 10_keyboard_types_the_password.py: รหัสผ่านควรพิมพ์บนจอ ไม่ใช่ฝังในโค้ด

ภาพจากตัวจำลอง bento_sim ซึ่งเรนเดอร์ด้วยโค้ด CM55 ชุดเดียวกับที่รันบนบอร์ด ที่ 800x480 เท่าจอของทั้งสองบอร์ด — แสดงหน้าจอที่ 10_keyboard_types_the_password.py สร้าง ชื่อวงในช่องซ้ายบนเครื่องโฮสต์เป็นค่าแทน บนบอร์ดจริงมันมาจาก wifi.scan()

14 ส.ค. 2026 บอร์ดสแกนเจอ 18 วง และไม่มีวงที่ตัวอย่างใช้อยู่ในนั้นเลย (HARDWARE_CHECKLIST.md ข้อ 6 ของต้นฉบับ) บอร์ดทุกตัวค้างที่ป้าย "กำลังต่อ" พร้อมกัน เพราะ WIFI_SSID กับ WIFI_PASS เขียนตายอยู่ในไฟล์ทุกไฟล์ของบทเรียน 4.1–5.3 — ของจริงทุกตัวที่ต่อ WiFi ได้ ถามรหัสผ่านจากคน ไม่ได้ฝังมาจากโรงงาน

ชิ้นส่วน หน้าที่
ui.Textarea ช่องที่ตัวอักษรไปโผล่
ui.Keyboard แป้นพิมพ์เต็มบนจอ
kb.bind(ta) บอกแป้นพิมพ์ว่าจะพิมพ์ลงช่องไหน — แป้นหนึ่งอันผูกได้ทีละช่องเดียว
.prop(ui.PROP_PASSWORD, 1) โหมดดาว คนที่ยืนข้าง ๆ ไม่ควรอ่านรหัสออก

สร้าง Textarea ก่อน Keyboard เสมอ — .bind() ไปยังแฮนเดิลที่ยังไม่ใช่ Textarea ถูก CM55 ปฏิเสธเงียบ ๆ

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

ui.Keyboard — เงียบจนกว่าจะขอ และอ่านกลับได้แล้ว

kb = ui.Keyboard(x=24, y=200, w=440, h=168, color=COL_TEXT)
kb.bind(ta_pass)
kb.listen("ready", "cancel")
...
        pw = ta_pass.text()
กติกา สิ่งที่เกิดจริง
ไม่ขอ ไม่ส่ง แป้นพิมพ์ไม่ส่ง event เองจนกว่าจะ kb.listen("ready", "cancel") — ปุ่มตกลงกับปุ่มปิดจึงรายงานกลับมา ค่าตั้งต้นคือเงียบ เพราะคิวเหตุการณ์มี 16 ช่อง
ตัวอักษรทีละตัวไม่ส่ง และนั่นถูกแล้ว — สิ่งที่โปรแกรมต้องรู้คือข้อความทั้งช่อง ไม่ใช่ปุ่มทีละใบ
อ่านกลับด้วย ta.text() ไม่ใส่อาร์กิวเมนต์ คืนสตริงที่อยู่ในช่องเดี๋ยวนั้น (GET_TEXT, opcode 0x6B ใน ipc_ui_protocol.h) — ไฟล์นี้จึงต่อด้วยรหัสที่พิมพ์จริง ไม่ใช่ค่าคงที่

จนถึง 15 ส.ค. 2026 ตาราง IPC_CMD_UI_* มี SET_TEXT (0x52) แต่ไม่มีคำสั่งอ่านกลับ หลักสูตรจึงเคยสอนว่า "สิ่งที่พิมพ์ คนอ่านได้ โปรแกรมอ่านไม่ได้" — ตอนนี้ไม่จริงแล้ว · ถ้าต้องประกอบสตริงจากปุ่มทีละใบด้วยเหตุผลอื่น ui.ButtonMatrix ยังทำได้ โดยส่ง value_changed พร้อมลำดับปุ่มกลับมา · อีกทางคือ SoftAP แล้วกรอกจากมือถือ ตาม 08_softap_fallback.py

เลขที่ทำให้ต้องคิดเรื่องนี้ตั้งแต่ออกแบบ — เป้าสัมผัสตามเกณฑ์คือ 88 พิกเซล แป้นพิมพ์มีสี่แถว 4 x 88 = 352 พิกเซล ซึ่งกินพื้นที่วาด 398 ไปเกือบหมด ปุ่มบนแป้นพิมพ์จึงเล็กกว่าเกณฑ์เสมอบนจอ 4.3 นิ้ว นี่เป็นข้อจำกัดของขนาดจอ ไม่ใช่ของ widget

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

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

หน้าจอของ 01_scan_tuples.py: ผลของ wifi.scan() หน้าตาเป็นอย่างไร หน้าจอของ 02_rank_by_rssi.py: เรียงวงจากแรงไปอ่อน แล้วแปลง dBm ให้คนอ่านออก หน้าจอของ 03_connect_says_first.py: บอกก่อนแล้วค่อยรอ เพราะ connect() บล็อก

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

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

หน้าจอของ 04_ping_two_targets.py: เกตเวย์ตอบ แต่อินเทอร์เน็ตไม่ตอบ แปลว่าอะไร หน้าจอของ 05_status_dict.py: อ่านสถานะครั้งเดียว แล้วใช้ค่าชุดนั้นทั้งรอบ หน้าจอของ 06_link_panel_hmi.py: หน้าจอสถานะลิงก์ ที่ทุกตัวเลขบนจอวัดมาจริง

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

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

หน้าจอของ 07_disconnect_rejoin.py: สร้างสถานะ "เน็ตหลุด" ขึ้นมาดูเองตามสั่ง หน้าจอของ 08_softap_fallback.py: ถ้าหาวงที่ตั้งไว้ไม่เจอ บอร์ดปล่อยวงของตัวเองได้

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

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

เอกสารและมาตรฐาน

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

  • Wireless Client Association Process — RUCKUS Education Services · youtube.com/watch?v=WoUKXm9iG7k
  • DHCP DORA Procedure — CMSystemsBe · youtube.com/watch?v=oD5nC2NhHWQ
  • DHCP DORA Process Explained — Sikandar Shaik CCIEx3 · youtube.com/watch?v=ywkJepIQNIU
  • Wi-Fi 4-Way Handshake In Depth — Tall Paul Tech · youtube.com/watch?v=vHIRmG_BzQI

ภาพจาก Wikimedia Commons (ตามลำดับที่ปรากฏ) TaBaZzz · Sss41 · Kirlf (สเปกตรัม 2.4 GHz และ 5 GHz ที่วัดจริง, CC BY-SA 4.0) · Michael Gauthier, Wireless Networking in the Developing World · Superspritz · Gelmo96 · Michel Bakni · Aaron Filbert · Moeenrahi — CC BY-SA 3.0/4.0 ทั้งหมด วางทั้งภาพพร้อมเครดิต ไม่ได้ครอปหรือแก้ไข

ไดอะแกรมที่เหลือวาดขึ้นใหม่สำหรับหลักสูตรนี้ · ข้อเท็จจริงของโมดูล wifi ตรวจจากซอร์ส modwifi.c ใน BENTO-TESAIoT-libraries ซึ่งเป็นโค้ดร่วมของทั้ง Eva Kit และ TESAIoT Dev Kit — แปดชื่อ เพดาน 20 วง และกับดักทุกข้อในตารางจึงเหมือนกันสองบอร์ด · หน้า Wi-Fi Setting บนหน้า Home ก็เป็นหน้าเดียวกันทั้งคู่

ทุกตัวเลขบนสไลด์นี้สืบกลับไปที่เอกสารต้นทางหรือซอร์สโค้ดได้ ถ้าเจอที่ไม่ตรง แจ้ง erratum ได้เลย

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

สองช่อง แป้นพิมพ์เดียว

หน้าจอของ 11_two_fields_one_keyboard.py: ฟอร์มสองช่อง แป้นพิมพ์เดียว และการอ่านค่ากลับ

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

ฟอร์มจริงมีมากกว่าหนึ่งช่อง แต่จอนี้ใส่แป้นพิมพ์ได้ใบเดียว — เหตุการณ์สองชนิดพอสำหรับทั้งฟอร์ม

ชนิด ใช้ทำอะไร
focused รู้ว่าผู้ใช้แตะช่องไหน แล้วผูกแป้นพิมพ์ไปที่ช่องนั้น
ready ผู้ใช้กดปุ่มตกลง — พิมพ์เสร็จแล้ว

ta.text() ที่ไม่ใส่อาร์กิวเมนต์ อ่านสิ่งที่ผู้ใช้พิมพ์กลับมาได้ (GET_TEXT, opcode 0x6B) · ตัวอักษรแต่ละตัวที่แตะ ไม่ ส่งเหตุการณ์มาทีละตัว และนั่นถูกแล้ว — สิ่งที่โปรแกรมต้องรู้คือข้อความทั้งช่อง ไม่ใช่ปุ่มทีละใบ

แป้นพิมพ์ใบเดียวย้ายไปตามช่องที่ถูกแตะ — focused คือเหตุการณ์ที่บอกว่าต้อง bind() ใหม่

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

อย่าเติมครบหกจุดแล้วค่อยรันทีเดียว — การสแกนกับการต่อเน็ตพังคนละแบบ แยกรันแล้วจะรู้ทันทีว่าใครพัง