ตัวอย่างการเขียนโค้ด MicroPython สำหรับบอร์ด Espressif ESP32 (ตอนที่ 1)#

Keywords: Python 3, MicroPython, Espressif SoCs, ESP32 / ESP32-S3, Thonny IDE, Arduino Lab for MicroPython, Wokwi


การทดลองโค้ด MicroPython สำหรับ ESP32#

บทความนี้ซึ่งแบ่งออกเป็นหลายตอน นำเสนอตัวอย่างการเขียนโค้ด MicroPython สำหรับไมโครคอนโทรลเลอร์ ESP32 เพื่อแสดงให้เห็นรูปแบบการเขียนโค้ดสำหรับ Embedded Systems Programming และการทำงานของไมโครคอนโทรลเลอร์ ESP32 และได้มีการทดลองใช้กับบอร์ด ESP32 / ESP32-S3 ร่วมกับ MicroPython Firmware v1.26 มีการใช้ซอฟต์แวร์ดังต่อไปนี้ ในการเขียนและรันโค้ด

ในบทความนี้ มีตัวอย่างการเขียนโค้ด เพื่อใช้งาน GPIO (ดิจิทัล) ของ ESP32 เบื้องต้น มีการนำเสนอและเปรียบเทียบรูปแบบการเขียนโค้ด เพื่อทำให้เกิดการเปลี่ยนสถานะลอจิกสำหรับขาเอาต์พุต GPIO เช่น การทำงานแบบมัลติเธรด (Multi-threading) การใช้ฟังก์ชันของ uasyncio (Asynchronous Programming) การใช้วงจร Hardware Timer ของชิป ESP32 เพื่อให้เกิดการทำงานของฟังก์ชันซ้ำด้วยอัตราคงที่ เป็นต้น นอกจากนั้นแล้ว ยังมีตัวอย่างการวัดสัญญาณด้วย USB Logic Analyzer เพื่อศึกษาพฤติกรรมการทำงานของโค้ด MicroPython

แนะนำให้ผู้อ่านได้ศึกษาการใช้ซอฟต์แวร์ที่เกี่ยวข้องสำหรับ MicroPython-ESP32 จากบทความ "การเขียนโปรแกรม MicroPython สำหรับบอร์ด Espressif ESP32"

 


โค้ดตัวอย่างนี้สาธิตการทำให้ LED กระพริบ โดยเขียนค่าลอจิกสลับกันไป และเลือกใช้ขา GPIO 22 เป็นเอาต์พุต มีการหน่วงเวลาในแต่ละครั้ง โดยใช้คำสั่งtime.sleep()

ข้อสังเกต: การเลือกใช้หมายเลขขา GPIO อาจต้องปรับให้เหมาะสมกับขาสำหรับวงจร LED ของบอร์ด ESP32 ที่ใช้งานจริง

#-------------------------------------------------------------------------
# MicroPython-ESP32 Example: Different ways to blink an LED.
# This script shows how to toggle an LED and use the time.sleep() function
# to delay between consecutive LED toggles.
#-------------------------------------------------------------------------
from machine import Pin      # Import Pin class to control GPIO pins
from time import sleep       # Import sleep function for delays

# Create a Pin object for GPIO22 configured as an output
led = Pin(22, Pin.OUT)

# The main loop
while True:
    value = led.value()          # Read the current LED state
    led.value( not value )       # Toggle the output.
    print(f"LED: {led.value()}") # Print the current LED state 
    sleep(0.5)                   # Wait 0.5 seconds (blocking call)

รูป: ตัวอย่างการใช้งานซอฟต์แวร์ Arduino Lab for MicroPython ร่วมกับบอร์ด ESP32-S3

 


โค้ดตัวอย่างนี้แตกต่างจากตัวแรก โดยหลีกเลี่ยงการใช้ฟังก์ชันหน่วงเวลาในไลบรารี time เช่น sleep() หรือ sleep_ms() ซึ่งเป็นคำสั่งประเภท blocking call แต่ใช้วิธีตรวจสอบเวลาที่ผ่านไปจากการสลับสถานะลอจิกครั้งก่อน โดยใช้ฟังก์ชัน time.ticks_diff(...) เปรียบผลต่างของค่าเวลา (หน่วยมิลลิวินาที) หากมีค่ามากกว่าหรือเท่ากับระยะเวลาที่กำหนดไว้ ก็จะสลับสถานะลอจิกอีกครั้ง

