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

บทเรียน 2.1 — โมดูล gpio: LED ปุ่ม และบอร์ดที่บอกได้ว่ามีอะไร

LED ทุกดวงบนบอร์ด ปุ่มหนึ่งปุ่ม และลูปที่ไม่มีวันหยุด

โมดูล 2 — จากจอสู่ฮาร์ดแวร์

คาถาประจำบทเรียน: โปรแกรมฝังตัวคือลูปที่อ่านของจริง ตัดสิน แล้วสั่งของจริงกลับไป

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

เริ่มจากของที่เราเคยแตะแล้ว

วันนี้เราแทนที่ "นิ้ว + หน้า Controls (Eva)" ด้วย "ลูป Python ของเราเอง" หน้า Controls (Eva Kit) แตะด้วยนิ้ว s03_led_button.py gpio.led(i).on() btn.is_pressed() ลูปของทีมเรา บน CM33 ไม่ต้องพึ่งหน้า Controls หลอดจริงบนบอร์ด Eva: D3 D4 D5 · Dev Kit: 5 ดวง เดิม วันนี้ หลอดเดียวกัน ปุ่มเดียวกัน เปลี่ยนแค่ว่าใครเป็นคนสั่ง

เปิดบอร์ด แตะการ์ด Controls แล้วแตะวงกลมสีบนจอ หลอด LED จริงบนบอร์ดติดตามนิ้วเรา (การ์ดนี้มีเฉพาะ Eva Kit — ทีมที่ถือ Dev Kit ให้รัน 11_lights_and_a_button.py ซ้ำอีกรอบแล้วมองหลอดแทน ได้เห็นสิ่งเดียวกัน: ของจริงเปลี่ยนสถานะเพราะคำสั่ง)

บทเรียน 1.1–1.3 เราเรียกสิ่งนี้ว่า "แตะกระจกแล้วของจริงเปลี่ยนสถานะ" วันนี้เราจะทำสิ่งเดียวกัน แต่ไม่ผ่านนิ้ว

วันนี้เราจะสั่งไฟดวงเดียวกันนี้ด้วยลูปที่ทีมเขียนเอง — บทเรียน 1.1–1.3 เรารัน 11_lights_and_a_button.py ไปแล้ว และเห็นไฟติดจริง แต่นั่นคือโค้ดที่เขียนมาให้ วันนี้เราเปิดโมดูล gpio ออกดูให้ครบทุกคำสั่ง แล้วประกอบลูปของเราขึ้นมาใหม่

หน้า Controls (บน Eva Kit) เป็นโปรแกรมที่คนอื่นเขียนไว้ให้ พอจบชุดบทเรียนนี้เราจะมีโปรแกรมของเราเองที่ทำงานกับหลอดไฟและปุ่มเดียวกัน โดยไม่ต้องพึ่งหน้านั้นอีก — และรันได้ทั้งสองบอร์ด

ของบนบอร์ดไม่ได้เป็นของหน้าจอไหนเป็นเจ้าของ — ใครสั่งก่อนก็ได้ไป

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

จากชุดบทเรียนก่อนหน้า — บันไดสามขั้นของบทเรียน 1.4–1.6 ยังอยู่ในมือเรา

บทเรียน 1.4–1.6 ปีนบันไดนี้ไปแล้ว ท้ายบทเรียนวันนี้เราจะปีนซ้ำ แล้ววางปุ่มกับไฟไว้บนยอด 1 · WiFi wifi.connect() 2 · ได้ IP ไม่ใช่ "0.0.0.0" 3 · broker mqtt.connect() ท้ายบทเรียนวันนี้ ปุ่มที่กันเด้งแล้ว ไฟที่จำสถานะเอง ขึ้น broker ทั้งคู่

ทบทวนสั้น ๆ ก่อนลืม เพราะท้ายบทเรียนเราต้องใช้ทุกขั้น

  • wifi.connect() บล็อกได้นานราว 85 วินาทีถ้าวงนั้นไม่มีในห้อง ป้าย "กำลังต่อ" จึงต้องขึ้นจอก่อนบรรทัดนี้
  • wifi.ip() ที่ยังได้ "0.0.0.0" แปลว่ามีลิงก์แต่ยังไม่มีที่อยู่ ส่งอะไรออกไม่ได้
  • mqtt.publish() ตอนสายหลุดไม่ได้คืน False มันโยน OSError · get_message() มีช่องเดียว ใบใหม่ทับใบเก่า

