ลงมือทำ: แผงควบคุมสัมผัสของเรา และ widget ขั้นต่อไป
โมดูล 2 — จากจอสู่ฮาร์ดแวร์ · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร
สร้างแผงควบคุม LED สามสีบนจอสัมผัสที่ไฟสถานะบนจอตรงกับหลอดจริงเสมอ ด้วยปุ่มเปิดกับปุ่มปิดที่แยกกัน ฟังก์ชัน set_led() ที่เป็นประตูเดียว และกล่องยืนยันก่อนปิดทั้งหมด แล้วรู้จัก widget กับเหตุการณ์ขั้นต่อไปที่หน้าจอควบคุมจริงใช้
เป้าหมาย
หัวข้อที่มีชื่อว่า “เป้าหมาย”เมื่อจบบทเรียนนี้ คุณจะ:
- เติมหกช่องว่างใน practice/s04_touch_panel.py จนแผง LED สามสีทำงานบนบอร์ดหรือ Emulator โดยไฟสถานะบนจอ ตัวเลข “ติดอยู่ n จาก 3” และบรรทัดสถานะตรงกับหลอดจริงในทุกกรณีที่ทดสอบ รวมทั้งหลัง “ปิดทั้งหมด”
- อธิบายได้ว่าทำไมแผงควบคุมจริงแยกปุ่มเปิดกับปุ่มปิด (คำสั่งที่กดซ้ำแล้วได้ผลเดิม) ทำไมทุกเส้นทางที่เปลี่ยนไฟต้องผ่าน set_led() ที่เดียว และทำไมเก็บสถานะไว้ใน led_on[] แทนการอ่านกลับจาก gpio.led(n).value()
- เลือก widget หรือชนิดเหตุการณ์ให้ตรงกับคำถามของหน้าจอได้ เช่น ui.Bar วางทับ ui.Scale เพื่อบอกพิสัย ui.Led เพื่อบอกสถานะ ui.Tabview เมื่อหน้าเดียวไม่พอ และ pressed / long_pressed_repeat / released ที่ขอด้วย .listen() เมื่อต้องรู้ว่านิ้วยังกดค้างอยู่
ก่อนเริ่ม
หัวข้อที่มีชื่อว่า “ก่อนเริ่ม”ต่อจากบทเรียน 2.5 เปิด 03_switch_matches_led.py ของบทเรียนนั้นไว้ข้าง ๆ เพราะ set_led() ในบทเรียนนี้คือแบบแผน
“ฟังก์ชันเดียวที่มีสิทธิ์เปลี่ยนสถานะ” ชุดเดียวกัน ก่อนเขียนโค้ด อ่านตาราง “กับดักที่เจอบ่อย” ในสไลด์
ห้าแถวแรกคือกฎเหล็กห้าข้อของ ui ในรูปอาการจริงที่จะเจอ
บนจอบอร์ดแตะการ์ด BENTO Playground ค้างไว้ และเตรียมกล้องมือถือไว้ถ่ายจอกับหลอดไฟในเฟรมเดียว
- อุปกรณ์: บอร์ด Eva Kit หรือ TESAIoT Dev Kit ที่ลงเฟิร์มแวร์ MicroPython ของ BENTO แล้ว หรือ BENTO Emulator ใน BENTO IDE (ส่วนขยาย 17_panel_to_broker.py ส่งถึง broker จริงจาก Emulator ผ่าน WebSocket ได้แล้ว)
- เรียนมาก่อน: บทเรียน 2.5 — event loop: แตะจอแล้วไฟจริงติด
โจทย์ของบทเรียนนี้ไม่ใช่ “กดแล้วไฟติด” แต่คือ จอกับไฟจริงต้องตรงกันเสมอ ความจริงของโปรแกรมอยู่ที่ list led_on[]
ที่เดียว จอเป็นแค่ผู้รายงาน เราไม่อ่านสถานะกลับจาก gpio.led(n).value() เพราะมันตอบระดับของขา ไม่ใช่สิ่งที่เราสั่ง
และหลัง hold() หรือ brightness() ค่าที่ได้เป็น 0 ทั้งที่ไฟอาจยังติดอยู่
แผงควบคุมจริงแยก ปุ่มเปิด กับ ปุ่มปิด เสมอ ปุ่มสลับบอกไม่ได้ว่าตอนนี้อยู่สถานะไหน คนกดต้องเดา
set_led(i, True) จึงเป็นคำสั่ง “เปิด” ไม่ใช่ “สลับ” กดซ้ำสิบครั้งได้ผลเท่ากดครั้งเดียว (idempotent)
ไฟสถานะ ui.Led แยกจากปุ่ม เพราะไฟตอบว่า “ตอนนี้เป็นยังไง” ส่วนปุ่มตอบว่า “สั่งอะไรได้” และ .value(0) ทำให้ไฟหรี่
ไม่ใช่หาย คนดูจึงแยกออกว่าไฟดับหรือจอเสีย
ทุกเส้นทางที่เปลี่ยนไฟต้องผ่าน set_led() ซึ่งทำสี่อย่างตามลำดับ คือจำลง led_on[i] สั่งหลอดจริงด้วย
gpio.led(LED_IDX[i]) ให้ไฟบนจอสะท้อนของจริง แล้วเรียก show_status() ตอนเริ่มโปรแกรมเราสั่งให้ทุกดวงดับจริง
ไม่สมมติว่าดับอยู่แล้ว เลขดวงใน LED_IDX ถามจากชื่อใน gpio.board_info()["led_names"] เพราะ Eva Kit กับ Dev Kit
เรียงดวงไม่เหมือนกัน (บน Dev Kit สีน้ำเงินคือดวง 3 และดวง 0-1 อยู่บนโมดูล)
“ปิดทั้งหมด” เปลี่ยนของจริงสามชิ้นในการแตะครั้งเดียว จึงต้องเปิดกล่องยืนยันก่อน กล่องกับปุ่มคำตอบสองปุ่มถูกสร้างพร้อมหน้าจอ แล้วซ่อนไว้ ไม่ได้สร้างตอนคนกด และคำยืนยันบอกสิ่งที่จะเกิด (“ไฟทุกสีจะดับพร้อมกัน”) ไม่ใช่ถามลอย ๆ ว่าแน่ใจไหม ทั้งหน้าใช้ 22 widget ต่ำกว่างบของคอร์ส 32 (เพดานเฟิร์มแวร์ 64) นับบนกระดาษก่อนพิมพ์โค้ดทุกครั้ง
ครึ่งหลังของสไลด์แนะนำของขั้นต่อไป:
ui.Scaleคือไม้บรรทัด ไม่รับ.value()ตัวที่ขยับคือui.Barที่วางทับ ส่วนui.Spinboxใช้ป้อนค่าที่แถบเลื่อนป้อนไม่ได้ เช่น 23.75ui.Tableจัดคอลัมน์ให้เอง จะแก้ค่าในแถวเดิมใช้.cell()ไม่ใช่.add_row()และ widget ที่.hide()ไว้ยังกินโควตา มีแต่.delete()ที่คืนui.Tabviewแบ่งพื้นที่เป็นหลายหน้า แท็บสามใบกิน 4 แฮนเดิลvalue=ของมันคือความสูงแถบแท็บ และของที่ต้องเห็นตลอดเวลาห้ามอยู่ในแท็บ ส่วนparent=ทำให้x=0, y=0ของลูกคือมุมของกล่อง ไม่ใช่มุมจอclickedมาถึงตอนปล่อยนิ้วแล้ว ถ้าต้องรู้ว่ายังกดค้างอยู่ใช้pressedlong_pressed_repeatreleasedซึ่งต้องขอด้วย.listen()ก่อน เพราะคิวเหตุการณ์มีแค่ 16 ช่อง และเมื่อคิวเต็ม ของใหม่คือตัวที่ถูกทิ้ง
ตัวอย่างสมบูรณ์
หัวข้อที่มีชื่อว่า “ตัวอย่างสมบูรณ์”ไฟล์ 07–17 ไม่ต้องเปิดครบในบทเรียน ทำแผงให้ผ่านเกณฑ์ก่อน แล้วเปิดตามอาการที่เจอ ก่อนรันแต่ละไฟล์ให้ทายก่อนว่าจอจะแสดงอะไร แล้วค่อยรันเทียบ
- อยากย้าย ย่อ ซ่อน หรือลบ widget ที่สร้างไปแล้ว: 07 (
ui.list()ใช้คีย์idส่วนui.poll()ใช้คีย์handle) - ต้องใช้ช่องเลือกหรือช่องพิมพ์: 08 (
Dropdownส่งกลับแค่ลำดับของตัวเลือก เริ่มที่ 0 ไม่ได้ส่งข้อความ) - หน้าจอต้องบอกพิสัย สถานะ และรับค่าที่ผู้ใช้ป้อน: 09 ซึ่งบทเรียน 2.7–2.9 ใช้ต่อทันที
- ของที่ต้องโชว์เกินหนึ่งหน้า: 10 (แท็บ) และ 11 (เมนูเป็นชั้น) แล้วต่อด้วย 14 เรื่องระบบพิกัดของกล่อง
- หลายค่าพร้อมกัน หรือคำสั่งที่ต้องถามก่อน: 12 และ 13
- ต้องรู้ว่านิ้วยังกดค้าง ปัด หรือกำลังเลื่อนรายการ: 15 และ 16
ไฟล์ 17 เป็นส่วนขยายที่ไม่อยู่ในเกณฑ์ผ่าน ต้องแก้ TEAM = "teamXX" เป็นรหัสที่ไม่ซ้ำใครก่อน เช่น nok4821 (ค่าตั้งต้นจงใจให้ไม่ยอมรัน)
และต้องมี WiFi ที่ออกพอร์ต 1883 ได้ พอร์ตนี้ไม่เข้ารหัสและ broker เป็นของสาธารณะ ห้ามส่งอะไรที่เป็นความลับ
| ไฟล์ | ไฟล์นี้สอน |
|---|---|
| examples/07_find_move_hide_delete.py | จัดการ widget ที่สร้างไปแล้ว |
| examples/08_dropdown_textarea.py | อีกสามชนิดที่รับอินพุตได้ และค่าที่ถามกลับได้จริง |
| examples/09_scale_led_spinbox.py | สาม widget ที่แยกหน้าจอ HMI ออกจากหน้าจอเล่น ๆ |
| examples/10_tabview_second_screen.py | จอที่สองที่ไม่ต้องเขียนโปรแกรมที่สอง |
| examples/11_menu_settings_tree.py | จอที่สองแบบที่สอง เมื่อของไม่ได้เท่ากันทุกหัวข้อ |
| examples/12_table_and_list.py | หลายค่าพร้อมกัน ต้องเป็นตาราง ไม่ใช่ป้ายเรียงกัน |
| examples/13_confirm_before_acting.py | คำสั่งที่ทำให้ของจริงขยับ ต้องถามก่อนหนึ่งครั้ง |
| examples/14_container_coordinates.py | กล่องที่คอนเทนเนอร์คืนมา และระบบพิกัดของมัน |
| examples/15_press_and_hold.py | ปุ่มที่ต้องกดค้าง และเหตุการณ์ที่ clicked บอกไม่ได้ |
| examples/16_swipe_scroll_focus.py | ปัดนิ้ว เลื่อนรายการ และช่องไหนกำลังถูกเลือก |
| examples/17_panel_to_broker.py | แตะบนจอบอร์ด แล้วหน้าเว็บของเพื่อนเห็น สั่งจากเว็บ แล้วแถบบนจอขยับ |
สไลด์ของบทเรียนนี้อ้างถึงไฟล์ที่อยู่ในบทเรียนอื่นด้วย:
- m02-ui-to-hardware/l05-event-loop/examples/01_first_widgets.py — widget ตัวแรก และเหตุผลที่ต้องใส่ x กับ y ทุกครั้ง
- m02-ui-to-hardware/l05-event-loop/examples/02_event_types.py — เหตุการณ์หน้าตาเป็นอย่างไร และใครส่งอะไร
- m02-ui-to-hardware/l05-event-loop/examples/03_switch_matches_led.py — จอกับไฟจริงต้องพูดตรงกันเสมอ
- m02-ui-to-hardware/l05-event-loop/examples/04_seg7_takes_text.py — Seg7 รับได้ทั้งสองทาง แต่ให้ผลไม่เท่ากัน
- m02-ui-to-hardware/l05-event-loop/examples/05_sound_feedback.py — เสียงตอบรับตอนแตะปุ่ม
- m02-ui-to-hardware/l05-event-loop/examples/06_layout_budget.py — พื้นที่ 792x398 กับงบ widget: งบของคอร์ส 32 ตัว (เพดานเฟิร์มแวร์ 64)
- shared/usecase/07_hold_to_confirm.py — กดค้างเพื่อยืนยันคำสั่งที่ย้อนกลับไม่ได้
ภาพจอจาก BENTO Emulator ของตัวอย่างในบทนี้ (คลิกชื่อไฟล์เพื่อเปิดโค้ด)