#-------------------------------------------------------------------------
# This version avoids time.sleep() and uses time.ticks_ms() 
# with time.ticks_diff() for a non-blocking delay, so other code 
# can run in the same loop without being paused.
#-------------------------------------------------------------------------
from machine import Pin     # Import Pin class to control GPIO pins
import time                 # Import time functions for timing operations

led = Pin(22, Pin.OUT)      # Setup LED pin (GPIO 22) as output
interval_msec = 500         # Set the time interval for LED update
last_time = time.ticks_ms() # Store the last recorded time in msec

# The main loop
while True:
    # Get the current time in milliseconds
    now = time.ticks_ms()
    # Check if the time interval has passed since the last toggle
    if time.ticks_diff(now, last_time) >= interval_msec:
        last_time = now              # Update the last_time variable
        led.value(not led.value())   # Toggle the LED state
        print(f"LED: {led.value()}") # Print the current LED state

รูป: ตัวอย่างการใช้งานซอฟต์แวร์ Wokwi Simulator จำลองการทำงานของโค้ด MicroPython ด้วยบอร์ด ESP32 แบบเสมือนจริง

 


Example 3: Multi-threaded LED Blinking#

โค้ดตัวอย่างนี้สาธิตการใช้งานไลบรารี _thread เพื่อใช้เรียกหรือรันฟังก์ชันในรูปแบบของ "เธรด" (Thread) ซึ่งสามารถทำงานได้พร้อม ๆ กัน (Concurrency)

  • ฟังก์ชัน toggle_led_state(...) ทำหน้าที่สลับสถานะลอจิกและหน่วงเวลาตามที่กำหนด (ทุก ๆ 0.5 วินาที)
  • ฟังก์ชัน print_led_state(...) ตรวจสอบการเปลี่ยนแปลงค่าลอจิกของ GPIO ทุก ๆ 1 มิลลิวินาที และถ้ามีการเปลี่ยนแปลงก็จะแสดงค่าลอจิกล่าสุด
#-------------------------------------------------------------------------
# This script demonstrates using MicroPython's _thread module to blink
# an LED and print the LED state changes in two separate threads.
# It shows how to use _thread in MicroPython, but there is a better way
# to do this task (=> use asyncio or timer).
#-------------------------------------------------------------------------
from machine import Pin       # Import Pin class to control GPIO pins
from time import sleep_ms     # Import millisecond sleep function
import _thread                # Import MicroPython threading support
import time                   # Import MicroPython time library
import gc                     # Import MicroPython garbage collector

# Create a Pin object for GPIO 22 configured as an output
led = Pin(22, Pin.OUT)

# Global variable to store the last LED state
last_led_state = led.value()

# Define a function as the entry point for a thread
# This toggles the LED state every 500msec.
def toggle_led_state(pin):
    while True:
        pin.value(not pin.value())    # Toggle the LED state
        sleep_ms(500)                 # Wait for 500 msec

# Define a function as the entry point for a thread
# This checks the LED state change every 1 msec
# and prints only when the state changes.
def print_led_state(pin):
    global last_led_state
    while True:
        if last_led_state != pin.value():   # LED state change detected
            last_led_state = pin.value()    # Update the stored LED state
            print(f"LED: {last_led_state}") # Print the LED state
        sleep_ms(1)                         # Wait for 1 ms

# Start two new threads
_thread.start_new_thread(toggle_led_state, (led,))
_thread.start_new_thread(print_led_state, (led,))

# The main loop
while True:
    pass

รูป: ตัวอย่างการใช้ซอฟต์แวร์ Thonny IDE เพื่อรันโค้ดตัวอย่างโดยใช้บอร์ด ESP32

 