บอร์ดยังต่อ Hotspot มือถือของทีม เหมือนบทเรียน 1.4–1.6 (WiFi ขององค์กรต้อง login บอร์ดใช้ไม่ได้) WIFI_SSID WIFI_PASS ในไฟล์ 07 กับ 08 ใช้ชื่อกับรหัสชุดเดิม · ชุดบทเรียนนี้ใช้ broker สาธารณะ broker.hivemq.com พอร์ต 1883 และชื่อทีมที่ผู้สอนแจก (team01 ถึง team19) ไฟล์ท้ายบทเรียนตั้งต้นเป็น TEAM = "teamXX" และไม่ยอมรันจนกว่าจะแก้ ใช้ชื่อทีมคนอื่นเมื่อไร บอร์ดของทีมนั้นถูกเตะหลุดทันที

วันนี้ครึ่งแรกเป็นเรื่องบนโต๊ะล้วน ๆ ไฟ ปุ่ม และเวลา พอนับปุ่มได้ตรงแล้ว เราค่อยส่งมันออกไป

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

ทำไม · คืออะไร · ทำยังไง — แผนที่ของชุดบทเรียนนี้

คำถาม คำตอบของชุดบทเรียนนี้ อยู่ช่วงไหน
Why ทำไมต้องคุมขาสัญญาณเอง ในเมื่อจอก็แสดงผลได้แล้ว เพราะ IoT ไม่ได้จบที่การแสดงค่า มันต้องสั่งของจริงให้ขยับ ทุกเครื่องจักรที่คุณเคยเห็นเปิดปิดเอง มีบรรทัดแบบนี้อยู่ข้างใน ครึ่งแรก · ทฤษฎีกระแสสองเส้นทาง
What มีอะไรให้ใช้บ้าง โมดูล gpio ทั้ง 18 ชื่อ — ตัวไหนตอบ "สิ่งที่วัดได้" ตัวไหนตอบแค่ "สิ่งที่เราสั่งไป" · บวก time.ticks_ms() ticks_diff() สำหรับคุมจังหวะ สไลด์บัญชี 18 ชื่อ
How ประกอบยังไงให้ใช้งานได้จริง ลูป polling ที่อ่านปุ่ม กันสัญญาณเด้ง แล้วสั่งไฟตามจังหวะที่ทีมตั้งเอง หกไฟล์ตัวอย่าง + ไฟล์ฝึก · ท้ายบทเรียนอีกสองไฟล์ที่ส่งขึ้น broker (07 งานประยุกต์ · 08 เกมทั้งห้อง)

ปลายทางที่จับต้องได้ — ไฟทุกดวงบนบอร์ดวิ่งไล่กันตามจังหวะที่ทีมตั้ง (Eva 3 ดวง · Dev Kit 5 ดวง — โค้ดชุดเดียวกัน) และทุกครั้งที่กดปุ่มบนบอร์ด ตัวเลขบนจอเพิ่มขึ้นหนึ่ง

บทเรียน 1.1–1.3 เห็นไฟติดจากโค้ดที่เราเขียนให้ · ชุดบทเรียนนี้เป็นคนเขียนลูปที่สั่งมันเอง

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

เป้าหมายของชุดบทเรียนนี้

  1. สั่ง LED บนบอร์ดด้วย gpio.led(n) ได้ทั้ง เปิด ปิด สลับสถานะ และสั่งด้วยตัวเลขที่คำนวณได้
  2. อ่านปุ่มจริงด้วย gpio.button(0).is_pressed() แล้วบอกได้ว่า polling คืออะไร
  3. เขียนลูปที่คุมจังหวะด้วย time.sleep_ms() และ time.ticks_ms()/ticks_diff() เป็น
  4. อธิบายได้ว่าทำไมปุ่มถึง "เด้ง" และแก้ด้วยซอฟต์แวร์ได้
  5. บอกได้ว่าโมดูล gpio มีอะไรอยู่ทั้งโมดูล และตัวไหนตอบ "สิ่งที่วัดได้" ตัวไหนตอบแค่ "สิ่งที่เราสั่งไป"
  6. รู้จักกับดักชื่อของเฟิร์มแวร์ตัวนี้ — ชื่อที่โค้ดคืนกับป้ายบนแผ่นวงจรไม่ใช่ตัวเดียวกัน และรู้ว่าทำไมถึงจงใจให้เป็นแบบนั้น

