> For the complete documentation index, see [llms.txt](https://think-embedded.gitbook.io/micropython/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://think-embedded.gitbook.io/micropython/micropython-for-esp32/esp32-code-examples.md).

# ESP32 Code Examples

## คำแนะนำ

1. ผู้อ่านควรมีความรู้ภาษา **Python** เบื้องต้น พื้นฐานอิเล็กทรอนิกส์ และการเขียนโค้ดไมโครคอนโทรลเลอร์ในระดับพื้นฐานมาบ้าง เช่น **Arduino**
2. บอร์ด **ESP32** ที่เราสามารถเลือกมาใช้งานนั้น มีหลายรูปแบบ ความเหมาะสมของบอร์ดแต่ละชนิด ก็อาจแตกต่างกันไปขึ้นอยู่กับวัตถุประสงค์สำหรับการใช้งาน ดังนั้นขอแนะนำให้ใช้บอร์ดที่เสียบขา **Pin Headers** ลงบนเบรดบอร์ดได้ สำหรับการต่อวงจรใช้งานร่วมกับอุปกรณ์หรือวงจรอิเล็กทรอนิกส์พื้นฐานแบบต่าง ๆ&#x20;
3. ในกรณีที่ต้องการใช้ไมโครไพธอนสำหรับ **ESP32** แนะนำให้ใช้บอร์ด **ESP32** ที่มีชิป **PSRAM** หรือเรียกว่า **SPIRAM** (เช่น **4MB**) เพื่อเพิ่มความจุของหน่วยความจำ นอกเหนือจาก **RAM** ที่มีอยู่ภายในชิป **ESP32 SoC**
4. ประเด็นหนึ่งที่สำคัญเมื่อได้เลือกใช้บอร์ดไมโครคอนโทรลเลอร์ คือ การทราบตำแหน่งของขาต่าง ๆ ของบอร์ด หรือ **Pin Layout** (**PinOut Map**) แตกต่างกันไปขึ้นอยู่กับบอร์ดที่ใช้ ดังนั้นควรตรวจสอบให้ถูกต้องเมื่อนำไปต่อวงจร จะไม่ได้เกิดความผิดพลาดในการใช้งานขา **GPIO** หรือขาแรงดันไฟเลี้ยง (ขา **3.3V** และ **GND**) จากบอร์ด **ESP32**&#x20;

![รูปภาพ: LilyGO / TTGO-T8 v1.3 Pin Map](/files/-MKL8TdYBw2dOQBDilif)

แหล่งข้อมูลเกี่ยวกับบอร์ด **LilyGO TTGO T8 (V1.1 / V1.3 / V1.7)** ที่ได้นำมาใช้งาน

* **TTGO-T8-ESP32 (Repository):** <https://github.com/LilyGO/TTGO-T8-ESP32>
* **Schematic (PDF)**: <https://github.com/LilyGO/TTGO-T8-ESP32/blob/master/t8_v1.7.1.pdf>

## โค้ดตัวอย่างที่ 1: LED Blink + Push Button (Polling)

ตัวอย่างแรกเป็นการทดลองใช้ขา **GPIO** ของ **ESP32** เป็นเอาต์พุตหรืออินพุตแบบดิจิทัล ถ้าเป็นเอาต์พุต มีการกำหนดและเปลี่ยนสถานะค่า 0 หรือ 1 สำหรับเอาต์พุต และสลับค่าโดยเว้นระยะเวลาตามที่กำหนดไว้ ถ้านำไปขาเอาต์พุตนี้ไปต่อกับวงจร **LED** ก็จะทำให้มองเห็นการเปลี่ยนแปลงของสถานะลอจิกได้&#x20;

อีกกรณีหนึ่งคือ การใช้ขา **GPIO** เป็นอินพุต ใช้ในการอ่านค่าจากวงจรปุ่มกด (**Push Button**) เช่น วงจรปุ่มกดที่ทำงานแบบ **Active-Low** คือ ถ้าไม่กดปุ่ม จะได้ค่าเป็น **High (1)** แต่ถ้ากดปุ่มจะได้ค่าเป็น **Low (0)**&#x20;

คลาส [`Pin`](https://docs.micropython.org/en/latest/library/machine.Pin.html) จากไลบรารี [`machine`](https://docs.micropython.org/en/latest/library/machine.html) เกี่ยวข้องกับการใช้งาน **GPIO (Digital I/O pins)** ของฮาร์ดแวร์ เช่น ใช้งานเป็นขาดิจิทัล-อินพุต หรือเอาต์พุต การเปิดใช้งานขาอินพุตร่วมกับอินเทอร์รัพท์ (**Interrupt**) และการเปิดใช้งาน **Internal Pull-Up** หรือ **Pull-Down** สำหรับขาที่จะถูกใช้เป็นอินพุต เป็นต้น

ในตัวอย่างนี้ได้เลือกใช้ขา **GPIO-21** สำหรับ **LED** และ **GPIO-22** สำหรับปุ่มกด (ทำงานแบบ **Active-Low** และเปิดใช้งาน **Pull-Up** ภายในวงจรที่ขาดังกล่าว)

การทำงานของโปรแกรมจะทำให้ **LED** กระพริบ และมีการตรวจสอบสถานะอินพุตของปุ่มกด ถ้าพบว่า มีการกดปุ่ม (ได้ค่าเป็น 0) จะหยุดการทำงานของลูป `while` และจบการทำงานของโปรแกรม

```python
from micropython import const
from machine import Pin
import utime as time

LED_GPIO = const(21) # use GPIO-21 for LED output
BTN_GPIO = const(22) # use GPIO-22 for push-button input

# create an object for the LED pin
led = Pin( LED_GPIO, mode=Pin.OUT ) 
# create an object for button pin (with pull-up enabled)
btn = Pin( BTN_GPIO, mode=Pin.IN, pull=Pin.PULL_UP )

state = False # used to keep the output state
try:
    while btn.value() != 0:  # check input button 
        state = not state    # toggle state
        led.value( state )   # write value to output pin
        time.sleep_ms(100)   # sleep for 0.1 seconds
except KeyboardInterrupt:
    pass
finally:
    led.value(0) # turn off the LED
print('Done')
```

## โค้ดตัวอย่างที่ 2: LED Blink + Push Button (Interrupt-Driven)

จากตัวอย่างที่แล้ว เราได้ใช้วิธีวนซ้ำ (**Polling**) เพื่อคอยอ่านค่าอินพุตจากวงจรปุ่มกด และนำค่าอินพุตที่อ่านได้ในแต่ละครั้ง มากำหนดเงื่อนไขในการหยุดหรือออกจากลูป `while` ในตัวอย่างนี้ เราจะใช้วิธีที่เรียกว่า "อินเทอร์รัพท์" (**Interrupt**) สำหรับอินพุต-ปุ่มกด

เมื่อมีการกดปุ่ม จะเกิดการเปลี่ยนระดับลอจิก จาก **High** เป็น **Low** หรือที่เรียกว่า “ขอบขาลง” (**Falling Edge**) และเมื่อปล่อย จะเกิด “ขอบขาขึ้น” **(Rising Edge)** และอาจเกิดได้มากกว่าหนึ่งครั้ง ถ้ามีการกระเด้งของสวิตซ์ปุ่มกด **(Switch Bouncing)**

เราสามารถกำหนดให้ไมโครไพธอนเรียกฟังก์ชันที่เราสร้างขึ้นมา (**Callback Function** หรือ **Interrupt Handler**) เช่น ฟังก์ชัน **`btn_handler()`** ในโค้ดตัวอย่าง ให้ทำงานโดยอัตโนมัติเมื่อเกิดเหตุการณ์ดังกล่าว เช่น ขอบขาลง ในตัวอย่างนี้จะทำให้ตัวแปรภายนอก `stop` เปลี่ยนจาก `False` เป็น `True` ซึ่งใช้ในการตรวจสอบเงื่อนไขการออกจากลูป `while`&#x20;

```python
from micropython import const
from machine import Pin
import utime as time

LED_GPIO = const(21) # use GPIO-21 for LED output
BTN_GPIO = const(22) # use GPIO-22 for push-button input

# create objects from machine.Pin
led = Pin( LED_GPIO, mode=Pin.OUT )
btn = Pin( BTN_GPIO, mode=Pin.IN, pull=Pin.PULL_UP )

def btn_handler(pin): # callback function
    global stop
    args = (pin, pin.value())
    print( 'callback: {} value={}'.format(*args) )
    stop = True

# enable interrupt handler for Button pin
btn.irq( handler=btn_handler, trigger=Pin.IRQ_FALLING )

stop  = False # loop condition variable
state = False # LED state

try:
    while not stop:
        state = not state    # toggle state
        led.value( state )   # write value to output pin
        time.sleep_ms( 100 ) # sleep for 100 msec
except KeyboardInterrupt:
    pass
finally:
    btn.irq( handler=None ) # disable interrupt for button pin
    led.value(0) # turn off the LED
print('Done')
```

## โค้ดตัวอย่างที่ 3: LED Toggle on Button Press

โค้ดตัวอย่างถัดไปสาธิตการทำให้ **LED** เปลี่ยนสถานะเอาต์พุตหนึ่งครั้งเมื่อมีการกดปุ่มการทำงานของโค้ดจะอาศัยการเปิดใช้งานอินเทอร์รัพท์ที่ขาอินพุต (ตรวจสอบเหตุการณ์ขอบขาลง) และมีการสร้างฟังก์ชัน **`btn_handler()`** ให้ทำหน้าที่เป็น **Callback Function**&#x20;

เมื่อเกิดเหตุการณ์ดังกล่าว ฟังก์ชัน **`btn_handler()`** จะสลับสถานะของ **LED** หนึ่งครั้ง นอกจากนั้น ถ้ามีการกดปุ่มค้างไว้ (**Long Press**) ให้จบการทำงานของโปรแกรม

```python
from micropython import const
from machine import Pin
import utime as time

BTN_GPIO = const(22) # use GPIO-22 for push-button input
LED_GPIO = const(21) # use GPIO-21 for LED output

# create objects from machine.Pin
led = Pin( LED_GPIO, mode=Pin.OUT )
btn = Pin( BTN_GPIO, mode=Pin.IN, pull=Pin.PULL_UP )

# global variable
btn_was_pressed = False

def btn_handler(pin): # callback function
    global btn_was_pressed
    state = led.value()     # get current state
    led.value( not state )  # update LED output
    btn_was_pressed = True
    btn.irq( handler=None ) # disable IRQ

# enable interrupt handler for Button pin
btn.irq( handler=btn_handler, trigger=Pin.IRQ_FALLING )

stop = False
try:
    while not stop:
        if btn_was_pressed:
            cnt = 0
            while btn.value() == 0: # wait for button release
                time.sleep_ms(20)
                cnt = cnt + 1
                if cnt > 100: # long pressed 
                    stop = True
                    break
            btn_was_pressed = False
            btn.irq( handler=btn_handler ) # re-enable IRQ
            time.sleep_ms(100)
except KeyboardInterrupt:
    pass
finally:
    btn.irq( handler=None ) # disable IRQ for button 
    led.value(0) # turn off LED
print('Done')
```

## โค้ดตัวอย่างที่ 4: **Timer-based LED Blink**

ตัวอย่างถัดไปเป็นการใช้ **Timer** จากคลาส `Timer` ในไลบรารี `machine` เพื่อทำคำสั่งของฟังก์ชัน  `led_toggle()` ที่ทำหน้าที่เป็น **Callback** ตามระยะเวลาที่กำหนด (**Periodic Mode**) เช่น ทุก ๆ 500 มิลลิวินาที โดยอัตโนมัติ และทำให้เกิดการสลับสถานะลอจิกสำหรับ **LED**&#x20;

ในตัวอย่างนี้มีการระบุหมายเลขของ **Timer** เป็น **-**&#x31; ซึ่งหมายถึง การใช้งานไทม์เมอร์แบบซอฟต์แวร์ (**FreeRTOS-based**) และไม่ได้ใช้ **Hardware Timer**  (หมายเลข 0,1,2,3) ของ **ESP32**

```python
from micropython import const
from machine import Pin, Timer
import utime as time

BTN_GPIO = const(22) # use GPIO-22 for push-button input
LED_GPIO = const(21) # use GPIO-21 for LED output

led = Pin( LED_GPIO, mode=Pin.OUT )
btn = Pin( BTN_GPIO, mode=Pin.IN, pull=Pin.PULL_UP )

def led_toggle(timer): # callback function for timer
    global led
    # show elapsed system time in msec
    print( 'timer ticks: {} msec'.format(time.ticks_ms()) )
    led.value( not led.value() ) # toggle LED 

timer = Timer( -1 ) # create a Timer object
# use the timer in periodic mode (period = 500 msec)
timer.init( period=500, 
            mode=Timer.PERIODIC,
            callback=led_toggle )

# Press the button to stop and exit the loop
try:
    while True:
        # check whether button is pressed
        if btn.value() == 0:
           break
        time.sleep_ms(10)
except KeyboardInterrupt:
    pass
    
led.value(0)   # turn off LED
timer.deinit() # stop timer
print('Done')
```

## โค้ดตัวอย่างที่ 5: Thread-based LED Blink

ตัวอย่างถัดไป สาธิตการกระพริบของ **LED** ซึ่งเกิดจากการทำงานของ ‘เธรด’ (**Thread**) โดยใช้ไลบรารี `_thread` และกำหนดใช้ฟังก์ชัน `led_toggle()` สำหรับการทำงานที่เกี่ยวข้อง การกระพริบจะเกิดซ้ำไปเรื่อย ๆ จนกว่า ตัวแปรภายนอก `stop` จะมีค่าเป็น `True` เช่น เกิดขึ้นเมื่อมีการกดปุ่ม

นอกจากนั้น ยังมีการใช้วงจร **Timer** ของ **ESP32** จากคลาส `machine.Timer` ในโค้ดตัวอย่างนี้ `timer` จะถูกตั้งเวลาให้ทำงานแบบ **One-Shot** เมื่อเวลาผ่านไปตามที่กำหนด เช่น มีคาบเท่ากับ **5000** มิลลิวินาที เมื่อถึงเวลา จะเรียกฟังก์ชัน `stop_led_blink()` ที่ทำหน้าที่เป็น **Callback Function** และทำคำสั่งเพียงหนึ่งครั้ง คือ การเปลี่ยนค่าของตัวแปรภายนอก `stop` จาก `False` ให้เป็น `True` ซึ่งจะถูกใช้ในการตรวจสอบเพื่อการจบการทำงานของโปรแกรม

```python
from micropython import const
from machine import Pin, Timer
import utime as time
import _thread # for multi-threading

LED_GPIO = const(21) # use GPIO-21 for LED output
BTN_GPIO = const(22) # use GPIO-22 for push-button input

btn = Pin( BTN_GPIO, mode=Pin.IN, pull=Pin.PULL_UP )
led = Pin( LED_GPIO, mode=Pin.OUT )

stop = False # used as a global variable

def led_toggle(led): # thread function
    global stop
    state = 0
    while not stop:
        state = not state
        print( 'LED state: {}'.format( int(state) ))
        led.value(state)   # update LED output
        time.sleep_ms(100) # sleep for 0.1 seconds

# create and start a new thread
_thread.start_new_thread( led_toggle, (led,) )

def stop_led_blink(timer): # callback function for timer
    global stop
    stop = True

timer = Timer( 0 ) # create a Timer object
# start the timer in one-shot mode
timer.init( period=5000,
            mode=Timer.ONE_SHOT,
            callback=stop_led_blink )
# Press the button to stop and exit the loop
while not stop:
    # check whether button is pressed
    if btn.value() == 0: 
        stop = True
        break
    time.sleep_ms(10)

led.value(0)   # turn off LED
timer.deinit() # stop timer
print('Done')
```

## โค้ดตัวอย่างที่ 6: **PWM LED Dimming**&#x20;

ถัดไปเป็นตัวอย่างการสร้างสัญญาณประเภท **PWM** (**Pulse Width Modulation**) แล้วนำไปใช้เป็นสัญญาณเอาต์พุตสำหรับวงจร **LED** ถ้าปรับค่า **Duty Cycle** ของสัญญาณ **PWM** จาก **0** ไปจนถึงค่าสูงสุด **1023** สำหรับ **ESP32** ซึ่งมีความละเอียดเท่ากับ 10 บิต (ช่วงความกว้างที่เป็น **High** ในแต่ละคาบ จะอยู่ระหว่าง 0% ถึง 100% ตามลำดับ) เราจะเห็น **LED** สว่างขึ้นตามลำดับ ความถี่ของ **PWM** เมื่อใช้ **ESP32** สามารถเลือกได้ในช่วง **1 Hz** ถึง **40 MHz** แต่สำหรับตัวอย่างนี้ เลือกความถี่เท่ากับ **500 Hz**

การสร้างสัญญาณ **PWM** สำหรับขา **Pin** จะใช้คลาส `PWM` ของไลบรารี `machine` ให้ลองสังเกตการใช้คำสั่ง เช่น `duty()` และ `freq()` ในการเขียนหรืออ่านค่าสำหรับค่า **Duty Cycle** และความถี่ของสัญญาณ

```python
from micropython import const
from machine import Pin, PWM
import utime as time
import math

LED_GPIO = const(21) # use GPIO-21 for LED output
BTN_GPIO = const(22) # use GPIO-22 for push-button input

btn = Pin( BTN_GPIO, mode=Pin.IN, pull=Pin.PULL_UP )
led = Pin( LED_GPIO, mode=Pin.OUT )

# create an object from PWM for the LED pin
pwm = PWM( led, freq=500, duty=0 )
time.sleep_us(10)
print ('PWM freq: {} Hz'.format( pwm.freq() ))

N = const(32)
# create a list of precomputed duty-cyle values
values = [int(1023*math.sin(math.pi*i/N)) for i in range(N)]

try:
    i = 0
    while True:
        if btn.value() == 0: # is button pressed ?
            break
        value = values[i]
        pwm.duty( value )  # set the duty cycle value
        percent = 100*( pwm.duty()/1024.0 )
        print( 'Duty cycle: {:4d} ({:4.1f}%)'.format(value, percent) )
        i = (i+1) % N
        time.sleep_ms( 50 )
except KeyboardInterrupt:
    pass
pwm.duty(0)  # set duty cycle to 0
pwm.deinit() # important: turn off the PWM pin
print('Done')
```

ในตัวอย่างนี้ มีการคำนวณค่าคงตัวโดยใช้ฟังก์ชัน `math.sin()` และเก็บไว้ในอาร์เรย์ที่มีขนาดเท่ากับ **N** (**N=32**) แล้วนำไปใช้เพื่อกำหนดค่า **Duty Cycle** ทีละค่าตามลำดับในอาร์เรย์&#x20;

เมื่อทดสอบการทำงานของโค้ดตัวอย่างกับอุปกรณ์จริง จะเห็นการปรับความสว่างของ **LED** ด้วยสัญญาณแบบ **PWM**

## โค้ดตัวอย่างที่ 7: **Neopixel RGB LED**

เฟิร์มแวร์ของไมโครไพธอน มีไลบรารี `neopixel` เพื่อใช้ในการกำหนดสีให้แก่โมดูล **RGB LED** (**WS2812B**) หรือที่มักเรียกว่า **Neopixel** ซึ่งใช้สายสัญญาณดิจิทัลเป็นเอาต์พุต เพียงเส้นเดียว (ในตัวอย่างนี้ ได้เลือกใช้ขา **GPIO-23**)

ตัวอย่างโค้ดนี้ สาธิตการกำหนดสีให้ **RGB LED** จำนวน 1 ดวง โดยเปลี่ยนสี (ระบุค่าสีแบบ 24 บิต ประกอบด้วย 3 ไบต์แบบ **3-Tuple**) จากแดง (**Red**), เขียว (**Green**) และน้ำเงิน (**Blue**) ไปตามลำดับ

ข้อสังเกต: เนื่องจากใช้โมดูลที่มี **RGB LED** เพียงดวงเดียว ดังนั้นเลือกจึงใช้ **3.3V** จากบอร์ด **ESP32** เป็นแหล่งจ่ายแรงดันคงที่ได้ แต่ถ้าใช้ **RGB LED** จำนวนหลายดวงและอาจใช้กระแสไฟมาก ควรใช้แหล่งจ่ายแรงดันคงที่จากภายนอก

```python
from micropython import const
from machine import Pin
import utime as time
from neopixel import NeoPixel

GPIO_NUM = const(23)  # use GPIO-23
np_pin = Pin( GPIO_NUM, Pin.OUT ) # for Neopixel output

BTN_GPIO = const(22) # use GPIO-22 for push-button input
btn = Pin( BTN_GPIO, mode=Pin.IN, pull=Pin.PULL_UP )

stop = False
def btn_callback(pin):
    global stop
    stop = True

btn.irq( trigger=Pin.IRQ_FALLING,
         handler=btn_callback )

NUM_PIXELS = 1 # only one RGB LED in the strip
# create a Neopixel object
np = NeoPixel( np_pin, NUM_PIXELS )

# convert an integer to a 3-tuple object for RGB value
int2rgb = lambda x: ((x >> 16)&0xff,(x>>8)&0xff,(x)&0xff)

# a list of predefined RGB color values
colors  = [ (255,0,0), (0,255,0), (0,0,255) ]
colors += [ int2rgb(0xffff00),
            int2rgb(0xff00ff),
            int2rgb(0x00ffff) ]
colors += 3*[ (0,0,0) ] # RGB off, 3 times
print( colors)
try:
    while not stop:
        for color in colors:
            if stop:
                break
            # set color value to the first RGB LED
            np[0] = color
            # apply the color value
            np.write() 
            time.sleep_ms(1000)
except KeyboardInterrupt:
    pass
np[0] = (0,0,0) # turn off color (black)
np.write()
print('Done')
```

![รูปภาพ:  ตัวอย่างการต่อวงจรทดลองโมดูล WS2812B](/files/-MKYkWURIH7dMBz9AON5)

![รูปภาพ: โมดูล WS2812B ขณะที่ให้แสงสีเขียว](/files/-MKYkgJF2E4akTJPd9qi)

## โค้ดตัวอย่างที่ 8: **DHT22 Sensor Reading**

เฟิร์มแวร์ของไมโครไพธอน มีไลบรารี `dht` สำหรับการอ่านค่าจากโมดูลเซ็นเซอร์ **DHT22** มาให้แล้ว ดังนั้นเราสามารถลองใช้คำสั่ง เพื่ออ่านค่าอุณหภูมิ (**Temperature**) และความชื้นสัมพัทธ์ (**Relative Humidity**) ได้ไม่ยาก&#x20;

การต่อวงจรเพื่อใช้งานโมดูล **DHT22** ก็ใช้แรงดันไฟเลี้ยง **3.3V** และ **GND** จากบอร์ด **ESP32** และสายสัญญาณ **Digital I/O** เพียงเส้นเดียว ในตัวอย่างนี้ได้เลือกใช้ขา **GPIO-19** (และจะต้องมีตัวต้านทานแบบ **Pull-Up** เช่น **4.7k** หรือ **10k** ต่ออยู่ด้วย)

การอ่านค่าจากโมดูล **DHT22** จะทำทุก ๆ **2000** มิลลิวินาที โดยใช้ **Software Timer** ช่วยจัดการเพื่อให้ทำงานในลักษณะที่มีคาบ **(Periodic Task)**

```python
from micropython import const
from machine import Pin, Timer
import utime as time
import dht

GPIO_NUM = const(19) # use GPIO-19
dht22 = dht.DHT22( Pin(GPIO_NUM) ) # create a DHT22 object

text = u'DHT22 reading: {:.1f} °C, {:.1f} %RH'

def read_dht22(timer):
    try:
        # start the measurement 
        dht22.measure()
        # read and show values from DHT22
        t,h = dht22.temperature(), dht22.humidity()
        print( text.format(t,h) )
    except OSError as ex:
        print('Sensor reading error!', ex)

timer = Timer( -1 ) # create a Timer object
# use the timer in periodic mode (period = 500 msec)
timer.init( period=2000, 
            mode=Timer.PERIODIC,
            callback=read_dht22 )
try:
    while True:
        pass
except KeyboardInterrupt:
    pass
timer.deinit()
print('Done')
```

![รูปภาพ:  ตัวอย่างการต่อวงจรทดลองโมดูล DHT22](/files/-MKYqdua46_t7vF5ltbR)

## โค้ดตัวอย่างที่ 9: **ESP32 ADC Reading**

การใช้คำสั่งของไมโครไพธอน เพื่ออ่านค่าจากวงจร **ADC (Analog-to-Digital Converter)** ของ **ESP32** จะใช้ได้กับขาหมายเลข **GPIO 32** ถึง **39** สำหรับ **ADC1** ค่าที่ได้จะอยู่ในช่วง **0** ถึง **4095 (**&#xE04;วามละเอียดสูงสุด **12** บิ&#xE15;**)** และแรงดันจะต้องอยู่ในช่วง **0V** ถึง **1.1V** และถ้าสูงกว่านั้น จะอ่านได้ค่า **4095**&#x20;

แต่ถ้าจะให้รับแรงดันอินพุตได้สูงกว่านั้น (แต่ต้องไม่เกิน **3.6V !!!)** ต้องกำหนดค่าสำหรับ **Attenuation** (การลดทอนขนาดสัญญาณ) ให้เหมาะสม มีค่าให้เลือกได้ดังนี้

* **`ADC.ATTN_0DB`** (ไม่มีการลดทอนสัญญาณ หรือ 0dB และอ่านแรงดันในช่วง 0V ถึง 1.1V โดยประมาณ)
* **`ADC.ATTN_2_5DB`** (การลดทอนสัญญาณ -2.5dB)
* **`ADC.ATTN_6DB`** (การลดทอนสัญญาณ -6dB)
* **`ADC.ATTN_11DB`** (การลดทอนสัญญาณสูงที่สุด -11dB และอ่านแรงดันในช่วง 0V ถึง 3.3V โดยประมาณ)

นอกจากนั้นยังสามารถเลือกได้ว่า ต้องการจะอ่านข้อมูลที่มีความละเอียดกี่บิต ตั้งแต่ 9 บิต จนถึง 12 บิต

```python
from micropython import const
from machine import Pin, ADC
import utime as time

ADC_GPIO = const(34) # use GPIO-34 for ADC input channel
adc = ADC(Pin(ADC_GPIO),unit=1) # create an ADC object
adc.atten(ADC.ATTN_11DB)   # set 11dB attenuation for input
adc.width(ADC.WIDTH_12BIT) # set 12 bit return values

NUM_SAMPLES = const(4)
for i in range(20): # repeat 20 times
    samples = []
    for j in range(NUM_SAMPLES):
        samples.append( adc.read() )
    # calcuate the average value from N samples
    value_avg = round(sum(samples)/NUM_SAMPLES)
    print( 'ADC: {:4d}'.format(value_avg))
    time.sleep_ms(200)
print('Done')
```

ข้อสังเกต: จากการทดลองอ่านค่าจากวงจรแบ่งแรงดัน และตั้งค่าแรงดันให้อยู่ในช่วง **0V** ถึง **3.3 V** ค่าที่อ่านได้จาก **ADC** ของ **ESP32** อาจมีความแม่นยำไม่สูงนัก (ค่าไม่ค่อยนิ่ง)

## โค้ดตัวอย่างที่ 10: ESP32 DAC Output

**ESP32** มีวงจร **DAC (Digital-to-Analog Converter)** อยู่ภายใน แบ่งเป็น 2 ช่อง ซึ่งตรงกับขา **GPIO-26 (DAC Channel 1)** และ **GPIO-25 (DAC Channel 2)** ตามลำดับ สามารถกำหนดค่าตัวเลขขนาด 8 บิต สำหรับเอาต์พุตได้ ในช่วง 0 ถึง 255 ซึ่งหมายถึง 0V ถึง 3.3V

เราสามารถใช้คลาส `machine.DAC` เพื่อสร้างสัญญาณเอาต์พุตแบบแอนะล็อกโดยใช้ **DAC** ของ **ESP32** ตามตัวอย่างดังนี้ ซึ่งได้เลือกใช้ **DAC** ช่อง 1 ที่ขา **GPIO-26** เป็นเอาต์พุต แล้วใช้สายไฟต่อจากขานี้ไปยังขา **GPIO-34** ซึ่งจะถูกใช้เป็นขาสำหรับ **ADC** เพื่ออ่านค่าอินพุตจากสัญญาณแอนะล็อก (เลือกขนาดข้อมูลที่อ่านได้เป็นแบบ 10 บิต)

```python
from micropython import const
from machine import Pin, ADC, DAC
import utime as time

ADC_GPIO = const(34) # use GPIO-34 for ADC input channel
adc = ADC(Pin(ADC_GPIO))   # create an ADC object
adc.atten(ADC.ATTN_11DB)   # set 11dB attenuation for input
adc.width(ADC.WIDTH_10BIT) # set 10 bit return values

#  GPIO25 (Channel 2) and GPIO26 (Channel 1)
DAC_NUM = const(26)        # GPIO26 
dac = DAC( Pin(DAC_NUM) )  # 8-bit DAC

NUM_SAMPLES = const(4)
text = 'DAC:{:4d} -> ADC:{:5d}'
for value in range(255):
    dac.write( value )
    time.sleep_us(100)
    samples = []
    for j in range(NUM_SAMPLES):
        samples.append( adc.read() )
    # calcuate the average value from N samples
    value_avg = round(sum(samples)/NUM_SAMPLES)
    print( text.format(value, value_avg) )
print('Done')
```

## โค้ดตัวอย่างที่ 11: Pulse Width Measurement

ตัวอย่างถัดไปสาธิตการวัดความกว้างของพัลส์ช่วงที่เป็น **High** โดยใช้คำสั่ง `machine.time_pulse_us()` และสร้างสัญญาณ **PWM** เพื่อใช้ในการทดสอบ

ในการทดสอบการทำงานของโค้ด ได้เลือกใช้ขา **GPIO-19** เป็นขาเอาต์พุตสำหรับสัญญาณ **PWM** โดยเลือกความถี่เท่ากับ **1kHz** และปรับค่า **Duty Cycle** ได้ในช่วง 10% ถึง 90% และสัญญาณเอาต์พุตจะถูกนำไปป้อนกลับให้ขา **GPIO-23** ที่ใช้เป็นขาอินพุต-ดิจิทัล

```python
from micropython import const
from machine import Pin, PWM, time_pulse_us
import utime as time

PWM_OUT_GPIO  = const(19)
PULSE_IN_GPIO = const(23)

freq = 1000 # in Hz
pwm_pin = Pin( PWM_OUT_GPIO, mode=Pin.OUT )
# create an object from PWM for output pin
pwm = PWM( pwm_pin, freq=freq, duty=0 )
time.sleep_us(10)

pwm_freq = pwm.freq()
pwm_period = 10**6//pwm_freq  # in usec
print ('PWM freq:   {:6d} Hz'.format( pwm_freq ))
print ('PWM period: {:6d} usec'.format( pwm_period ))

pulse_in = Pin( PULSE_IN_GPIO )
N = 10 # total steps for the PWM duty cycle
for i in range(1,N):
    p = pwm_period*i//N # pulse with in usec
    pwm.duty( 1023*p//pwm_period ) # set duty cycle
    time.sleep_us( 2*pwm_period )
    # wait until the input signal goes low
    while pulse_in.value()==1: pass
    # measure the next high pulse width
    t = time_pulse_us( pulse_in, 1, pwm_period )
    # show the set value against the measured value
    print( 'Pulse width: {:4d},{:4d} usec'.format(p,t) )
pwm.deinit()
print('Done')
```

ตัวอย่างข้อความเอาต์พุตเปรียบเทียบค่าที่ต้องการกับค่าที่วัดได้ สำหรับการตั้งค่า **Duty Cycle** ในแต่ละกรณี ตั้งแต่ 10% ถึง 90% หรือความกว้างของพัลส์ตั้งแต่ 100 usec ถึง 900 usec สำหรับสัญญาณ **PWM** ที่มีคาบเท่ากับ 1000 usec

![รูปภาพ: ตัวอย่างข้อความเอาต์พุต](/files/-MKQqJ2Lpc2yWr_gFDfE)

## โค้ดตัวอย่างที่ 12: Getting System Info

โค้ดตัวอย่างถัดไป สาธิตการอ่านข้อมูลเกี่ยวกับระบบ เช่น ข้อมูลเกี่ยวกับ **Machine ID** ซึ่งในกรณีของ **ESP32** ก็คือ **MAC Address** ที่มี 6 ไบต์ โดยการใช้คำสั่ง `machine.unique_id()` หรือคำสั่ง  `network.WLAN().config('mac')` ซึ่งจะได้ข้อมูลเป็น `bytes` และใช้คำสั่ง `ubinascii.hexlify()` แปลงข้อมูลดังกล่าวให้เป็นข้อความเลขฐานสิบหก

```python
import machine
import ubinascii
import network

# get the machine ID (MAC address of ESP32)
machine_id = machine.unique_id()
hex_id = ubinascii.hexlify( machine_id,':' )
print( hex_id.decode('ascii') )

# get the MAC address of ESP32
mac_addr = network.WLAN().config('mac')
hex_addr = ubinascii.hexlify( mac_addr,':' )
print( hex_addr.decode('ascii') )
```

ถ้าต้องการตรวจสอบดู ขนาดของหน่วยความจำ **Flash** และขนาดของหน่วยความจำ **RAM** ที่ยังว่างอยู่ หรือถูกใช้ไปในขณะนั้น ก็สามารถทำคำสั่งตามโค้ดตัวอย่างนี้ได้

```python
import esp
import gc

gc.collect() # run garbage collector

# get flash size
fs = esp.flash_size()//1024 # in KB
# get free memory
free_mem_kb = gc.mem_free()//1024 # in KB
# get allocated memory
alloc_mem_kb = gc.mem_alloc()//1024 # in KB

print( '      Flash size: {:4d} MB'.format(fs) )
print( '     Free memory: {:4d} KB'.format(free_mem_kb) )
print( 'Allocated memory: {:4d} KB'.format(alloc_mem_kb) )
```

ถ้าต้องการทราบข้อมูลเกี่ยวกับระบบ เช่น ชื่อของระบบ เวอร์ชันของเฟิร์มแวร์ของไมโครไพธอนที่กำลังใช้งานอยู่นั้น ก็สามารถทำคำสั่ง `uos.uname()` ตามโค้ดตัวอย่างต่อไปนี้ได้

```python
import machine
import uos 

names = ['sysname','nodename',
         'release','version','machine']

sys_info = dict(zip(names,uos.uname()))
for n,v in sys_info.items():
    print( "{:>10s}: '{}'".format(n,v) )

```

## โค้ดตัวอย่างที่ 13: SPI Loopback Test

บัส **SPI** เป็นวิธีการสื่อสารข้อมูลที่มักพบเห็นได้บ่อย โดยทั่วไปจะใช้สัญญาณ 3 เส้น คือ **SCK (Serial Clock)**, **MOSI (Master-Out-Slave-In)** และ **MISO (Master-In-Slave-Out)**

โดยทั่วไปแล้ว เราจะใช้ไมโครคอนโทรลเลอร์ทำหน้าที่เป็น ‘มาสเตอร์’ (**Master**) สำหรับบัส **SPI** เพื่อเชื่อมต่อกับอุปกรณ์อื่นในระบบบัส ซึ่งทำหน้าที่เป็น ‘สเลฟ’ (**Slave**)&#x20;

แต่ถ้ามีอุปกรณ์ ‘สเลฟ’ มากกว่าหนึ่งตัว จะต้องมีสัญญาณควบคุมสำหรับอุปกรณ์แต่ละตัว เรียกว่า **Slave Select (SS)** หรือ **Chip Select (CS)** ซึ่งทำงานแบบ **Active-Low**

ในการทำงานของบัส **SPI** การรับส่งข้อมูลจะเกิดขึ้นพร้อมกัน เมื่อส่งข้อมูลทีละบิตออกไปทาง **MOSI** ก็จะได้ข้อมูลทีละบิตจาก **MISO** เข้ามา

ตัวอย่างโค้ดต่อไปนี้ สาธิตการเขียนข้อมูลไบต์ในอาร์เรย์และอ่านข้อมูลโดยใช้บัส **SPI** โดยเชื่อมต่อขา **MOSI** เข้ากับ **MISO** เข้าด้วยกัน ดังนั้นจึงเป็นการรับส่งข้อมูลแบบ **Loopback**

การเลือกใช้ขาสำหรับ **Hardware SPI** ของ **ESP32** จะใช้ขา **GPIO-14**, **GPIO-13** และ **GPIO-12** สำหรับ **SCK**, **MOSI** และ **MISO** ตามลำดับ ซึ่งตรงกับขาสำหรับบัส **HSPI** ของ **ESP32**

โหมดการทำงานของ **SPI** มี 4 โหมด ในกรณีนี้ได้เลือกใช้โหมด (**polarity=0, phase=0**) ความถี่ของสัญญาณ **SCK** กำหนดให้เป็น **10 MHz (baudrate)** ขนาดเฟรมข้อมูลเป็น 8 บิต ส่งข้อมูลบิตแบบ **MSB First**

โค้ดตัวอย่าง สาธิตการส่งข้อมูลคราวละ 5 ไบต์ โดยที่ 4 ไบต์แรกคือ `0xaa`, `0xbb`, `0xcc`, `0xdd` และไบต์สุดท้ายจะเป็นค่าของตัวนับขนาด 8 บิต

แต่ถ้าไม่มีการเชื่อมต่อระหว่างขา **MISO** และ **MOSI** เมื่อโค้ดทำงาน จะอ่านได้ค่า `0xff`, `0xff`, `0xff`, `0xff`, `0xff`, …

```python
from micropython import const
from machine import Pin, SPI
import utime

SCK  = const(14)
MOSI = const(13)
MISO = const(12)
hspi = SPI(1, baudrate=10000000, 
    polarity=0, phase=0, bits=8, firstbit=SPI.MSB,
    sck=Pin(SCK), mosi=Pin(MOSI), miso=Pin(MISO))
    
for i in range(256):
    rbuf = bytearray(5*[0]) # read buffer filled with zero
    wbuf = bytearray( [0xaa,0xbb,0xcc,0xdd, i] )
    hspi.write_readinto( wbuf, rbuf )
    for b in rbuf:
        print( hex(b), end=' ' )
    print('')
    
hspi.deinit() # disable the SPI bus
```

## โค้ดตัวอย่างที่ 14: UART Data Transmission

**ESP32** มีวงจร **UART (Universal Asynchronous Receiver/Transmitter)** สำหรับสื่อสารข้อมูลบิตแบบอนุกรม หรือที่เรียกว่า **Hardware UART** มีจำนวน 3 ชุด คือ **UART0**, **UART1** และ **UART2**&#x20;

แต่ **UART0** จะถูกใช้สำหรับ **MicroPython REPL** และเชื่อมต่อกับไอซี **USB-to-Serial** ไว้แล้ว (เช่น **CP2102 / CP2104** เป็นต้น) ดังนั้นถ้าเราต้องการจะส่งข้อมูลด้วย **UART** ก็สามารถเลือกใช้ **UART1** หรือ **UART2** แทนได้ เช่น เลือกใช้ขา **GPIO** สำหรับ **UARTx** ดังนี้ เหมือนในกรณีที่เขียนโค้ดด้วย **Arduino-ESP32** แต่จะเลือกขา **GPIO** อื่นได้เช่นกัน&#x20;

| UARTx | RX GPIO | TX GPIO | CTS   | RTS    |
| ----- | ------- | ------- | ----- | ------ |
| UART0 | GPIO3   | GPIO1   | N/A   | N/A    |
| UART1 | GPIO9   | GPIO10  | GPIO6 | GPIO11 |
| UART2 | GPIO16  | GPIO17  | GPIO8 | GPIO7  |

ในกรณีที่เราจะใช้ **UART** ส่งข้อมูลไปยังคอมพิวเตอร์ อุปกรณ์ที่จำเป็นต้องใช้ร่วมกันคือ โมดูล **USB-to-Serial** และให้เลือกใช้แรงดันลอจิกที่ **3.3V** เท่านั้น (ไม่ใช่ **5V**)

ตัวอย่างต่อไปนี้ สาธิตการใช้งาน **UART** โดยเลือกขา **TX / GPIO-2** (ทิศทางเอาต์พุตสำหรับ **ESP32**) และ **RX / GPIO-15** (ทิศทางอินพุตสำหรับ **ESP32**) ตามลำดับ และให้นำไปเชื่อมต่อกับขา **RXD** และ **TXD** ของโมดูล **USB-to-Serial** ตามลำดับ อีกทั้งให้ต่อขา **GND** ร่วมกันด้วย

เมื่อเปิดใช้งาน **UART** ให้ **id = 1** เลือกใช้ **Baudrate** เท่ากับ **115200** จากนั้นจะมีการส่งข้อความทีละบรรทัด ซึ่งได้จากการแปลงตัวเลขที่คำนวณได้จากฟังก์ชัน `math.sin()` โดยให้มีค่าอยู่ในช่วง -100 .. +100&#x20;

การส่งข้อความ จะใช้คำสั่ง `write()` ของไลบรารี `machine.UART`&#x20;

```python
from micropython import const
from machine import UART
import math

TX = const(2)  # use GPIO-2  for TXD
RX = const(15) # use GPIO-15 for RXD
id = 1
uart = UART(id, tx=TX, rx=RX,
            baudrate=115200, timeout=1000)
N = 512
A = 100
for i in range(N): # repeat N times
    x = A*(math.sin(2*math.pi*i/N))
    uart.write('{}\n'.format( round(x)) )
uart.deinit()
print('Done')
```

ถ้าใช้โปรแกรมอย่างเช่น **Arduino IDE** และเปิดใช้งาน **Serial Plotter** จะเห็นการแสดงข้อมูลในรูปของกราฟ

![](/files/-MKVZM4S3V22SHE_ojCd)

ถ้าจะลองเขียนโค้ดที่คอยรับข้อความทีละบรรทัดจาก **UART** แล้วส่งกลับไปยังคอมพิวเตอร์ เพื่อทดสอบการทำงานในลักษณะ **UART Loopback** ก็มีตัวอย่างดังนี้

```python
from micropython import const
from machine import UART
import math

TX = const(2)  # use GPIO-2  for TXD
RX = const(15) # use GPIO-15 for RXD
id = 2
uart = UART(id, tx=TX, rx=RX, 
            baudrate=115200, parity=None, stop=1,
            timeout=1000)
try:
    while True:
        line = uart.readline() # read next line
        if line: # if line is not None
            print( line.decode().strip() )
            uart.write( line ) # send back
except KeyboardInterrupt:
    pass
uart.deinit()
```

ลองเปรียบเทียบกับอีกโค้ดตัวอย่างหนึ่ง ซึ่งให้ผลการทำงานเหมือนกัน แต่ใช้คำสั่งของไลบรารี `machine.UART` ที่แตกต่างกันในการรับข้อมูล

```python
from micropython import const
from machine import UART
import math

TX = const(2)  # use GPIO-2  for TXD
RX = const(15) # use GPIO-15 for RXD
id = 2
uart = UART(id, tx=TX, rx=RX, 
            baudrate=115200, parity=None, stop=1)

BUF_SIZE = const(64)

try:
    # create buffer for UART RX
    buf = bytearray( BUF_SIZE )
    # set timeout to 100 msec
    uart.init(timeout=100)
    
    while True:
        n = uart.readinto( buf )
        if n: # check the number of bytes received
            line = buf[:n].decode()
            print( line.strip() ) # show line
            uart.write( line )    # send back
            
except KeyboardInterrupt:
    pass
    
uart.deinit()
```

## โค้ดตัวอย่างที่ 15: BH1750 I2C Light Sensor&#x20;

**ESP32** มีวงจรภายในที่สำหรับการสื่อสารข้อมูลด้วยบัส **I2C** จำนวน 2 ชุด (**Hardware I2C**) และจะต้องเลือกใช้ขา **SCL (Clock)** และ **SDA (Data)** ให้ถูกต้อง และการเขียนโค้ดด้วยไมโครไพธอน เราจะใช้คลาส `machine.I2C`

เริ่มต้นด้วยการต่ออุปกรณ์ที่ทำงานเป็น **I2C Slave** และให้ **ESP32** ทำหน้าที่เป็น **I2C Master** ในตัวอย่างนี้ เราจะใช้โมดูลเซ็นเซอร์แสง **BH1750** ซึ่งสามารถใช้แรงดันไฟเลี้ยง **3.3V** และ **GND** จากบอร์ด **ESP32** ได้ และเลือกใช้ขา **SCL / GPIO-22** และ **SDA / GPIO-21** ตามลำดับ

โค้ดต่อไปนี้ สาธิตการตรวจหาอุปกรณ์ที่เชื่อมต่อกับ **I2C Bus** โดยใช้ขา **SCL** และ **SDA** ตามที่กำหนดไว้ ถ้าตรวจพบอุปกรณ์ **BH1750** จะแสดงข้อความที่ระบุหมายเลขแอดเดรส **`0x23`**

โมดูล **GY302** เป็น **Breakout Board** สำหรับ **BH1750** ที่ได้เลือกมาใช้งาน มีขา **VCC, GND , SCL, SDA, ADDR** ตามลำดับ ถ้าไม่ต่อขา **ADDR** จะได้หมายเลขแอดเดรสเป็น **`0x23`** แต่ถ้าต่อขาดังกล่าวกับ **VCC** จะได้ **`0x5c`**

```python
import machine, utime

i2c_bus = 0 # use either I2C bus 0 or 1
i2c = machine.I2C( i2c_bus, freq=100000,
    scl=machine.Pin(22), sda=machine.Pin(21) )

devices = i2c.scan() # scan I2C devices

num_found = len(devices)
if num_found > 0:
    print('I2C device%s found: ' % ('s' if num_found > 1 else ''))
    cnt = 1
    for dev in devices:
        print('({}) addr: {}'.format(cnt, hex(dev)))
        cnt = cnt+1
    print('')
else:
    print('no I2C devices found')
```

ตัวอย่างถัดไปสาธิตการอ่านค่าความเข้มแสงจากโมดูล **BH1750** ซึ่งจะได้ค่าตัวเลขจำนวนเต็มในช่วง **0 .. 65535** (16 บิต)

```python
import machine, utime

i2c_bus = 0 # use bus 0 or 1
i2c = machine.I2C( i2c_bus, freq=100000,
    scl=machine.Pin(22), sda=machine.Pin(21) )

def bh1750_init(addr):
    try:
        # power on the BH1750
        i2c.writeto(addr, bytes([0x01]) )
        # reset the BH1750
        i2c.writeto(addr, bytes([0x07]) )
        utime.sleep_ms(200)
        # set mode to 1.0x high-resolution, continuous measurement
        i2c.writeto(addr, bytes([0x10]) )
        utime.sleep_ms(150)
        return True
    except OSError as ex:
        return False

def bh1750_read(addr):
    try:
        data  = i2c.readfrom(addr, 2) # read two bytes
        value = (data[0]<<8 | data[1])/(1.2)
        return value
    except OSError as ex:
        print('BH1750 reading error')
        return None

addr_found = i2c.scan()
bh1750_addr_list = []
for addr in [0x23,0x5c]:
    if addr in addr_found:
        if bh1750_init( addr ):
            bh1750_addr_list.append( addr )

text = 'Light ({0:02X}h) {1:>6.1f} lx'
try:
    while True:
        for addr in bh1750_addr_list:
            value = bh1750_read( addr )
            if value:
                print( text.format(addr,value) )
        utime.sleep_ms(500)
except KeyboardInterrupt:
    pass
print('Done')
```

![รูปภาพ: ตัวอย่างการต่อวงจรทดลองโมดูล GY-302 BH1750 จำนวน 2 ชุด](/files/-MKZO7P4r0OzwrerGTeA)

## โค้ดตัวอย่างที่ 16: TouchPad

ตัวอย่างถัดไปสาธิตการใช้งาน **TouchPad** สำหรับขา **GPIO** บางขาของ **ESP32** โดยได้เลือกใช้ขา **GPIO-4 (TOUCH0)** และ **GPIO-0 (TOUCH1)** จำนวน 2 ขา&#x20;

ในการตรวจสอบดูว่า มีการใช้นิ้วมือสัมผัสที่ลวดหรือแผ่นโลหะที่เชื่อมต่อกับขา **TouchPad** หรือไม่ ก็ใช้วิธีการอ่านค่าเหมือนกรณีแอนะล็อก-อินพุต และจะได้ค่าเป็นเลขจำนวนเต็มบวก ถ้ามีการสัมผัส จะได้ค่าตัวเลขค่อนข้างต่ำ ในโค้ดตัวอย่างได้กำหนดไว้ว่า ถ้าได้ค่าต่ำกว่า 200 แสดงว่า มีการสัมผัสด้วยปลายนิ้ว

โดยทั่วไป ขา **GPIO** ที่ใช้เป็น **TouchPad Input** ควรมีตัวต้านทาน **Pull-Up** เช่น 1 เมกกะโอห์ม ต่อเอาไว้ด้วย&#x20;

```python
from machine import TouchPad, Pin
import utime as time

THRESHOLD_LEVEL = 200

pins = [0,4] # use GPIO-0 and GPIO-4 as touchpad pins
touch_pads = [ TouchPad( Pin(p) ) for p in pins ]

stop = False
text = 'Touched, GPIO{}, value={}'

while not stop:
    for i,tp in enumerate(touch_pads):
        value = tp.read() # read touchpad input
        if value < THRESHOLD_LEVEL:
            pin = pins[i]
            print( text.format(pin,value) )
            stop = True
    time.sleep_ms(100)

print('Done')
```

## โค้ดตัวอย่างที่ 17: การใช้ OLED I2C Display

ตัวอย่างถัดไปสาธิตการใช้งานโมดูแสดงผลแบบ **OLED** โดยเชื่อมต่อกับบัส **I2C** ของ **ESP32** โมดูล OLED ที่คนทั่วไปนิยมนำมาใช้งานจะใช้ไอซีควบคุมคือ **SSD1306** และ **SH1106** ดังนั้นจึงต้องเลือกไลบรารีที่นำมาใช้งานสำหรับไมโครไพธอนให้ถูกต้อง&#x20;

เราสามารถดาวน์โหลดไฟล์ .py ซึ่งเป็นไลบรารีสำหรับไมโครไพธอนได้จาก

* [ssd1306.py](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py)
* [sh1106.py](https://github.com/robert-hh/SH1106/blob/master/sh1106.py)

โมดูล **OLED** อาจจะมีขนาดที่แตกต่างกันคือ ความกว้าง (**Width**) และความสูง (**Height**) เช่น **128 x 64** หรือ **128 x 32** เป็นต้น

{% hint style="info" %}
ในบางกรณีจะพบว่า โมดูล **OLED** นั้นใช้งานแบบบัส **SPI** ซึ่งจะมีจำนวนขามากกว่า และสิ่งที่ควรระวังคือ โมดูล **OLED** ที่ใช้วิธีการเชื่อมต่อแบบ **I2C** มีขา 4 ขา ได้แก่ **GND**, **VCC**, **SCL**, **SDA** ตามลำดับ แต่ก็อาจพบกรณีที่มีการเรียงขาแตกต่างไป ดังนั้นตรวจสอบให้แน่ใจก่อนต่อใช้งานวงจร
{% endhint %}

โค้ดสำหรับสาธิตการใช้งานโมดูล **SH1106-based OLED I2C 128x64 pixels (default address: 0x3C)**

```python
import machine, utime
from sh1106 import *
# https://github.com/robert-hh/SH1106/blob/master/sh1106.py
import time

i2c_bus = 0 # use either I2C bus 0 or 1
i2c = machine.I2C( i2c_bus, freq=400000,
    scl=machine.Pin(22), sda=machine.Pin(21) )

I2C_ADDR = 0x3C
W, H = 128,64
BLACK, WHITE = 0,1

# scan for I2C devices
if 0x3C not in i2c.scan():
    raise RuntimeError('OLED SH1106 not found!!!')

display = SH1106_I2C(W, H, i2c, addr=I2C_ADDR)
display.rotate(True)
# fill the entire display
display.fill( BLACK )
display.rect(0, 0, W, H, WHITE ) 
# write some text lines (using the default font)
xpos,ypos = 0, 4
text_lines = ["Hi!", "MicroPython", "ESP32"]
for line in text_lines:
    display.text( '{:^16s}'.format(line), xpos, ypos )
    ypos += 10
display.show() # update the display 
time.sleep_ms(1000)
display.poweroff()
print('Done')
```

&#x20;

![รูปภาพ: การทดลองใช้งานโมดูล OLED I2C 128x64 pixels](/files/-MQodTpTJNvkMAC4Z09U)

{% hint style="info" %}
**เผยแพร่ภายใต้ลิขสิทธิ์**\
**Attribution-ShareAlike 4.0 International (**[**CC BY-SA 4.0**](https://creativecommons.org/licenses/by-sa/4.0/)**)**
{% endhint %}