โค้ดตัวอย่างนี้สาธิตการใช้คำสั่งของไลบรารี uasyncio สำหรับ MicroPython โดยประกอบด้วยฟังก์ชันแบบ Coroutine สองฟังก์ชัน (หรือเรียกว่า Async Function) ได้แก่

  • ฟังก์ชัน toggle_led_state(...) ทำหน้าที่สลับสถานะลอจิกและหน่วงเวลาตามที่กำหนด (ทุก ๆ 0.5 วินาที)
  • ฟังก์ชัน print_led_state(...) ตรวจสอบการเปลี่ยนแปลงค่าลอจิกของ GPIO ทุก ๆ 1 มิลลิวินาที และถ้ามีการเปลี่ยนแปลงก็จะแสดงค่าลอจิกล่าสุด

ทั้งสองฟังก์ชันนี้ จะทำงานได้โดยสลับช่วงเวลาการทำงาน โดยมี Asyncio Scheduler ทำหน้าที่เป็นตัวกำหนดว่า จะให้ฟังก์ชันใดทำงานในช่วงเวลาใด

ในแต่ละฟังก์ชัน มีการใช้คำสั่ง await asyncio.sleep(...) เมื่อทำคำสั่งดังกล่าว การทำงานของฟังก์ชัน จะหยุดชั่วคราว และถูกสลับให้ฟังก์ชันอื่นได้ทำงานต่อไป

#-------------------------------------------------------------------------
# This code demonstrates how to use MicroPython's uasyncio library 
# to run multiple asynchronous tasks concurrently on an ESP32.
# The use of asyncio allows these tasks to share CPU time efficiently
# without blocking each other or requiring multiple threads.
#-------------------------------------------------------------------------
from machine import Pin       # Import Pin class to control GPIO pins
import uasyncio as asyncio    # Import MicroPython asyncio

# Create a Pin object for GPIO 22 configured as an output
led = Pin(22, Pin.OUT)

last_led_state = led.value() # Store the last LED state (initially)

# Async task to toggle the LED every 500 ms
async def toggle_led_state():
    while True:
        led.value(not led.value())   # Toggle the LED state
        await asyncio.sleep(0.5)     # Non-blocking delay 500 ms

# Async task to check and print LED state changes every 10 ms
async def print_led_state():
    global last_led_state
    while True:
        current_state = led.value()
        if last_led_state != current_state:  # LED state change detected
            last_led_state = current_state   # Update stored state
            print(f"LED: {last_led_state}")  # Print LED state
        await asyncio.sleep_ms(10)           # Non-blocking delay 10 ms

# Main coroutine to run both tasks concurrently
async def main():
    # Run toggle_led_state() and print_led_state() concurrently
    await asyncio.gather(toggle_led_state(), print_led_state())

# Run the asyncio event loop
asyncio.run(main())

 


โค้ดตัวอย่างนี้ได้ใช้วงจร Hardware Timer ภายในชิป ESP32 ทำให้เกิดการรันฟังก์ชันซ้ำตามคาบเวลาได้ โดยไม่ต้องใช้เธรด (Thread) หรือการหน่วงเวลาในลูปหลัก ฟังก์ชันประเภทนี้เรียกว่า ฟังก์ชัน Timer Callback ในตัวอย่างนี้ได้แก่ toggle_led_callback(...) และ print_led_state_callback(...) และมีข้อสังเกตว่า การทำงานของฟังก์ชัน Timer Callback จะต้องไม่มีการหน่วงเวลา และทำคำสั่งทั้งหมดของฟังก์ชันให้แล้วเสร็จ

#-------------------------------------------------------------------------
# This script demonstrates using MicroPython's machine.Timer
# to blink an LED and print its state periodically without threads.
# Timers execute callback functions in the background at fixed intervals.
#-------------------------------------------------------------------------
# Import Pin for GPIO, Timer for periodic tasks
from machine import Pin, Timer  

# Create a Pin object for GPIO 22 configured as output
led = Pin(22, Pin.OUT)

# Store last LED state for change detection
last_led_state = led.value()

# A timer callback: toggles LED every 500 ms
def toggle_led_callback(timer):
    led.value(not led.value())  # Toggle LED state

# A timer callback: checks LED state every 1 ms and prints on change
def print_led_state_callback(timer):
    global last_led_state
    current_state = led.value()
    if current_state != last_led_state:
        last_led_state = current_state
        print(f"LED: {current_state}")

# Create and start timers
# Timer 0: Toggle LED every 500 ms
toggle_timer = Timer(0)
toggle_timer.init(period=500, mode=Timer.PERIODIC, 
                  callback=toggle_led_callback)

# Timer 1: Monitor the LED state every 1 ms
print_timer = Timer(1)
print_timer.init(period=1, mode=Timer.PERIODIC, 
                 callback=print_led_state_callback)

# The main loop does nothing, timers work in the background
while True:
    pass

 


Example 6: Dual-GPIO Toggle with Multi-Threading#

โค้ดตัวอย่างนี้สาธิตการใช้ไลบรารี _thread เพื่อรันฟังก์ชันที่ทำหน้าที่สลับสถานะลอจิกของขา GPIO โดยไม่มีการหน่วงเวลา ในตัวอย่างจะใช้สองขา คือ GPIO11 และ GPIO12 ทำงานในเธรดแยกจากกัน ผู้ใช้สามารถนำบอร์ดไปเชื่อมต่อกับอุปกรณ์ตรวจวัดสัญญาณ เช่น USB Logic Analyzer เพื่อดูรูปแบบคลื่นสัญญาณเอาต์พุตที่ได้จากทั้งสองขา

#-------------------------------------------------------------------------
# This script shows how to toggle two pins (e.g. GPIO11 and GPIO12)
# in separate threads as fast as possible (no delays in loops)
#-------------------------------------------------------------------------
import _thread
from machine import Pin

# Configure pins as outputs
pins = [Pin(11, Pin.OUT), Pin(12, Pin.OUT)]

# Thread function: toggle pin as fast as possible
def toggle_pin(pin):
    while True:
        pin.value(not pin.value())

# Start threads
for pin in pins:
    _thread.start_new_thread(toggle_pin, (pin,))

# Main loop does nothing — threads run independently
while True:
    pass

ตัวอย่างการใช้อุปกรณ์ USB Logic Analyzer ร่วมกับซอฟต์แวร์ PulseView เพื่อวัดสัญญาณ ได้ผลดังนี้

รูป: ตัวอย่างคลื่นสัญญาณเอาต์พุตที่ขา GPIO11 และ GPIO12

จากรูปจะเห็นได้ว่า แต่ละเธรดนั้น จะทำงานตามฟังก์ชัน toggle_pin(...) ที่ได้กำหนดไว้ แต่ใช้กับขา GPIO ต่างกัน ในช่วงเวลาใดถ้าฟังก์ชันรันโค้ด จะเห็นว่า สัญญาณมีการเปลี่ยนแปลงสถานะลอจิก แต่จะไม่สามารถทำงานต่อเนื่องได้ เกิดการหยุดชั่วคราว (ช่วงนั้นไม่มีการสร้างสัญญาณพัลส์ออกมา) และถูกสลับให้อีกฟังก์ชันหนึ่งได้รันโค้ดบ้าง ซึ่งเป็นไปตามรูปแบบการทำงานของ Multi-threading โดยสลับกันไปเป็นช่วง ๆ แต่ละช่วงมีระยะเวลาประมาณ ~130 usec (ไมโครวินาที)

รูป: การวัดช่วงเวลาการทำงานของแต่ละฟังก์ชันก่อนถูกสลับให้ฟังก์ชันอื่นทำงานบ้าง

รูป: การวัดช่วงความกว้างของสัญญาณพัลส์ ซึ่งได้ค่าประมาณ 4 usec

จากรูปคลื่นสัญญาณจะเห็นได้ว่า พัลส์ที่เกิดขึ้นมีความกว้างช่วงที่เป็นลอจิก High ประมาณ 4 usec

 


Example 7: Dual-GPIO Toggle with Asyncio#

โค้ดตัวอย่างนี้ได้เปลี่ยนจากการใช้ _thread มาเป็น uasyncio เพื่อรันฟังก์ชันที่ทำหน้าที่สลับสถานะลอจิกของขา GPIO โดยไม่มีการหน่วงเวลา ในตัวอย่างจะใช้สองขา คือ GPIO11 และ GPIO12 ทำงานแยกจากกัน