ปลายทางของวันนี้: ไฟทุกดวงบนบอร์ดวิ่งไล่กันตามจังหวะที่ทีมตั้งเอง และทุกครั้งที่กดปุ่มบนบอร์ด ตัวเลขบนจอเพิ่มขึ้นหนึ่ง

บทเรียน 1.1–1.3 เราเห็นไฟติดจากตัวอย่างที่เขียนมาให้ · ชุดบทเรียนนี้เราเป็นคนเขียนลูปที่สั่งมันเอง และรู้ว่าทุกคำสั่งในโมดูลนี้ทำอะไร

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

ปลายทางของชุดบทเรียนนี้ — แผงคุมไฟวิ่งที่คุยกับบอร์ดรู้เรื่อง

หน้าจอจาก BENTO Emulator ของเฉลยไฟวิ่งกับปุ่ม พร้อมตัวนับการกด

หน้าจอจริงจากการรันโค้ดเฉลยบน BENTO Emulator ที่ 800x480 เท่าจอของทั้งสองบอร์ด — ไม่ใช่ภาพวาด ไม่ใช่ mock-up และไม่ใช่ภาพถ่ายจากบอร์ด · โค้ดคือ s03_led_button.py ของบทเรียน 2.3
  • บนซ้าย ไฟบนบอร์ดตามจำนวนที่ถามบอร์ดเอาเอง: Eva Kit ตอบ 3 ดวง · Dev Kit ตอบ 5 ดวง ดวงที่สว่างบนจอคือดวงที่โค้ดสั่ง — จอสะท้อนคำสั่ง ไม่ใช่ค่าที่ขาอ่านกลับมา
  • บนขวา ปุ่มกับตัวนับการกด — ชื่อ USER Button 1 คือชื่อที่เฟิร์มแวร์ตอบ ไม่ใช่ป้ายที่พิมพ์บนแผ่นวงจร และบอร์ดสองรุ่นพิมพ์ต่างกัน (สไลด์ "กับดักชื่อ" เล่าว่าทำไม)
  • ล่าง ปุ่มสัมผัส "เดินไฟวิ่ง" กับ "หยุดไฟวิ่ง" และเวลาที่เหลือของรอบ 30 วินาที

ผลลัพธ์จริงอยู่ที่ LED บนตัวบอร์ด ที่ไล่ดวงอยู่ จอบอกแค่ว่าโค้ดสั่งอะไร และนับการกดได้กี่ครั้ง

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

สองชุดบทเรียนที่ผ่านมาเราสะสมอะไรไว้บ้าง

บทเรียน 1.1–1.3 เป็นบทเรียนเดินชมของ เราไล่ดูว่าไลบรารีชุดนี้มีอะไรบ้าง และหนึ่งในนั้นคือ 11_lights_and_a_button.py ที่ สั่งหลอดไฟจริงและอ่านปุ่มจริงไปแล้ว — ไฟไล่ทีละดวง หรี่ความสว่างด้วย hold() แล้วรอปุ่ม

บทเรียน 1.4–1.6 เราส่งค่าจากบอร์ดออกไปถึงเครื่องอื่น และรับคำสั่งกลับมาจุดไฟบนบอร์ดนี้ ระหว่างทางได้อ่านเลข IP ของมัน แยก "ต่อติดตอนนั้น" ออกจาก "ยังต่ออยู่ตอนนี้" แล้วเขียนกฎของเราเองว่าลิงก์แบบไหนถึงเรียกว่าใช้งานได้

พูดให้ตรง: วันนี้ไม่ใช่ครั้งแรกที่เราสั่งไฟ ครั้งแรกอยู่ในบทเรียน 1.1–1.3 แล้ว สิ่งที่ต่างคือบทเรียนนั้นเรา รันของที่เขียนมาให้ เพื่อดูว่าบอร์ดทำอะไรได้บ้าง วันนี้เราเปิดฝาโมดูล gpio ออกดูทั้งใบ แล้วเขียนลูปควบคุมของทีมเราเอง

สามอย่างที่บทเรียน 1.1–1.3 ให้เห็นผ่าน ๆ แล้ววันนี้จะรู้จริง: เมื่อไร hold() ถึงจำเป็น · ทำไม duty() ถึงเชื่อไม่ได้ · และ ทำไม ปุ่มที่กดครั้งเดียวถึงนับได้หลายครั้ง

เครื่องมือเดิมที่ยังใช้ต่อ: lcd.print() กับ markup, time.sleep_ms(), ลูป for และหน้า Playground

บทเรียน 1.1–1.6 · เดินชมของ รันตัวอย่างที่เขียนมาให้ เห็นไฟติดแล้ว แต่ยังไม่รู้กลไก บทเรียน 2.1–2.3 · ผู้ควบคุม ลูปของเราสั่งของจริง อ่าน → ตัดสิน → สั่ง บทเรียน 2.4–5.3 · โครงเดิม เปลี่ยนแค่ว่าอ่านอะไร และสั่งอะไร วันนี้คือจุดที่บทบาทเปลี่ยน สองชุดบทเรียนแรกสะสม "รู้ว่ามีอะไร" · วันนี้เพิ่ม "สั่งมันได้"

ทุกบทเรียนต่อจากนี้จะมีลูปหลักเป็นแกน — วันนี้คือลูปแรกของเรา

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

โมดูล gpio — สี่บรรทัดที่คุมของจริงได้

import gpio
gpio.led(0).on()        # ติดค้าง
gpio.led(0).off()       # ดับ
gpio.led(1).toggle()    # สลับสถานะจากเดิม
gpio.led(2).value(1)    # สั่งด้วยตัวเลข 1 = ติด, 0 = ดับ
gpio.led(2).brightness(50)   # หรี่เป็นเปอร์เซ็นต์ของกำลังไฟ 0-100
pressed = gpio.button(0).is_pressed()   # True ตอนที่นิ้วกดอยู่

gpio.led(n) ไม่ต้อง init ไม่ต้องบอกขา ไม่ต้องตั้งโหมด — เฟิร์มแวร์จัดการให้ตอนเรียกครั้งแรก · .name() ขอชื่อของหลอดตามที่เฟิร์มแวร์รู้จัก — เดี๋ยวจะเจอว่าชื่อนี้มีเรื่อง

n ใช้ได้ตั้งแต่ 0 ถึง gpio.num_leds() - 1 — Eva Kit 3 ดวง (0-2) · Dev Kit 5 ดวง (0-4) นอกช่วงได้ ValueError ทันที · ดวงไหนสีอะไร และดวงไหนมองเห็นบนบอร์ดที่ประกอบแล้ว ให้สั่ง on() ทีละดวงแล้วดูที่บอร์ดของทีม (ในบันทึกการเรียน) — บน Dev Kit หลอด LED1/LED2 อยู่บนโมดูล ดวง RGB คือดวง 2-4

สี่คำสั่งนี้ทำอะไรกับสถานะของหลอดหนึ่งดวง .on() บังคับเป็น 1 .off() บังคับเป็น 0 .toggle() กลับด้านจากเดิม .value(1) / .value(0) สั่งด้วยตัวเลขที่คำนวณได้ ผลปลายทางคือหลอดจริง
ภาพเคลื่อนไหวสัญญาณ PWM ที่ duty cycle เปลี่ยนจากน้อยไปมากภาพเคลื่อนไหว PWM ที่ค่าเฉลี่ยของแรงดันเปลี่ยนตามสัดส่วนเวลาติด
.brightness(pct) ไม่ได้ลดแรงดันที่ขา แต่ สลับติด-ดับเร็วกว่าที่ตาจับได้ แล้วปล่อยให้ตาเห็นเป็นค่าเฉลี่ย ภาพนิ่งอธิบายเรื่องนี้ไม่ได้ ต้องดูสองภาพซ้ายตอนมันเคลื่อน — ซ้าย สัดส่วนเวลาติดต่อหนึ่งคาบคือสิ่งเดียวที่เปลี่ยน · ขวา เส้นค่าเฉลี่ยไต่ตามความกว้างพัลส์ "ความสว่างที่ตาเห็น" คือค่าเฉลี่ย ไม่ใช่ค่าที่ขาส่งออกจริง (ภาพ: "PWM duty cycle with label" สาธารณสมบัติ · "PWM gemiddelde waarde" โดย Jef daems CC0 1.0 — Wikimedia Commons)

on()/off() เมื่อรู้ว่าอยากได้สถานะไหน · toggle() เมื่อแค่อยากกลับด้าน · brightness(pct) ค่ากลาง 1-99 เลือกทางตามขาของหลอด: ดวงที่มีเส้น PWM ของฮาร์ดแวร์ (Eva 0-2 · Dev Kit ดวง RGB 2-4) ค้างระดับไว้ ไม่บล็อก · ดวงอื่นได้พัลส์ราว 12 ms แล้วจบด้วยหลอดดับ · ค้างได้ทุกดวงด้วย hold(pct, ms) ซึ่งบล็อกจนครบแล้วจบด้วยหลอดดับ — แต่ถ้าเพิ่งสั่ง brightness() ค่ากลางบนดวง PWM ต้องเรียก off() คั่นก่อน ไม่งั้นขายังถูก PWM ถืออยู่ hold() ไม่เห็นผลและหลอดไม่ดับตอนจบ (03_led_brightness.py ทำแบบนั้น) · brightness(0)/100 ไม่บล็อก และ 100 ทิ้งหลอดติดค้าง · มองหลอดจริงเป็นคนตัดสิน

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

ทั้งโมดูล gpio มีอยู่เท่านี้ (1/2) — สิบแปดชื่อ ไม่มีมากกว่านี้

สไลด์นี้ไม่ได้ให้ท่อง แต่ให้ รู้ว่าอะไรมีอยู่ จะได้ไม่ไปเขียนของที่ไม่มี แล้วนั่งงงว่าทำไม error

ระดับโมดูล — ห้าฟังก์ชัน สองชนิด (import gpio แล้วเรียกได้เลย)

ชื่อ คืนอะไร ใช้ตอนไหน
gpio.board_info() dict ห้าช่อง: name leds buttons led_names btn_names เปิดโปรแกรมมา อยากรู้ว่าบอร์ดนี้มีอะไร
gpio.num_leds() int — Eva Kit ได้ 3 · Dev Kit ได้ 5 เขียน range() ให้ย้ายบอร์ดแล้วยังถูก — คอร์สนี้ย้ายจริง
gpio.num_buttons() int — ได้ 1 เสมอ เพราะเฟิร์มแวร์ตรึงไว้ที่ 1 (ทั้งสองบอร์ด) เช็กก่อนวนอ่านปุ่ม
gpio.led(n) อ็อบเจกต์ LED · n นอกช่วง 0..num_leds()-1 โยน ValueError ทุกครั้งที่จะสั่งไฟ
gpio.button(n) อ็อบเจกต์ Button · n ต้องเป็น 0 เท่านั้น ทุกครั้งที่จะอ่านปุ่ม
gpio.LED · gpio.Button ชนิด ไม่ใช่ฟังก์ชัน เรียกสร้างเองไม่ได้ isinstance(x, gpio.LED) ตอนเขียนฟังก์ชันที่รับได้ทั้งไฟและปุ่ม

เมธอดของ LED — แปดตัว · เมธอดของ Button — สามตัว

