# 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://think-embedded.gitbook.io/micropython/micropython-for-esp32/esp32-code-examples.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