#-------------------------------------------------------------------------
# This script shows how to toggle two pins (e.g. GPIO11 and GPIO12)
# using asyncio coroutines instead of threads.
#-------------------------------------------------------------------------
import uasyncio as asyncio
from machine import Pin

# Configure pins as outputs
pins = [Pin(11, Pin.OUT), Pin(12, Pin.OUT)]

# Coroutine: toggle pin as fast as possible
async def toggle_pin(pin):
    while True:
        pin.value(not pin.value())  # Toggle the output
        await asyncio.sleep_ms(0)   # Yield to other coroutine

# Main entry point
async def main():
    # Create toggle tasks for each pin
    tasks = [asyncio.create_task(toggle_pin(pin)) for pin in pins]
    await asyncio.gather(*tasks)

# Run event loop
asyncio.run(main())

จากผลการวัดสัญญาณด้วย USB Logic Analyzer จะเห็นได้ว่า ทั้งสองฟังก์ชันมีการสลับการทำงานทุก ๆ ~148 usec จึงเห็นเป็นรูปคลื่นสัญญาณสี่เหลี่ยมที่ขา GPIO ทั้งสอง

รูป: ตัวอย่างรูปคลื่นสัญญาณเมื่อไม่มีการหน่วงเวลา โดยใช้ await asyncio.sleep_ms(0)

 

แต่ถ้าเปลี่ยนจากกรณีแรกที่ใช้ await asyncio.sleep_ms(0) ซึ่งจะไม่มีการหน่วงเวลา (และเป็นการส่งต่อให้อีกฟังก์ชันหนึ่งได้ทำงานต่อไป) ให้มีการหน่วงเวลา เช่น ให้มีการหน่วงเวลาไว้อย่างน้อย 1 msec และ 10 msec (มิลลิวินาที) ตามลำดับ โดยใช้คำสั่งต่อไปนี้ แล้วลองวัดสัญญาณเอาต์พุต ดูความแตกต่าง

  • await asyncio.sleep_ms(1)
  • await asyncio.sleep_ms(10)
# Coroutine: toggle pin as fast as possible
async def toggle_pin(pin):
    while True:
        pin.value(not pin.value())  # Toggle the output
        await asyncio.sleep_ms(1)   # Sleep for 1 msec

ในกรณีที่สองนี้ จากผลการวัดสัญญาณด้วย USB Logic Analyzer แสดงให้เห็นได้ว่า ทั้งสองฟังก์ชันมีการสลับการทำงานจริง แต่ให้ผลแตกต่างจากกรณีแรกอย่างชัดเจน

รูป: ตัวอย่างรูปคลื่นสัญญาณเมื่อหน่วงเวลาด้วย await asyncio.sleep_ms(1)

รูป: ตัวอย่างรูปคลื่นสัญญาณเมื่อหน่วงเวลาด้วย await asyncio.sleep_ms(10)

ข้อสังเกต: การหน่วงเวลา 1~10msec ด้วยคำสั่ง await asyncio.sleep_ms(...) ในความเป็นจริง อาจใช้เวลามากกว่าค่าตัวเลขที่กำหนดไว้ เช่น วัดได้ถึง 10 msec ซึ่งเป็นผลมาจากอัตราการทำงาน หรือ Tick Rate ของระบบปฏิบัติการ FreeRTOS ที่ชิป ESP32 ใช้ในการทำงาน

ค่า Default FreeRTOS Tick Rate (CONFIG_FREERTOS_HZ) คือ 100 Hz ดังนั้นจึงมีช่วงเวลาคาบเท่ากับ 10 msec หากต้องการจะใช้ FreeRTOS Tick Rate สูงขึ้น เช่น 1 kHz จะต้องมีการคอมไพล์ MicroPython สำหรับ ESP32 เพื่อปรับเปลี่ยนค่าดังกล่าว

หากมีการเปลี่ยนเฟิร์มแวร์ เพื่อปรับค่าความถี่ FreeRTOS Tick Rate = 1000 Hz การหน่วงเวลาด้วยคำสั่ง await asyncio.sleep_ms(1) เพื่อสร้างสัญญาณพัลส์ในโค้ดตัวอย่าง จะส่งผลอย่างไรบ้าง ก็สามารถวัดสัญญาณเอาต์พุต ได้ตามรูปตัวอย่างต่อไปนี้

รูป: ตัวอย่างรูปคลื่นสัญญาณเมื่อหน่วงเวลาด้วย await asyncio.sleep_ms(1) แต่ใช้ความถี่ FreeRTOS Tick Rate = 1000 Hz

จากรูปสัญญาณจะเห็นได้ว่า ความกว้างพัลส์ของแต่ละสัญญาณลดลง และวัดได้ประมาณ 2msec

 


Example 8: Dual-GPIO Toggle with ESP32 Timers#

ตัวอย่างถัดไปเป็นการใช้วงจร Hardware Timer ของชิป ESP32 ซึ่งสามารถใช้ได้ถึง 4 ชุด (Hardware Timer 0..3) และนำมาเปิดใช้งานจำนวน 2 ชุด เพื่อสร้างสัญญาณอินเทอร์รัพท์ และจะเกิดเหตุการณ์ทุก ๆ 1 มิลลิวินาที และทำคำสั่งของฟังก์ชันที่เป็น Timer Callback ตามที่กำหนดไว้ การทำงานของฟังก์ชัน Timer Callback จะต้องไม่มีการหน่วงเวลา และทำคำสั่งทั้งหมดของฟังก์ชันให้แล้วเสร็จ

ข้อสังเกต: ระยะเวลาในการเกิดอินเทอร์รัพท์ น้อยที่สุดคือ 1 msec หรือ กำหนดความถี่ได้สูงสุดไม่เกิน 1kHz

#-------------------------------------------------------------------------
# This script toggles two GPIO pins (GPIO11 & GPIO12) using ESP32 timers.
# Each pin is toggled periodically by its own timer interrupt callback,
# allowing very precise and efficient toggling without blocking the CPU.
#-------------------------------------------------------------------------
from machine import Pin, Timer 

# Configure pins as outputs
pins = [Pin(11, Pin.OUT), Pin(12, Pin.OUT)]

# Create a timer callback function to toggle a GPIO pin
def make_toggle_callback(pin):
    def toggle_pin(timer):
        pin.value(not pin.value())
    return toggle_pin # Return a Timer callback function

# Create timers and start them
TIMER_PERIOD_MSEC = 1
timers = []
callbacks = []
for i, pin in enumerate(pins):
    tim = Timer(i)  # Create a hardware timer object
    timers.append(tim)
    callbacks.append(make_toggle_callback(pin))
for i, tim in enumerate(timers):
    tim.init(period=TIMER_PERIOD_MSEC, mode=Timer.PERIODIC, 
             callback=callbacks[i]) # Start timer
while True:
    pass

ตัวอย่างการวัดสัญญาณเอาต์พุตด้วย USB Logic Analyzer มีดังนี้

รูป: ตัวอย่างรูปคลื่นสัญญาณทั้งสองช่อง วัดความกว้างของหนึ่งคาบได้ 1 msec หรือ มีความถี่เท่ากับ 1 kHz

 


Example 9: Dual-GPIO Toggle with Event-based Synchronization#

โค้ดในตัวอย่างนี้สาธิตการสร้างฟังก์ชันแบบ Coroutine โดยมีสองฟังก์ชันที่ทำหน้าที่สลับสถานะลอจิกที่ขาเอาต์พุต ที่ขา GPIO11 และ GPIO12 ทั้งสองมีการสื่อสารกันโดยใช้ Events เพื่อส่งต่อลำดับการทำงานให้ทำงานสลับกัน

โค้ดตัวอย่างนี้สาธิตการใช้งาน ฟังก์ชันแบบ Coroutine (ทำงานแบบอะซิงโครนัส) จำนวน 2 ฟังก์ชัน โดยแต่ละฟังก์ชันมีหน้าที่สลับสถานะลอจิก (Toggle) ที่ขาเอาต์พุต GPIO11 และ GPIO12 ตามลำดับ ฟังก์ชันทั้งสองทำงานสลับกันโดยอาศัย Event จากไลบรารี uasyncio ทำหน้าที่เป็นตัว "ส่งต่อสิทธิ์" (Token Passing) เพื่อควบคุมลำดับการทำงานไม่ให้ทำงานพร้อมกัน แต่จะรอให้ฟังก์ชันหนึ่งเสร็จก่อนแล้วส่งสิทธิ์ไปให้อีกฟังก์ชัน

#-------------------------------------------------------------------------
# This script demonstrates token passing synchronization (using events)
# between two async tasks, each toggling a separate GPIO pin alternately.
#-------------------------------------------------------------------------
import uasyncio as asyncio    # Import asyncio for async support
from machine import Pin       # Import Pin class to control GPIO pins

# Initialize two output pins (GPIO11 and GPIO12)
pins = [Pin(11, Pin.OUT), Pin(12, Pin.OUT)]  
# Create two Event objects for synchronization between two tasks
events = [asyncio.Event(), asyncio.Event()] 

async def task(id, pins, events):
    # Compute the index of the next task in a circular manner
    next_id = (id + 1) % len(pins)  
    pin = pins[id]
    event = events[id]
    while True:
        await event.wait()           # Wait until this task's event is set
        event.clear()                # Clear the event
        pin.value(not pin.value())   # Toggle the pin state
        await asyncio.sleep_ms(1)    # Wait at least for 1 msec
        events[next_id].set()        # Notify the next task

async def main():
    events[0].set()  # Set the first event to start the token passing with task 0
    for i, _ in enumerate(pins):
        asyncio.create_task(task(i, pins, events))  # Create async task
    await asyncio.sleep(10)  # Run the event loop for 10 seconds before exiting

#asyncio.run(main())  # Start the asyncio event loop and run main()

try:
    # Gets the async "scheduler"
    loop = asyncio.get_event_loop()
    # Runs that coroutine until it's completely done
    loop.run_until_complete(main())

except KeyboardInterrupt:
    # This part runs when Ctrl+C is pressed
    print("Program stopped. Exiting...")
    # Stop the async scheduler
    loop.stop() 

จากการวัดสัญญาณด้วย USB Logic Analyzer พบว่า:

  • สัญญาณเดียวกัน: การเปลี่ยนสถานะลอจิกสองครั้งถัดกันของสัญญาณเดียวกัน เมื่อวัดสัญญาณ พบว่า มีระยะห่างประมาณ 20 msec เนื่องจากในโค้ด มีการกำหนดให้แต่ละฟังก์ชัน รอให้ฟังก์ชันอีกตัวทำงานเสร็จ (ให้หน่วงไว้อย่างน้อย 1 msec แต่หน่วงเวลาจริงประมาณ 10msec) ก่อนที่จะได้รับสิทธิ์ทำงานอีกครั้ง
  • สัญญาณต่างกัน: เมื่อเปรียบเทียบการเปลี่ยนสถานะระหว่างสองสัญญาณ จะพบว่ามีระยะห่าง 10 msec (ใกล้เคียงกับ Tick Period ของ FreeRTOS) เพราะทั้งสองฟังก์ชันจะต้องทำงานสลับกัน

รูป: ตัวอย่างรูปคลื่นสัญญาณทั้งสองช่อง (GPIO-11 และ GPIO-12)

 


กล่าวสรุป#

บทความนี้ได้นำเสนอเนื้อหาตอนที่ 1 ที่มีตัวอย่างการเขียนโค้ด เพื่อใช้งาน GPIO (ดิจิทัล) ของ ESP32 เบื้องต้น ได้มีการนำเสนอและเปรียบเทียบรูปแบบการเขียนโค้ดที่แตกต่างกันสำหรับการใช้คำสั่ง หรือไลบรารีต่าง ๆ ของ MicroPython และมีตัวอย่างการวัดสัญญาณด้วย USB Logic Analyzer เพื่อศึกษาพฤติกรรมการทำงานของโค้ด MicroPython และช่วยให้เข้าใจหลักการทำงานของ MicroPython สำหรับ ESP32 / ESP32-S3 ได้ดีขึ้น

บทความถัดไป: "ตัวอย่างการเขียนโค้ด MicroPython สำหรับบอร์ด Espressif ESP32 (ตอนที่ 2)

 


This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Created: 2025-08-11 | Last Updated: 2025-08-17