07_find_move_hide_delete.py จัดการ widget ที่สร้างไปแล้ว
08_dropdown_textarea.py อีกสามชนิดที่รับอินพุตได้ และค่าที่ถามกลับได้จริง
09_scale_led_spinbox.py สาม widget ที่แยกหน้าจอ HMI ออกจากหน้าจอเล่น ๆ
10_tabview_second_screen.py จอที่สองที่ไม่ต้องเขียนโปรแกรมที่สอง
11_menu_settings_tree.py จอที่สองแบบที่สอง เมื่อของไม่ได้เท่ากันทุกหัวข้อ
12_table_and_list.py หลายค่าพร้อมกัน ต้องเป็นตาราง ไม่ใช่ป้ายเรียงกัน
13_confirm_before_acting.py คำสั่งที่ทำให้ของจริงขยับ ต้องถามก่อนหนึ่งครั้ง
14_container_coordinates.py กล่องที่คอนเทนเนอร์คืนมา และระบบพิกัดของมัน
15_press_and_hold.py ปุ่มที่ต้องกดค้าง และเหตุการณ์ที่ clicked บอกไม่ได้
16_swipe_scroll_focus.py ปัดนิ้ว เลื่อนรายการ และช่องไหนกำลังถูกเลือก
17_panel_to_broker.py แตะบนจอบอร์ด แล้วหน้าเว็บของเพื่อนเห็น สั่งจากเว็บ แล้วแถบบนจอขยับฝึกเติม
หัวข้อที่มีชื่อว่า “ฝึกเติม”ไฟล์ฝึกมีช่องว่าง # เติม: หกจุด เรียงตามลำดับที่โปรแกรมทำงานจริง
- จุด 1: สร้างปุ่ม “เปิด” ของแต่ละแถวด้วย
btn_on.append(ui.Button("เปิด", ...)) - จุด 2–3: ใน
set_led()สั่งหลอดจริงด้วยgpio.led(LED_IDX[i]).on()และ.off() - จุด 4–5: ในลูปเหตุการณ์ แปลง
handleกลับเป็นแถวด้วยset_led(on_ids.index(h), True)และset_led(off_ids.index(h), False) - จุด 6:
time.sleep_ms(50)ท้ายลูป
เติมทีละจุดแล้วส่งขึ้นบอร์ดทุกครั้ง อย่าเติมครบหกจุดแล้วรันทีเดียว เพราะจะไม่รู้ว่าพังที่จอ ที่ไฟ หรือที่ตรรกะ
หลังเติมจุด 2–3 ลองเรียก set_led(0, True) บรรทัดเดียวก่อนลูป ถ้าไฟติด ฝั่งฮาร์ดแวร์เรียบร้อยแล้ว
| ไฟล์ฝึก | เรื่อง |
|---|---|
| practice/s04_touch_panel.py | แผงควบคุม LED บนจอสัมผัส (ฉบับฝึกเติมโค้ด) |
เปิดเฉลยหลังจากลองเองแล้วอย่างน้อยหนึ่งรอบ แล้วอ่าน วิธีใช้เฉลย ก่อน
| เฉลย | คู่กับ |
|---|---|
| solution/s04_touch_panel.py | practice/s04_touch_panel.py |
เช็กความเข้าใจ
หัวข้อที่มีชื่อว่า “เช็กความเข้าใจ”คำถามชุดเดียวกันอยู่ใน quiz.yaml สำหรับระบบที่ตรวจอัตโนมัติ
-
ในเฉลย ปุ่ม “เปิด” แถวแดงเรียก set_led(0, True) แทนคำสั่งที่สลับสถานะไฟ ข้อดีหลักของการออกแบบนี้คืออะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)
- ก) กดซ้ำกี่ครั้งก็ได้ผลเดิม คนที่กดซ้ำเพราะไม่แน่ใจจะไม่ได้ผลตรงข้ามกับที่ตั้งใจ
- ข) ใช้ widget น้อยกว่าปุ่มสลับ
- ค) ไม่ต้องเรียก ui.poll() ในลูปอีก
- ง) ไฟติดเร็วขึ้นเพราะไม่ต้องผ่านคิวเหตุการณ์
เฉลย
ก — คำสั่งที่ระบุปลายทาง (idempotent) ได้ผลเดิมไม่ว่ากดกี่ครั้ง ต่างจากคำสั่งสลับที่ผลขึ้นกับสถานะก่อนหน้าซึ่งคนกดมองไม่เห็น ส่วนเรื่องจำนวน widget ปุ่มแยกกลับใช้มากกว่าด้วยซ้ำ
-
ทีมหนึ่งให้ show_status() นับดวงที่ติดโดยอ่าน gpio.led(n).value() แล้วพบว่าไฟดวงที่สามติดอยู่แต่จอบอกว่าดับ ข้อใดคือวิธีแก้ที่ถูก (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)
- ก) เก็บสถานะที่สั่งไว้ใน led_on[] ของเราเอง แล้วให้จอรายงานจากตรงนั้น
- ข) อ่าน gpio.led(n).value() สองครั้งติดกันแล้วใช้ค่าหลัง
- ค) เพิ่ม time.sleep_ms(200) ก่อนอ่านค่า
- ง) อ่านสถานะจากสีของปุ่มบนจอแทน
เฉลย
ก — gpio.led(n).value() ตอบระดับของขา ไม่ใช่สิ่งที่เราสั่ง และคืน 0 หลัง hold() หรือ brightness() ความจริงของโปรแกรมจึงต้องอยู่ที่ led_on[] ที่เดียว แล้วให้จอเป็นผู้รายงาน
-
เรียงสิ่งที่ set_led(i, on) ในเฉลยทำ ให้ถูกลำดับ (เรียงลำดับ · เป้าหมายข้อ 1)
- ก) สั่งหลอดจริงด้วย gpio.led(LED_IDX[i])
- ข) เรียก show_status() ให้ตัวเลข “ติดอยู่ n จาก 3” ตรง
- ค) จำค่าลง led_on[i]
- ง) ให้ไฟสถานะบนจอ lamps[i] สะท้อนของจริง
เฉลย
ค → ก → ง → ข — จำไว้ก่อน สั่งของจริง ให้ไฟบนจอสะท้อน แล้วรายงานให้ครบ เมื่อทุกเส้นทางที่เปลี่ยนไฟผ่านประตูนี้ประตูเดียว จอก็ไม่มีทางพูดไม่ตรงกับหลอด
-
ทีมที่ถือ Dev Kit เขียน LED_IDX = [0, 1, 2] ตายตัว แตะ “เปิด” แถวน้ำเงินแล้วไม่เห็นหลอดสีน้ำเงินติด สาเหตุคืออะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)
- ก) สองบอร์ดเรียงดวงไม่เหมือนกัน บน Dev Kit สีน้ำเงินคือดวง 3 และดวง 0-1 อยู่บนโมดูล ต้องถามเลขดวงจากชื่อใน gpio.board_info()[“led_names”]
- ข) Dev Kit ไม่มีหลอดสีน้ำเงินเลย
- ค) หลอดจริงจะติดก็ต่อเมื่อ ui.Led บนจอถูกตั้ง value=1 ก่อน
- ง) ปุ่มแถวที่สามส่งเหตุการณ์ toggled ไม่ใช่ clicked
เฉลย
ก — Eva Kit กับ Dev Kit เรียงดวงไม่เหมือนกัน เฉลยจึงให้ LED_IDX มาจากชื่อ RGB_RED / RGB_GREEN / RGB_BLUE ที่บอร์ดรายงาน แทนเลขดวงที่จำมา ทุกปุ่มในแผงนี้ส่ง clicked เหมือนกันหมด
-
ข้อใดถูกเกี่ยวกับ widget และเหตุการณ์ขั้นต่อไปในบทเรียนนี้ เลือกทุกข้อที่ถูก (เลือกได้หลายข้อ · เป้าหมายข้อ 3)
- ก) ui.Scale ไม่รับ .value() ตัวที่ขยับคือ ui.Bar ที่วางทับ
- ข) value= ของ ui.Tabview คือความสูงของแถบแท็บ
- ค) .hide() คืนโควตา widget ให้ทันที
- ง) clicked มาถึงตั้งแต่นิ้วแตะลง จึงใช้ทำปุ่มเร่งระหว่างกดค้างได้
- จ) long_pressed_repeat ต้องขอด้วย .listen() ก่อน ไม่งั้นปุ่มจะไม่ส่งเลย
เฉลย
ก, ข, จ — Scale คือไม้บรรทัด Tabview ใช้ value= เป็นความสูงแถบแท็บ และเหตุการณ์กดค้างเงียบจนกว่าจะขอด้วย .listen() ส่วน .hide() ยังกินโควตา (มีแต่ .delete() ที่คืน) และ clicked มาถึงตอนปล่อยนิ้วแล้ว
MVP ของชุดบทเรียน 2.4–2.6 ทำบนบอร์ดจริงหรือ Emulator แล้วจดผลลงบันทึกการเรียน
- จอมีสามแถว แถวละไฟสถานะ ปุ่ม “เปิด” และปุ่ม “ปิด” การ์ดขวามีตัวเลข “ติดอยู่ n จาก 3 ดวง” ปุ่ม “เปิดทั้งหมด” และ “ปิดทั้งหมด” และมีบรรทัดสถานะข้างหัวเรื่อง
- แตะ “เปิด” แถวไหน หลอดไฟจริงบนบอร์ดติดสีนั้น แตะ “ปิด” แล้วดับ และกด “เปิด” ซ้ำสิบครั้งได้ผลเท่ากดครั้งเดียว
- “เปิดทั้งหมด” ติดครบสามสี “ปิดทั้งหมด” เปิดกล่องยืนยันก่อน ยืนยันแล้วดับหมด และกด “ไม่ปิด” แล้วไฟไม่เปลี่ยน
- ไฟสถานะบนจอ ตัวเลข “ติดอยู่ n จาก 3” และบรรทัดสถานะ ตรงกับหลอดจริงในทุกกรณีที่ทดสอบ รวมทั้งหลัง “ปิดทั้งหมด” (ข้อนี้ตกกันมากที่สุด ทดสอบเป็นข้อสุดท้ายเสมอ)
- ถ่ายรูปหรือคลิปที่เห็นทั้งจอและหลอดไฟในเฟรมเดียวกัน
การบ้านของทีม: เลือกทำหนึ่งในสี่ข้อต่อยอดในสไลด์ คือปุ่ม START/STOP ของไฟวิ่งด้วยตัวแปร flag, ตัวนับการกดกับปุ่ม RESET,
สวิตช์ LOCK ที่ทำให้ปุ่มทั้งหมดกดไม่ได้ หรือปุ่มจริง gpio.button(0) ที่ทำงานร่วมกับปุ่มบนจอโดยระวัง debounce
ทั้งสี่ข้อเดินบน event loop เดิม ห้ามหลับรอด้วย sleep จนแตะปุ่มอื่นไม่ได้ และบน Dev Kit ห้ามโยกสวิตช์บนฐาน เพราะหลายตัวคือสวิตช์ตัดไฟ
บทเรียน 2.7 กลับทิศจาก “จอสั่งฮาร์ดแวร์” เป็นลูกบิดกับแผ่นสัมผัสที่สั่งจอ
บทเรียนถัดไป: บทเรียน 2.7 — อนาล็อกและสัมผัส: ADC ลูกบิด และ CapSense
สะท้อนคิด
หัวข้อที่มีชื่อว่า “สะท้อนคิด”- ถ้าคำสั่งเปิดไฟมาจากอินเทอร์เน็ตแทนนิ้ว โค้ดส่วนไหนต้องเปลี่ยน ส่วนไหนไม่ต้องเปลี่ยนเลย และเราจะรู้ได้อย่างไรว่าคำสั่งมาถึงจริง
- ในงานของทีม มีข้อมูลอะไรบ้างที่ต้องเห็นตลอดเวลา และถ้าหน้าจอมีแท็บ ข้อมูลนั้นอยู่นอกแท็บหรือยัง
คำถามทบทวน
ลองตอบเองก่อน แล้วค่อยเปิดดูเฉลย
-
ในเฉลย ปุ่ม "เปิด" แถวแดงเรียก set_led(0, True) แทนคำสั่งที่สลับสถานะไฟ ข้อดีหลักของการออกแบบนี้คืออะไร (เป้าหมายข้อ 2)
- กดซ้ำกี่ครั้งก็ได้ผลเดิม คนที่กดซ้ำเพราะไม่แน่ใจจะไม่ได้ผลตรงข้ามกับที่ตั้งใจ
- ใช้ widget น้อยกว่าปุ่มสลับ
- ไม่ต้องเรียก ui.poll() ในลูปอีก
- ไฟติดเร็วขึ้นเพราะไม่ต้องผ่านคิวเหตุการณ์
ดูเฉลย
คำตอบ: A. กดซ้ำกี่ครั้งก็ได้ผลเดิม คนที่กดซ้ำเพราะไม่แน่ใจจะไม่ได้ผลตรงข้ามกับที่ตั้งใจ
คำสั่งที่ระบุปลายทาง (idempotent) ได้ผลเดิมไม่ว่ากดกี่ครั้ง ต่างจากคำสั่งสลับที่ผลขึ้นกับสถานะก่อนหน้าซึ่งคนกดมองไม่เห็น ส่วนเรื่องจำนวน widget ปุ่มแยกกลับใช้มากกว่าด้วยซ้ำ
-
ทีมหนึ่งให้ show_status() นับดวงที่ติดโดยอ่าน gpio.led(n).value() แล้วพบว่าไฟดวงที่สามติดอยู่แต่จอบอกว่าดับ ข้อใดคือวิธีแก้ที่ถูก (เป้าหมายข้อ 2)
- เก็บสถานะที่สั่งไว้ใน led_on[] ของเราเอง แล้วให้จอรายงานจากตรงนั้น
- อ่าน gpio.led(n).value() สองครั้งติดกันแล้วใช้ค่าหลัง
- เพิ่ม time.sleep_ms(200) ก่อนอ่านค่า
- อ่านสถานะจากสีของปุ่มบนจอแทน
ดูเฉลย
คำตอบ: A. เก็บสถานะที่สั่งไว้ใน led_on[] ของเราเอง แล้วให้จอรายงานจากตรงนั้น
gpio.led(n).value() ตอบระดับของขา ไม่ใช่สิ่งที่เราสั่ง และคืน 0 หลัง hold() หรือ brightness() ความจริงของโปรแกรมจึงต้องอยู่ที่ led_on[] ที่เดียว แล้วให้จอเป็นผู้รายงาน
-
เรียงสิ่งที่ set_led(i, on) ในเฉลยทำ ให้ถูกลำดับ (เป้าหมายข้อ 1)
- สั่งหลอดจริงด้วย gpio.led(LED_IDX[i])
- เรียก show_status() ให้ตัวเลข "ติดอยู่ n จาก 3" ตรง
- จำค่าลง led_on[i]
- ให้ไฟสถานะบนจอ lamps[i] สะท้อนของจริง
ดูเฉลย
ลำดับที่ถูก: C. จำค่าลง led_on[i] → A. สั่งหลอดจริงด้วย gpio.led(LED_IDX[i]) → D. ให้ไฟสถานะบนจอ lamps[i] สะท้อนของจริง → B. เรียก show_status() ให้ตัวเลข "ติดอยู่ n จาก 3" ตรง
จำไว้ก่อน สั่งของจริง ให้ไฟบนจอสะท้อน แล้วรายงานให้ครบ เมื่อทุกเส้นทางที่เปลี่ยนไฟผ่านประตูนี้ประตูเดียว จอก็ไม่มีทางพูดไม่ตรงกับหลอด
-
ทีมที่ถือ Dev Kit เขียน LED_IDX = [0, 1, 2] ตายตัว แตะ "เปิด" แถวน้ำเงินแล้วไม่เห็นหลอดสีน้ำเงินติด สาเหตุคืออะไร (เป้าหมายข้อ 1)
- สองบอร์ดเรียงดวงไม่เหมือนกัน บน Dev Kit สีน้ำเงินคือดวง 3 และดวง 0-1 อยู่บนโมดูล ต้องถามเลขดวงจากชื่อใน gpio.board_info()["led_names"]
- Dev Kit ไม่มีหลอดสีน้ำเงินเลย
- หลอดจริงจะติดก็ต่อเมื่อ ui.Led บนจอถูกตั้ง value=1 ก่อน
- ปุ่มแถวที่สามส่งเหตุการณ์ toggled ไม่ใช่ clicked
ดูเฉลย
คำตอบ: A. สองบอร์ดเรียงดวงไม่เหมือนกัน บน Dev Kit สีน้ำเงินคือดวง 3 และดวง 0-1 อยู่บนโมดูล ต้องถามเลขดวงจากชื่อใน gpio.board_info()["led_names"]
Eva Kit กับ Dev Kit เรียงดวงไม่เหมือนกัน เฉลยจึงให้ LED_IDX มาจากชื่อ RGB_RED / RGB_GREEN / RGB_BLUE ที่บอร์ดรายงาน แทนเลขดวงที่จำมา ทุกปุ่มในแผงนี้ส่ง clicked เหมือนกันหมด
-
ข้อใดถูกเกี่ยวกับ widget และเหตุการณ์ขั้นต่อไปในบทเรียนนี้ เลือกทุกข้อที่ถูก (เป้าหมายข้อ 3)
- ui.Scale ไม่รับ .value() ตัวที่ขยับคือ ui.Bar ที่วางทับ
- value= ของ ui.Tabview คือความสูงของแถบแท็บ
- .hide() คืนโควตา widget ให้ทันที
- clicked มาถึงตั้งแต่นิ้วแตะลง จึงใช้ทำปุ่มเร่งระหว่างกดค้างได้
- long_pressed_repeat ต้องขอด้วย .listen() ก่อน ไม่งั้นปุ่มจะไม่ส่งเลย
ดูเฉลย
คำตอบ: A. ui.Scale ไม่รับ .value() ตัวที่ขยับคือ ui.Bar ที่วางทับ · B. value= ของ ui.Tabview คือความสูงของแถบแท็บ · E. long_pressed_repeat ต้องขอด้วย .listen() ก่อน ไม่งั้นปุ่มจะไม่ส่งเลย
Scale คือไม้บรรทัด Tabview ใช้ value= เป็นความสูงแถบแท็บ และเหตุการณ์กดค้างเงียบจนกว่าจะขอด้วย .listen() ส่วน .hide() ยังกินโควตา (มีแต่ .delete() ที่คืน) และ clicked มาถึงตอนปล่อยนิ้วแล้ว
อ้างอิงบทเรียนนี้
ถ้านำบทเรียนนี้ไปสอน ทำสไลด์ หรือทำเอกสารต่อ ให้อ้างอิงด้วยข้อความนี้ ถ้าดัดแปลงเนื้อหา ให้เติม (ดัดแปลง)ต่อท้ายชื่อบทเรียน
"ลงมือทำ: แผงควบคุมสัมผัสของเรา และ widget ขั้นต่อไป" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
ข้อความอ้างอิงภาษาอังกฤษ: "Hands-on: your touch control panel and the next widgets" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0
ลิงก์บทเรียน: https://tesaiot.github.io/tesa-qualification-program/courses/aiot-micropython/m02-ui-to-hardware/l06-touch-panel-lab/
บทเรียนนี้ดัดแปลงจากต้นฉบับด้านล่าง เมื่ออ้างอิงให้คงเครดิตต้นฉบับไว้ด้วย
https://github.com/Advance-Innovation-Centre-AIC/embedded-systems-for-aiot-developer/blob/a80bbe88a34bcb9bb8d991f42f9252b77cdab079/session-04.html (slides 36–68)
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA