How To Blink LEDs with ESP32
ตัวอย่างสาธิตการเขียนโค้ดไมโครไพธอนสำหรับ ESP32 เพื่อทำให้ LED กระพริบ โดยใช้วิธีที่แตกต่างกันไป
เนื้อหาในส่วนนี้ มีโจทย์ง่าย ๆ เพื่อทดลองเขียนโค้ดที่จะทำให้ LED เช่น หนึ่งดวงที่อยู่บนบอร์ด ESP32 (หรือจะเลือกใช้ขา GPIO ขาใดขาหนึ่งแล้วต่อวงจร LED เพิ่มเองก็ได้) สามารถกระพริบได้ (LED Blink)
คำถามคือ แล้วมีวิธีการเขียนโค้ดอย่างไรได้บ้างโดยใช้ภาษาไมโครรไพธอน ลองมาดูตัวอย่างและศึกษาการทำงานของโค้ด อาจมองว่าเป็นการทบทวนการใช้ภาษาไพธอน สำหรับผู้ที่เคยเขียนโค้ดในภาษานี้มาบ้างแล้ว แต่อาจจะยังไม่เคยใช้งานไพธอนแบบสัมผัสฮาร์ดแวร์ (Python for Microcontrollers / Physical Computing)
LED Blink: Demo 1
จากเนื้อหาที่ผ่านมา เราได้เรียนรู้การใช้คลาส Pin
จากไลบรารีหรือโมดูล machine
เพื่อใช้งานขา GPIO ของบอร์ด ESP32
ตัวอย่างนี้ได้เลือกใช้ขา GPIO-5 และกำหนดทิศทางให้เป็นเอาต์พุต ฟังก์ชัน value()
ที่ของอ็อปเจกต์ที่สร้างมาจากคลาส Pin
มีไว้สำหรับอ่านหรือเขียนค่าลอจิก (0 หรือ 1)
เมื่อกำหนดสถานะของขา GPIO ได้แล้ว จะต้องมีการหน่วงเวลา เราก็ใช้คำสั่งจากโมดูล time
เช่น time.sleep()
, time.sleep_ms()
หรือ time.sleep_us()
ก็ได้ ซึ่งแตกต่างกันตรงที่หน่วยของเวลาสำหรับอาร์กิวเมนต์ เช่น วินาที มิลลิวินาที และไมโครวินาที ตามลำดับ
คำสั่งในฟังก์ชัน main_loop()
จะทำให้ LED กระพริบซ้ำไปเรื่อย ๆ โดยเว้นระยะเวลาในการสลับสถานะทุก ๆ 100 วินาที ถ้าทดลองรันโค้ดนี้ผ่าน REPL Shell ใน Thonny IDE และต้องการจะหยุดการทำงาน ก็ให้กดคีย์ Ctrl+C
ข้อสังเกต: ในตัวอย่างนี้ LED บนบอร์ด ESP32 ที่ได้นำมาใช้งาน จะสว่าง (ON) ก็ต่อเมื่อเขียนสถานะลอจิกเป็น 0 (False
) และจะดับ (OFF) เมื่อเป็น 1 (True
)
LED Blink: Demo 2
จากตัวอย่างแรก ตัวอย่างที่สองนี้ มีการแก้ไขจากเดิมเล็กน้อย โดยเปลี่ยนจากการใช้คำสั่ง time.sleep_ms()
มาเป็นการอ่านค่าเวลาของระบบ time.tick_ms()
ในหน่วยเป็นมิลลิวินาที แล้วคอยตรวจสอบซ้ำดูว่า เวลาผ่านไป 100 มิลลิวินาที แล้วหรือไม่ ถ้าเป็นเช่นนั้น ก็ให้ทำการสลับสถานะของ LED หนึ่งครั้ง แล้วอัปเดตเวลาการเปลี่ยนแปลงครั้งล่าสุด
คำสั่ง time.ticks_diff()
ใช้สำหรับการหาผลต่างของตัวเลขเวลาที่ได้บันทึกไว้ในตัวแปร t2
และ t1
LED Blink: Demo 3
ตัวอย่างที่สาม เป็นการสาธิตการใช้งาน Python Generator Function โดยสร้างฟังก์ชันชื่อ next_state_generator()
ภายในมีการใช้คำสั่ง yield
ดังนั้นจึงนำไปใช้เป็น Generator Iterator ได้ เมื่อเรียกฟังก์ชันนี้ในแต่ละครั้ง จะได้สถานะลอจิก (0 หรือ 1) ถัดไป เช่น สลับ 0 และ 1 ไปเรื่อย ๆ และมีการหน่วงเวลาด้วยคำสั่ง time.sleep_ms()
ด้วยเช่นกัน
LED Blink: Demo 4
ตัวอย่างถัดไปสาธิตการใช้ไทม์เมอร์ หรือตัวนับเวลา Timer
ของโมดูล machine
ซึ่งมีสองประเภทให้ใช้งานคือ Hardware Timer และ Software Timer แต่เนื่องจาก Hardware Timer สำหรับ ESP32 มีจำนวนจำกัด
โดยทั่วไปเราก็จะใช้ Software Timer (ระบุหมายเลขเป็น -1) เหมาะสำหรับกรณีที่มีความถี่หรืออัตราการนับจังหวะไม่สูงมากนัก แต่ถ้าจะใช้กับความถี่สูง ก็ให้ใช้ Hardware Timer (ระบุหมายเลขเป็น 0 – 3) สำหรับ ESP32
ในตัวอย่างนี้ เราใช้งาน Software Timer และกำหนดคาบในการทำงาน (Period) เท่ากับ 100 มิลลิวินาที และเลือกโหมดแบบทำซ้ำ (Timer.PERIODIC
) แต่ถ้าเลือกเป็น Timer.ONE_SHOT
จะทำเพียงครั้งเดียวแล้วจบการทำงานของไทม์เมอร์
นอกจากนั้นจะต้องกำหนดฟังก์ชันที่เรียกว่า Callback Function ซึ่งจะถูกเรียกให้ทำงานเมื่อเวลาผ่านไปครบหนึ่งคาบในแต่ละครั้ง ในตัวอย่างนี้ได้ระบุฟังก์ชันเป็นแบบ lambda
ซึ่งจะทำหน้าที่สลับสถานะของ LED
ข้อสังเกต: ในตัวอย่างนี้ ขณะที่ Timer กำลังทำงานเป็นอิสระ การทำงานของลูปภายในฟังก์ชัน main_loop()
เป็นเพียงการรอให้จบการทำงานของโปรแกรม โดยการกดคีย์ Ctrl+C
LED Blink: Demo 5
ตัวอย่างถัดไปสาธิตการสร้างสัญญาณแบบ PWM (Pulse Width Modulation) ให้มีความถี่ต่ำ (5 Hz หรือมีคาบเท่ากับ 200 msec) สำหรับนำไปใช้กับ LED และกำหนดค่า Duty Cycle ให้เท่ากับ 50% โดยระบุค่าเป็น 511 ซึ่งเป็นค่ากลางในช่วง 0..1023 ดังนั้นจึงได้สัญญาณเอาต์พุตแบบมีคาบ ในช่วงที่เป็น 0 (Low) และ 1 (High) มีความกว้างช่วงละ 100 มิลลิวินาที
LED Blink: Demo 6
ตัวอย่างถัดไปสาธิตการสร้าง 'เธรด' (Thread) ที่ทำงานได้อิสระจาก Main Thread โดยใช้คำสั่งของโมดูลหรือไลบรารี _thread
เธรดที่ถูกสร้างขึ้นมานั้น ทำหน้าที่คอยสลับสถานะลอจิกของ LED และเว้นระยะเวลา 100 มิลลิวินาที แล้วทำขั้นตอนซ้ำ
ในตัวอย่างนี้ ฟังก์ชัน led_thread_func()
จะถูกกำหนดให้เป็น Thread entry function (ฟังก์ชันที่ทำงานโดยเธรดที่ถูกสร้างขึ้นใหม่) โดยใช้คำสั่ง _thread.start_new_thread()
ตัวแปรภายนอก finished
แบบบูลีน (Boolean) จะถูกใช้ในการตรวจสอบเงื่อนไขการออกจากลูปการทำงานของเธรด ซึ่งจะจบการทำงานเมื่อมีการกดคีย์ Ctrl+C ใน REPL Shell
LED Blink: Demo 7
ตัวอย่างถัดไปเป็นการใช้เธรด เหมือนตัวอย่างแล้ว แต่สาธิตการสร้างเธรด 2 ชุด (หมายเลข id
คือ 0 และ 1) โดยให้แต่ละเธรดทำหน้าที่กำหนดสถานะลอจิกตาม id
ของตัวเอง กล่าวคือ ถ้า id
เป็น 0 ให้เขียนสถานะเป็น 0 แต่ถ้า id
เป็น 1 ให้เขียนสถานะเป็น 1 แล้วเว้นระยะเวลา 100 มิลลิวินาที
แต่เนื่องจากว่า เธรดทั้งสองชุดใช้ฟังก์ชันเดียวกันคือ led_thread_func()
ดังนั้นจึงใช้ id
เป็นตัวระบุการทำงานของแต่ละเธรด
ทั้งสองเธรดนั้น ต้องเข้าถึงอ็อปเจกต์ที่เป็น Pin
สำหรับ LED ร่วมกัน ดังนั้นจึงมีการสร้างและใช้ lock
(Mutex Lock) ในการป้องกันและจัดลำดับการเข้าถึงทรัพยากรที่ใช้ร่วมกัน
LED Blink: Demo 8
ตัวอย่างนี้สาธิตการใช้วงจร RMT (และใช้ได้เฉพาะกับ ESP32 เท่านั้น) เหมาะสำหรับสร้างสัญญาณพัลส์เป็นชุด (Pulse Train Generation) เช่น การสร้างสัญญาณควบคุมสำหรับรีโมทแสงอินฟราเรด (Infrared Remote Control) หรือใช้ควบคุมหรือกำหนดค่าสีให้กับโมดูล WS2812B Neopixel RGB LED
เราสามารถใช้ RMT มาสร้างสัญญาณพัลส์ เพื่อทำให้ LED กระพริบได้เช่นกัน (แต่มีข้อจำกัดเรื่องช่วงความถี่ที่สามารถใช้งานได้) วงจร RMT ภายใน ESP32 รับสัญญาณ Clock ที่มีความถี่ 80 MHz ไปผ่านวงจรหารความถี่ ขนาด 8 บิต (clock_div
) ซึ่งเลือกค่าได้ในช่วง 0..255 แล้วนำไปสร้างสัญญาณพัลส์
ความกว้างของพัลส์จะถูกกำหนดโดยค่าที่เป็นเลขจำนวนเต็มขนาด 15 บิต (0..32,767) เช่น ถ้าเลือกตัวหารความถี่เป็น 250 และเลือกตัวเลข 32,000 สำหรับความกว้างของพัลส์ (Bit Width) เราจะได้ความกว้างเท่ากับ (1/80 MHz) * 250 * 32000 = 100 msec
LED Blink: Demo 9
ตัวอย่างถัดไปเป็นการสร้างฟังก์ชันแบบ Coroutine ซึ่งมีความคล้ายกับ Python Generator ที่ภายในมีคำสั่ง yield
และเราสามารถนำมาใช้ร่วมกับคำสั่ง async
และ await
ได้เช่นกัน
LED Blink: Demo 10
ตัวอย่างนี้สาธิตการใช้คำสั่งจากไลบรารี uasyncio
และลองสร้างฟังก์ชันที่มีชื่อว่า led_toggle()
ให้ทำหน้าที่เป็น Coroutine (โดยการเขียนคำว่า async
ไว้นำหน้าเมื่อประกาศและสร้างฟังก์ชันดังกล่าว) และจะต้องมีคำสั่ง await
อยู่ภายในฟังก์ชัน
ในกรณีนี้ การทำงานของฟังก์ชัน เป็นการรอให้เวลาผ่านไป โดยใช้คำสั่ง asyncio.sleep_ms()
เช่น 100 มิลลิวินาที ก่อนที่จะสลับสถานะเอาต์พุตของ LED
ฟังก์ชันดังกล่าวจะถูกนำไปใช้ในการสร้างทาสก์ (Task) ที่ทำงานต่อเนื่องไปเรื่อย ๆ และถูกจัดการโดย Event Loop ของ asyncio
LED Blink: Demo 11
ตัวอย่างโค้ดถัดไปสาธิตการใช้ SoftSPI (ไม่ใช่ Hardware SPI หรือ HSPI ของ ESP32) ในการเลื่อนบิตข้อมูลออกไปที่ขา GPIO ที่ได้เลือกมาเป็นขาสำหรับส่งข้อมูลขาออก MOSI ของบัส SPI
การส่งข้อมูลไบต์ มีจำนวน 2 ไบต์ ได้แก่ 0xff
(High Pulse) และ 0x00
(Low Pulse) ตามลำดับ วนซ้ำไปเรื่อย ๆ และถ้าใช้ความถี่ของ SCK ต่ำ และใช้ขา MOSI ต่อกับวงจร LED ก็จะเห็นแสงไฟ LED กระพริบ
ในตัวอย่างนี้ได้เลือกขา GPIO-5 เป็นขาสำหรับสัญญาณ MOSI ซึ่งนำไปต่อกับวงจร LED และใช้ขา GPIO-0 สำหรับ SCK และ GPIO-4 สำหรับ MISO ซึ่งในความเป็นจริงไม่ได้ใช้งานขาทั้งสองกับวงจรใด ๆ ภายนอก
กล่าวสุรป
โดยสรุป จากตัวอย่างที่ได้นำเสนอไปนั้น เราได้เห็นรูปแบบการเขียนโค้ดภาษาไมโครไพธอนสำหรับ ESP32 โดยใช้เทคนิคที่แตกต่างกัน เพื่อวัตถุประสงค์เดียวกันคือ ทำให้ LED กระพริบได้
เผยแพร่ภายใต้ลิขสิทธิ์ Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
Last updated