
05_value_leaves_the_board.py บน BENTO Emulator ที่ 800x480 เท่าจอของทั้งสองบอร์ด ถ่ายจากไฟล์ปัจจุบันโดยตั้ง `TEAM = "nok4821"` — ภาพนี้แสดงหน้าตาจอ ไม่ใช่หลักฐานว่าบอร์ดจริงส่งผ่านเน็ตของห้องได้broker.hivemq.com · publish กำลังส่ง — เลขใบที่ส่งเดินขึ้นพร้อมค่าลูกบิดและค่าเอียง az ที่เพิ่งออกจากบอร์ดไปbento-aiot/<TEAM>/telemetry) และมุมขวาบนคือคำเตือนที่ไฟล์เขียนไว้บนจอตลอดเวลาที่รัน — พอร์ต 1883 ไม่เข้ารหัส ห้ามส่งของลับbroker.hivemq.com — ถ้าถูกกัน ขั้นที่ 2 จะเป็นแดงพร้อมบอกเหตุผล แล้วโปรแกรมจบตรงนั้นอย่างสุภาพ ส่วนฝั่งผู้รับ วันนี้คือหน้าเว็บ my_first_reader.html ไม่ต้องลงโปรแกรมใดในเครื่องยังไม่มีใครรันไฟล์ 05 กับ 06 บนบอร์ดกับ broker.hivemq.com สิ่งที่วัดแล้วคือเวลาไปกลับของ broker จาก Mac บนโต๊ะผู้สอน ไม่ใช่จากเน็ตขององค์กร ถ้าไปไม่ถึง ไฟล์จะขึ้นบนจอตรง ๆ ว่าหยุดที่ขั้นไหนและเพราะอะไร — ผลแบบนั้นก็ยังเป็นผลที่ใช้ได้
สี่โมดูลใหม่วันนี้ทำหน้าที่คนละอย่างกันชัดเจน wifi พาบอร์ดขึ้นวง · mqtt ส่งข้อความออกและรับกลับ · json แปลงข้อมูลของเราให้เป็นข้อความที่ทุกภาษาอ่านได้ · tesaiot เก็บค่าตั้งไว้บนแฟลชของบอร์ดเอง
ที่เหลือเป็นของเดิม sensors อ่านลูกบิด gpio สั่งหลอดไฟ ส่วน lcd ui time คือชุดเดิมจากชุดบทเรียนก่อนหน้าทุกประการ
ไม่ต้องจำรายชื่อนี้ตอนนี้ แต่ละไฟล์จะแนะนำของของมันเองตอนที่ถึงคิว
wifi.connect() แล้วอ่านหมายเลข IP ของบอร์ดตัวเองได้wifi.scan() แล้ววินิจฉัยเองได้ว่าต่อไม่ติดเพราะอะไรปลายทางของวันนี้: จอบอร์ดขึ้นหมายเลข IP ของทีม เลขใบที่ส่งออกไปเดินขึ้น และหลอดไฟบนบอร์ดติดเพราะมีคนพิมพ์คำสั่งมาจากอีกเครื่อง
เปิดบอร์ดรอไว้ได้เลย เล่นเกมเปิดบทเรียนเสร็จ เรารันไฟล์แรกทันที — ชุดบทเรียนนี้ทฤษฎีมาทีหลังของจริงทุกเรื่อง
WiFi ขององค์กรต้อง login ผ่านหน้าเว็บก่อนใช้งาน บอร์ดไม่มีเบราว์เซอร์ให้กดยอมรับ บอร์ดจะได้เลข IP แต่ออกอินเทอร์เน็ตไม่ได้ ไปไม่ถึง broker · วันนี้บอร์ดของทุกทีมต่อ Hotspot จากมือถือของคนในทีมหนึ่งเครื่อง
| ขั้น | iPhone | Android |
|---|---|---|
| 1 · ตั้งชื่อ | ชื่อ Hotspot คือชื่อเครื่อง: การตั้งค่า > ทั่วไป > เกี่ยวกับ > ชื่อ ตั้งเป็น bento-team05 |
การตั้งค่า Hotspot > ชื่อเครือข่าย ตั้งเป็น bento-team05 |
| 2 · ตั้งรหัส | ฮอตสปอตส่วนบุคคล > รหัสผ่าน Wi-Fi (อย่างน้อย 8 ตัว) | รหัสผ่าน อย่างน้อย 8 ตัว ความปลอดภัย WPA2 |
| 3 · ย่านความถี่ | เปิด เพิ่มความเข้ากันได้สูงสุด (Maximize Compatibility) | เลือกย่าน 2.4 GHz ถ้ามีให้เลือก |
| 4 · เปิดค้าง | เปิดหน้าฮอตสปอตค้างไว้ตอนบอร์ดต่อครั้งแรก | เปิด Hotspot ค้างไว้ |
แล้วแก้สองบรรทัดบนหัวทุกไฟล์ที่ต่อเน็ตให้ตรงกับมือถือ:
WIFI_SSID = "bento-team05" # ชื่อ Hotspot ตัวอักษรอังกฤษ ไม่มีช่องว่าง
WIFI_PASS = "<รหัส Hotspot ของทีม>"
สมชาย’s iPhone มีอักขระที่จอวาดไม่ได้ และยาวจนข้อความบนจอถูกตัดยังไม่ได้ทดสอบกับ Hotspot ครบทุกรุ่น ถ้าบอร์ดหา Hotspot ไม่เจอ ให้เช็กข้อ 3 และข้อ 4 ก่อน แล้วลองรันไฟล์
04_scan_the_room.pyดูว่าบอร์ดได้ยินชื่อนั้นไหม
เปิดหน้ารวม https://advance-innovation-centre-aic.github.io/embedded-systems-for-aiot-developer/examples/web/mqtt_dashboard.html บนเครื่องตัวเอง ไม่ต้องติดตั้งอะไร หน้าจะต่อ broker เอง ช่อง ทีม ในหัวข้อ "ส่งคำสั่ง" พิมพ์ team00 แล้วกด บี๊บ หรือพิมพ์ข้อความสั้น ๆ แล้วกด ส่งข้อความ (say) บอร์ดบนโต๊ะผู้สอนรัน 06_command_comes_back.py เป็น team00 อยู่ มันจะร้อง หรือขึ้นข้อความของคุณ (ตัดที่ 24 ตัวอักษร)
ทายก่อน แล้วค่อยดู ผู้สอนนับหนึ่งถึงสาม ทุกคนกดส่งพร้อมกัน ทีมเขียนคำทายลงบันทึกการเรียน ก่อน เขียนแล้วห้ามแก้
| ทีมเราทายว่าตัวเลข "ได้รับแล้ว" บนจอบอร์ดหน้าห้องจะขึ้น | ☐ น้อยกว่า 10 | ☐ 10 ถึง 30 | ☐ ครบทุกคน |
|---|
พิมพ์ภาษาไทยหรืออังกฤษเท่านั้น จอบอร์ดไม่มีตัวอักษรอื่น · ส่งจากหน้าเว็บ · ยังไม่มีใครนับว่าจริง ๆ จะขึ้นกี่ใบ ผลที่เห็นในห้องคือคำตอบ แล้วเราจะกลับมาดูว่าทำไมที่สไลด์ไฟล์ 06
เปิด 01_wifi_first_connect.py แก้สองบรรทัดบนสุดให้ตรงกับ Hotspot มือถือของทีม แล้วส่งขึ้นบอร์ดเลย
# แก้สองบรรทัดนี้ให้ตรงกับ Hotspot มือถือของทีม
WIFI_SSID = "bento-teamXX" # ชื่อ Hotspot มือถือของทีม (WiFi ขององค์กรต้อง login บอร์ดใช้ไม่ได้)
WIFI_PASS = "<รหัส Hotspot ของทีม>" # อย่างน้อย 8 ตัว
สิ่งที่ต้องจดระหว่างรอ: จอค้างนานแค่ไหน (นับในใจก็ได้) แล้วเทียบกับตัวเลข ms ที่บอร์ดรายงานตอนกลับมา ตรงกันไหม
ยังไม่เข้าใจว่าทำไมต้องรอ ไม่เป็นไร สองสไลด์ถัดไปเราจะแกะทีละบรรทัด แต่ต้องเห็นมันเกิดขึ้นกับตาก่อน
ทีมที่ต่อติดตั้งแต่ยี่สิบนาทีแรก จะมีเวลาเหลือไปถึงไฟล์ 05 กับ 06 อย่างสบาย ๆ ซึ่งเป็นสองไฟล์ที่สนุกที่สุดของชุดบทเรียน
wifi มีแปดตัว — ห้าตัวนี้ใช้วันนี้สามตัวบนใช้ตอนต่อ สองตัวล่างใช้ตอนสำรวจและตอนหาสาเหตุว่าทำไมต่อไม่ติด
อีกสามตัวที่เหลือ — wifi.disconnect() ตัดการเชื่อมต่อเอง · wifi.ping(host) วัดว่าปลายทางตอบไหมและช้าแค่ไหน · wifi.softap() เปลี่ยนบอร์ดเป็นตัวปล่อยสัญญาณเองเมื่อไม่มีเราเตอร์ให้เกาะ ทั้งสามอยู่ในบทเรียน 4.1–4.3 (04_ping_two_targets.py, 07, 08)
ตรวจเองได้เสมอว่าโมดูลไหนมีอะไร — พิมพ์
print(dir(wifi))บนบอร์ด แล้วนับเอง อย่าเชื่อสไลด์รวมทั้งแผ่นนี้
lcd ui และ time ยังเป็นชุดเดิมจากชุดบทเรียนก่อนหน้าทุกประการ — ui.poll() หลังแก้ widget · ui.Seg7 รับข้อความ · ui.Chart เกิดมาพร้อมเส้นที่ 0 อยู่แล้ว · lcd.print() ส่งได้ 127 ไบต์ต่อครั้ง ส่วนป้าย ui พาได้ 126 ไบต์ทั้งตอนสร้างและตอนเรียก .text()
ความต่างของสามตัวบนอยู่ที่ เวลาของคำถาม ไม่ใช่ที่ข้อมูลที่มันคืนมา จำประโยคนี้ไว้แล้วทั้งบทเรียนจะง่ายขึ้น
step_lbl = ui.Label("ป้ายนี้ขึ้นก่อน แล้วจอจะนิ่ง", x=24, y=208, color=COL_TEXT,
value=20)
...
ui.poll() # เคาะให้ป้ายขึ้นจอจริง ๆ ก่อนเข้าบรรทัดที่บล็อก
...
t0 = time.ticks_ms()
ok = wifi.connect(WIFI_SSID, WIFI_PASS)
elapsed = time.ticks_diff(time.ticks_ms(), t0)
...
seg.text(str(elapsed)) # Seg7 รับข้อความ จึงคุมรูปแบบได้ seg.value(4218) ได้แค่จำนวนเต็ม
ป้ายที่สร้างไว้แต่ยังไม่ได้เคาะ จะไปโผล่ หลัง connect() จบ ซึ่งคือตอนที่ไม่มีใครต้องการมันแล้ว กฎ ui.poll() จากชุดบทเรียนก่อนหน้ากลายเป็นเรื่องเป็นเรื่องตายขึ้นมาตรงนี้
time.ticks_ms() กับ ticks_diff() เป็นคู่เดิมจากไฟล์ 07 ของชุดบทเรียนที่แล้ว ที่ต่างคือคราวนี้เราเอามันมาคร่อมบรรทัดที่ควบคุมไม่ได้ เพราะ "นานจัง" ไม่ใช่ข้อมูล ส่วน "4218 ms" เอาไปเทียบกันได้ทั้งระหว่างทีมและระหว่างรหัสถูกกับรหัสผิด
ถ้า
okเป็นFalseไฟล์นี้รายงานแล้วraise SystemExitทันที โปรแกรมที่พูดเฉพาะตอนสำเร็จ จะเงียบในจังหวะที่คนอยากรู้ที่สุด
"0.0.0.0" — และทำไมรหัสผิดถึงช้ากว่ารหัสถูกip = wifi.ip()
# ip() คืนสตริงเสมอ ตอนยังไม่มีที่อยู่มันคืน "0.0.0.0"
# ซึ่งเป็นสตริงที่ if ถือว่าจริง — ต้องเทียบตรง ๆ เท่านั้น
if ip == "0.0.0.0":
note.text("ลิงก์ขึ้นแล้วแต่ยังไม่ได้เลข IP กำลังรอ DHCP")
การเข้าร่วมเครือข่ายกับการได้เลขที่อยู่เป็นคนละขั้น ลิงก์อาจขึ้นแล้วแต่ยังไม่มีใครแจกเลขให้ ช่วงนั้น wifi.ip() คืน "0.0.0.0" ซึ่ง Python ถือว่าจริง เพราะมันไม่ใช่สตริงว่าง เขียน if wifi.ip(): จึงผ่านทั้งที่ยังส่งอะไรออกไม่ได้
นี่คือความล้มเหลวแบบที่ ไม่มี error ให้จับ โปรแกรมเดินผ่านไปตามปกติ แล้วรายงานเลขที่อยู่ปลอมขึ้นจออย่างมั่นใจ
ตาคุณ อยู่ท้ายไฟล์ 01 พิมพ์รหัสผ่านผิดไปหนึ่งตัวแล้วรันใหม่ จับเวลาเทียบกับรอบที่ถูก แล้วตอบว่าทำไมกรณีผิดถึงใช้เวลา มากกว่า ไม่ใช่น้อยกว่า
ความล้มเหลวแพงกว่าความสำเร็จเสมอในงานเครือข่าย เพราะมีการลองใหม่ซ่อนอยู่หลายรอบ — และนั่นคือเหตุผลที่ระบบจริงต้องมีเพดานว่าจะลองกี่ครั้ง
04_scan_the_room.py — บอร์ดได้ยินทั้งห้องnets = wifi.scan() # คืน list ของ (ssid, rssi, security, channel)
...
nets.sort(key=lambda net: net[1], reverse=True) # เรียงเอง scan() ไม่เรียงให้
...
st = wifi.status()
...
trap.text("status() rssi = " + str(st["rssi"]) + " แต่ของจริง " +
str(top_rssi) + " dBm")
ก่อนจะถามว่า "ต่อไม่ติดเพราะอะไร" ต้องตอบให้ได้ก่อนว่า บอร์ดได้ยินวงนั้นไหม — สองปัญหานี้แก้คนละทางกันสิ้นเชิง "ไม่ได้ยินเลย" ต้องกลับไปดูที่ Hotspot (ย่าน 2.4 GHz และเปิดค้างไว้ ตามข้อ 3–4 ของสไลด์ก่อนเริ่ม) ส่วน "ได้ยินแต่ต่อไม่ผ่าน" คือเรื่องรหัสผ่านของทีมเอง
ไฟล์นี้สร้างป้ายห้าแถวไว้ครบตั้งแต่ต้น ก่อน จะรู้ว่าเจอกี่วง เพราะถ้าไปสร้างข้างในลูป จำนวน widget จะขึ้นกับจำนวนวงที่บังเอิญเจอในห้องนั้น ห้องที่มีวงเกินงบของคอร์ส 32 ตัว (เพดานเฟิร์มแวร์ 64) จะทำให้โปรแกรมตายกลางคัน ทั้งที่โค้ดไม่ได้ผิด
ตาคุณ อยู่ท้ายไฟล์ เดินถือบอร์ดไปสุดห้องแล้วรันซ้ำ จดชื่อวงเดิมกับตัวเลข dBm ทั้งสองจุด แล้วดูว่าอันดับของมันเลื่อนไหม
02_link_uptime.py — ต่อติดแล้ว กับ ยังต่ออยู่ up = wifi.is_connected() # ถามใหม่ทุกรอบ นี่คือทั้งบทเรียนของไฟล์นี้
now = 1 if up else 0
...
if up: # --- งานของจอ ---
...
link_lbl.text("ต่ออยู่")
...
chart.set_next(s_link, 100)
else:
link_lbl.text("หลุด")
...
chart.set_next(s_link, 0)
...
if now != last: # --- งานของลิ้นชัก ---
last = now
...
lcd.print("<span class=error>" + str(elapsed // 1000) +
" s ลิงก์หลุด</span>")
ไฟล์นี้ไม่มีคำสั่งใหม่เลยสักตัว โครงเป็นของ 08_status_screen.py เป๊ะ ๆ เปลี่ยนแค่ต้นทางของค่า ของใหม่คือการเอาสี่โมดูลมาต่อกันบนจอใบเดียว ซึ่งเป็นสิ่งที่นักพัฒนามืออาชีพ 80 คนบอกว่าตัวอย่างโค้ดทั่วโลกขาดไปมากที่สุด
last = -1 แปลว่า "ยังไม่เคยรู้สถานะมาก่อน" รอบแรกจึงนับเป็นการเปลี่ยนเสมอ ตั้งต้นเป็น 1 เมื่อไร ประวัติจะไม่มีบรรทัดแรก
ตาคุณ อยู่ท้ายไฟล์ ระหว่างที่โปรแกรมกำลังเฝ้าดู เดินถือบอร์ดออกไปจนสุดห้องแล้วเดินกลับมา แล้วเปิดลิ้นชักดูว่าได้กี่บรรทัด และหลุดตอนวินาทีที่เท่าไร