LED ทำอะไร กับดัก
.on() .off() บังคับเป็น 1 / 0 —
.toggle() กลับด้านจากเดิม ไม่ไปแก้ตัวเลขของ duty() พอ on() แล้ว toggle() หลอดดับแต่ duty() ยังตอบ 100
.value() อ่านระดับขาจริงกลับมา หลัง hold() ได้ 0 เสมอ (และหลัง brightness() ค่ากลางบนดวงที่ไม่มีเส้น PWM) — ตอบระดับขา ไม่ตอบความสว่าง
.value(n) ตัวเดียวกันแต่ใส่อาร์กิวเมนต์ = สั่ง 1/0 ใช้ตอนค่ามาจากการคำนวณ
.name() ชื่อในตารางเฟิร์มแวร์ Eva: ดวงที่ 2 ตอบ "RGB_RED" แต่ติดเป็นน้ำเงิน · Dev Kit: มี RGB_RED RGB_BLUE RGB_GREEN ครบ — หาดวงจากชื่อได้
.brightness(pct) 1-99: ดวงที่มีเส้น PWM ของฮาร์ดแวร์ (Eva 0-2 · Dev Kit 2-4) ค้างระดับไว้ ไม่บล็อก · ดวงอื่นได้พัลส์ 12 ms หนึ่งชุด (200 µs × 60 รอบ) แล้วจบด้วยหลอดดับ 0 กับ 100 เป็นทางลัด สั่งขาตรง ๆ แล้วคืนทันที — 100 จบด้วยหลอดติดค้าง ไม่ได้ดับ · จะ hold() ต่อจาก brightness() ค่ากลางบนดวง PWM ให้ off() คั่นก่อน (03_led_brightness.py ทำแบบนั้น)
.hold(pct, ms) ย้ำพัลส์นั้นจนครบ ms · ms ไม่ใส่ได้ ปริยาย 500 · ใช้ได้ทุกดวง จบด้วยหลอดดับ บล็อกจนครบ ระหว่างนั้นปุ่มไม่ถูกอ่าน · 100 ก็ค้างติดเหมือนกัน · ไม่ปลดเส้น PWM ให้ — ถ้าเพิ่ง brightness() ค่ากลางบนดวง PWM แล้วไม่ off() คั่น hold() จะไม่เห็นผลและหลอดไม่ดับตอนจบ
.duty() เปอร์เซ็นต์ที่ เราสั่งไปครั้งล่าสุด ไม่ได้ไปวัดหลอด · on() off() value(n) brightness() hold() เขียนตัวเลขนี้ทั้งหมด แต่ toggle() ไม่เขียน มันจึงเก่าได้
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก AIoT in Action (AIC มหาวิทยาลัยบูรพา) · CC BY-NC 4.0

ทั้งโมดูล gpio มีอยู่เท่านี้ (2/2) — ปุ่ม และของที่ไม่มีให้เรียก

เมธอดของ Button — สามตัว

Button ทำอะไร กับดัก
.is_pressed() True ตอนกดอยู่ — แปลความหมายให้แล้ว ใช้ตัวนี้เป็นหลัก
.value() ระดับไฟฟ้าดิบ กด = 0 if btn.value(): ทำงานกลับด้านทั้งหมด
.name() ชื่อในตารางเฟิร์มแวร์ ตอบ "USER Button 1" ทั้งสองบอร์ด — จงใจไม่ใช้ป้ายบนแผ่นวงจร (สไลด์กับดักชื่อเล่าว่าทำไม)

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

ชื่อ สถานะจริง
machine.PWM · machine.ADC · machine.SPI พอร์ตนี้ไม่ได้เขียนไว้เลย ไม่มีไฟล์ ไม่มีชื่อ
machine.Timer มีโค้ดอยู่ในพอร์ต แต่เฟิร์มแวร์ชุดนี้ไม่ได้เปิดสวิตช์คอมไพล์ให้ (เครื่องเกมของคอร์สอื่นเปิด) ผลที่เราเจอจึงเหมือนกันคือเรียกไม่ได้
machine.I2C อันนี้มี แต่ยังไม่ใช่เรื่องของชุดบทเรียนนี้

ไม่มี machine.PWM ให้เรียกจาก Python ก็จริง แต่ฝั่งเฟิร์มแวร์ brightness() ต่อดวง RGB เข้ากับ TCPWM ในตัวชิปให้แล้ว (ตาราง s_led_pwm_routes ใน modgpio.c) ดวงที่ไม่มีเส้นนั้นเท่านั้นที่ต้องกะพริบขาเองด้วยซอฟต์แวร์ — ดังนั้นการหรี่ไฟทำผ่าน gpio ไม่ใช่ผ่าน machine

เห็นคำตอบใน Stack Overflow ที่ขึ้นต้นด้วย machine.PWM(...) เมื่อไร ให้รู้ทันทีว่าคำตอบนั้นไม่ใช่ของบอร์ดเรา

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

ให้บอร์ดตอบเองว่ามีอะไรอยู่

info = gpio.board_info()
...
n = gpio.num_leds()        # 3 หรือ 5 แล้วแต่บอร์ด
...
for i in range(n):
    gpio.led(i).off()
...
print("board_info() =", info)
# Eva Kit:  {'name': 'PSoC Edge Eval Kit', 'leds': 3, 'buttons': 1,
#            'led_names': ['LED1', 'LED2', 'RGB_RED'], 'btn_names': ['USER Button 1']}
# Dev Kit:  {'name': 'PSoC Edge AI Dev Kit', 'leds': 5, 'buttons': 1,
#            'led_names': ['LED1', 'LED2', 'RGB_RED', 'RGB_BLUE', 'RGB_GREEN'], 'btn_names': ['USER Button 1']}

นิสัยที่ดีของงาน embedded คือ ถามอุปกรณ์ ไม่ใช่เดาจากความจำ — for i in range(3) จะพังเงียบ ๆ เมื่อย้ายไปบอร์ดที่มีห้าดวง (ซึ่งคือ Dev Kit ในห้องนี้) แต่ for i in range(n) ย้ายไปแล้วยังถูก (gpio.num_buttons() ตอบ 1 ทั้งสองบอร์ด) · สังเกตชื่อในรายการให้ดี 'RGB_RED' บน Eva กับ 'USER Button 1' บนทั้งสองบอร์ด — สองชื่อนี้ไม่ตรงกับสิ่งที่ตาเห็นหรือป้ายที่พิมพ์ไว้ คนละเหตุผลกัน

ผังด้านบนของบอร์ด Eva Kit พร้อมป้ายชี้ตำแหน่งจอ ปุ่ม LED ลูกบิด และเซนเซอร์

ภาพ: KIT_PSE84_EVAL PSOC™ Edge E84 Evaluation Kit guide, Infineon 002-39007 Rev.*B, รูปที่ 2 (หน้า 9) — ใช้เพื่อการเรียนการสอน · นี่คือ Eva Kit: หลอด D3/D4/D5 และปุ่มผู้ใช้ (ป้ายบนแผ่นวงจร SW2) คือของที่ board_info() รายงานเป็น 3 ดวงกับ 1 ปุ่ม — Dev Kit รายงาน 5 ดวงกับ 1 ปุ่ม และคำตอบที่บอร์ดพูดเองคือคำตอบที่ย้ายบอร์ดแล้วยังถูก · ภาพหน้าจอของ 01_board_info.py ถอดออกชั่วคราว ไฟล์ถูกลดจำนวน widget ลงเมื่อ 14 ส.ค. รอถ่ายใหม่

เขียนโค้ดให้ถามบอร์ด แล้วโค้ดจะรอดตอนย้ายบอร์ด

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

กับดักชื่อ — เรื่องที่ต้องรู้ก่อนเสียเวลาทั้งบ่าย

สิ่งที่โค้ดบอก ความจริงบนบอร์ด ต้องทำอย่างไร
gpio.led(2).name() คืน "RGB_RED" Eva Kit: หลอดที่ติดจริงเป็นสี น้ำเงิน (ชื่อตกทอดจากตารางร่วม) · Dev Kit: ตารางเฟิร์มแวร์ (modgpio.c) บอกว่าดวง 2 คือแดง และมี RGB_BLUE RGB_GREEN เป็นดวง 3-4 — ยังไม่มีใครยืนยันด้วยตาบนบอร์ดที่ประกอบแล้ว ทีมเป็นคนแรก เชื่อสายตา — สั่ง on() ทีละดวงแล้วดู ไม่ต้องเชื่อชื่อ
gpio.button(0).name() คืน "USER Button 1" ไม่ใช่ป้ายบนแผ่นวงจร — Eva Kit พิมพ์ว่า SW2 · Dev Kit: บนฐาน QWA309 สวิตช์ที่พิมพ์ว่า SW หลายตัวเป็นสวิตช์ตัดไฟเลี้ยง (ในผังวงจร SW2/SW3/SW4/SW11 คือสวิตช์ไฟของกล้อง/mikroBUS/ADU/RGB) ห้ามโยกสวิตช์ใดบนฐานที่บทเรียนไม่ได้สั่ง ตั้งใจ ให้เป็นแบบนี้ — เรียกปุ่มด้วยชื่อจาก .name() เสมอ ห้ามบอกเพื่อนว่า "กด SW2"
บนบอร์ดมีปุ่มอื่นอีก (Eva: SW4 · Dev Kit: ปุ่มกดสองปุ่มของฐาน ใช้ผ่านโมดูล buttons เท่านั้น — ชื่อบนแผ่นรุ่นผลิต SW4/SW5) gpio.button(1) โยน ValueError ทั้งสองบอร์ด ชุดบทเรียนนี้ใช้ปุ่มเดียวคือ gpio.button(0)
gpio.led(2).value() คืน 0 ทั้งที่หลอดเพิ่งสว่าง อ่านกลับได้จริง แต่ hold() (และ brightness() ค่ากลางบนดวงที่ไม่มี PWM) จบด้วยขาต่ำ จำสถานะไว้ในตัวแปรของเราเอง
duty() ตอบ 100 ทั้งที่หลอดดับอยู่ เพิ่งเรียก toggle() ซึ่ง ไม่ไปแก้ตัวเลขของ duty() duty() คือ "สั่งอะไรไปล่าสุด" ไม่ใช่ "หลอดเป็นอย่างไรตอนนี้"

Eva Kit: led(0) แดง · led(1) เขียว · led(2) น้ำเงิน — จำจากสายตา ไม่ใช่จากชื่อ · Dev Kit: ดวง RGB คือ 2-4 ตามชื่อใน led_names ส่วน LED1/LED2 อยู่บนโมดูล — ยืนยันด้วยสายตาบนบอร์ดของทีมเหมือนกัน

สองชื่อนี้หลอกด้วยเหตุผลคนละแบบ RGB_RED มาจากตารางร่วมของเฟิร์มแวร์ที่บอร์ดหลายรุ่นใช้ด้วยกัน ดวงที่สามของ Eva จึงได้ชื่อของดวงแดงบนบอร์ดอื่น · USER Button 1 ตรงกันข้าม มันถูกเลือกโดยตั้งใจให้ไม่ตรงกับป้ายบนแผ่นวงจร เพราะบน Dev Kit คำว่า "SW2" ชี้ไปที่สวิตช์ตัดไฟบนฐาน — ชื่อที่มีไว้ให้คนหาปุ่มด้วยนิ้ว ต้องไม่พาไปกดของผิดชิ้น (เหตุผลเขียนไว้ใน modgpio.c ตรงตารางปุ่ม) · สิ่งที่วิศวกรทำคือ ทดลองหนึ่งครั้งแล้วจดไว้ ไม่ใช่เถียงกับเอกสาร

ชื่อที่โค้ดตอบ ของจริงที่ตาเห็น led(2).name() = "RGB_RED" Eva: หลอด D5 สว่างเป็นสีน้ำเงิน ไม่ตรงกัน (ชื่อตกทอด) button(0).name() = "USER Button 1" Eva พิมพ์ SW2 · Dev Kit: SW2 บนฐานคือสวิตช์ไฟ ไม่ตรงกัน (ตั้งใจ) ทางแก้: ดูสีด้วยตาครั้งเดียว แล้วตั้ง LED_BLUE = ดัชนีที่เห็นว่าน้ำเงิน · ปุ่มเรียกด้วย btn.name()

อย่าให้ชื่อตัวแปรของทีมเราสืบทอดความสับสนต่อ — หาดัชนีดวงน้ำเงินครั้งเดียว (ตาเห็น หรือชื่อ RGB_BLUE ถ้าบอร์ดมี) แล้วตั้งชื่อว่า LED_BLUE ไปเลย · ส่วนปุ่ม ให้ป้ายบนจอพิมพ์ btn.name() ออกมาตรง ๆ คนก้มดูบอร์ดกับคนมองโค้ดจะได้เรียกปุ่มเดียวกันด้วยชื่อเดียวกัน

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