# SAMD21 Code Examples

## โค้ดตัวอย่างที่ 1: LED Blink

เริ่มต้นด้วยการทำให้ **LED** บนบอร์ดกระพริบ (ขา **D13**) โดยใช้คำสั่งจาก [`digitalio`](https://circuitpython.readthedocs.io/en/latest/shared-bindings/digitalio/index.html) เพื่อใช้งานขา **GPIO** แบบดิจิทัล (**Digital I/O**)

```python
import digitalio
from board import *
import time

# use Onboard LED on the XIAO board
led = digitalio.DigitalInOut(D13) 
led.direction = digitalio.Direction.OUTPUT

try:
    while True: # press Ctrl+C to stop
        led.value = True
        time.sleep(0.1)
        led.value = False
        time.sleep(0.1)
except KeyboardInterrupt:
    pass
led.value = 0 # LED output level LOW
led.deinit()  # release the LED pin (D13)
print('Done')
```

ในการหน่วงเวลา ก็ใช้คำสั่ง [`time.sleep()`](https://circuitpython.readthedocs.io/en/latest/shared-bindings/time/#time.sleep) โดยระบุค่าเป็นตัวเลขที่มีหน่วยเป็นวินาที&#x20;

หรือจะเขียนโค้ดในรูปแบบอื่นที่ทำงานได้เหมือนกัน ดังนี้

```python
import digitalio
from board import *
import time

led = digitalio.DigitalInOut(D13)
led.direction = digitalio.Direction.OUTPUT

try:
    t = int(1000*time.monotonic()) # in msec
    while True: # press Ctrl+C to stop
        now = int(1000*time.monotonic()) # in msec
        # toggle LED every 100 msec
        if now - t >= 100:
            t = now
            led.value = not led.value
except KeyboardInterrupt:
    pass
led.value = 0
led.deinit()
print('Done')
```

ในตัวอย่างนี้ ได้ใช้คำสั่ง [`time.monotonic()`](https://circuitpython.readthedocs.io/en/latest/shared-bindings/time/#time.monotonic) ซึ่งจะได้ค่าเป็นเลขทศนิยมแบบ `float` นำมาคูณด้วย 1000 แล้วแปลงให้เป็นข้อมูลแบบ `int` ดังนั้นจึงได้ค่าตัวเลขในหน่วยเป็น msec และค่าที่อ่านได้นี้ จะใช้ในการเปรียบเทียบกับค่าเวลาที่ได้บันทึกไว้ แล้วตรวจสอบดูว่า เวลาผ่านไป 100 msec หรือไม่ ถ้าใช่ ให้สลับสถานะของขาเอาต์พุตหนึ่งครั้ง และอัปเดทค่าเวลาที่บันทึกไว้

หรือจะเขียนโค้ดโดยใช้คำสั่งของ [`pwmio`](https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html) เพื่อสร้างสัญญาณ **PWM** ให้มีความถี่ต่ำ เช่น **10Hz** และมีค่า **Duty Cycle** เท่ากับ 50% เพื่อใช้เป็นสัญญาณสำหรับ **LED** (ขา **D13**) ก็ได้เช่นกัน

```python
import pwmio
from board import *
import time

pwm = pwmio.PWMOut(pin=D13,duty_cycle=0,frequency=10)
pwm.duty_cycle = (2**15 - 1) # (16-bit value) -> 50%
try:
    while True:
        time.sleep(1.0)
except KeyboardInterrupt:
    pass
pwm.duty_cycle = 0 # set duty cycle to 0
pwm.deinit() # relase the PWM output pin
print('Done')
```

**ข้อสังเกต**: ถ้าใช้บอร์ด **SAMD21 M0-Mini** แทน **Seeeduino XIAO** ขา **D13 (Arduino Pin)** ตรงกับขา **PA17 (SAMD21 Pin)** ซึ่งไม่มีวงจร **LED Onboard** ดังนั้นจะต้องต่อวงจร **LED** ภายนอกเพิ่ม

## โค้ดตัวอย่างที่ 2: NeoPixel RGB LEDs

ตัวอย่างถัดไปสาธิตการใช้คำสั่งของไลบรารี [`neopixel_write`](https://circuitpython.readthedocs.io/en/latest/shared-bindings/neopixel_write/) เพื่อกำหนดสีให้โมดูล **NeoPixel (WS2812B) RGB LEDs**&#x20;

เลือกใช้ขา **D10** เป็นขาเอาต์พุต เพื่อนำไปต่อกับขา **DIN** ของโมดูล **NeoPixel** ซึ่งมีทั้งหมด **8** พิกเซล และกำหนดให้ทุกพิกเซลเป็นสีแดง เริ่มต้นด้วยความสว่างระดับ 255 แล้วค่อย ๆ ลดลงจนเป็น 0

```python
import board
import neopixel_write
import digitalio
import time

np_pin = digitalio.DigitalInOut(board.D10)
np_pin.direction = digitalio.Direction.OUTPUT
NUM_PIXELS = 8
for i in range(256):
    colors = bytearray(NUM_PIXELS*[0,255-i,0])  # GRB
    neopixel_write.neopixel_write(np_pin, colors)
    time.sleep(0.01)
np_pin.deinit() # release the output pin
```

หรือจะใช้คำสั่งของไลบรารี [`neopixel`](https://github.com/adafruit/Adafruit_CircuitPython_Neopixel) ([**API**](https://circuitpython.readthedocs.io/projects/neopixel/en/latest/api.html)) ก็ได้เช่นกัน ตามตัวอย่างโค้ดต่อไปนี้ และจะต้องใช้ไฟล์ `neopixel.mpy` และ `adafruit_pypixelbuf.mpy` ซึ่งมีอยู่ใน [**CircuiPython Library Bundle**](https://github.com/adafruit/Adafruit_CircuitPython_Bundle) และให้นำไฟล์ทั้งสองไปใส่ลงใน **Flash File Storage** ของ **CircuitPython** ภายใต้ **/lib**

```python
import board
import time
import neopixel

NUM_PIXELS = 8
# bpp=3 (3 bytes per pixel), pixel order GRB 
pixels = neopixel.NeoPixel(board.D10, NUM_PIXELS,
    bpp=3, pixel_order=neopixel.GRB, auto_write=False)

color = (255, 0, 0) # red, green, blue
pixels[0] = color
for i in range(NUM_PIXELS): # turn on (use red color)
    pixels[i] = color if i==0 else pixels[i-1]
    pixels.show()
    time.sleep(0.1)
    
pixels.fill( (0,255,0) ) # change all pixels to green
for i in range(101): # fade the RGB LEDS off
    pixels.brightness = (100-i)/100.0
    pixels.show()
    time.sleep(0.02)

time.sleep(2.0)
# turn off the Neopixel strip and release pin
pixels.deinit() 
del pixels
```

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

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

ตัวอย่างถัดไปสาธิตการใช้ไลบรารี [`adafruit_dht`](https://github.com/adafruit/Adafruit_CircuitPython_DHT)  ([**API**](https://circuitpython.readthedocs.io/projects/dht/en/latest/)) สำหรับอ่านค่าอุณหภูมิและความชื้นสัมพัทธ์ของโมดูล **DHT11 / DHT22** ถ้าจะใช้งานไลบรารีดังกล่าว จะต้องใช้ไฟล์ `adafruit_dht.mpy` ร่วมด้วย ซึ่งมีอยู่ใน [**CircuiPython Library Bundle**](https://github.com/adafruit/Adafruit_CircuitPython_Bundle)&#x20;

```python
import time
import board
from adafruit_dht import DHT22

dht = DHT22(pin=board.D9,use_pulseio=True)
try:
    text = '{:.1f} deg.C, {:.1f} %RH'
    while True: # press Ctrl+C to stop
        dht.measure() # perform measurement
        values = (dht.temperature, dht.humidity)
        print( text.format(*values) )
        time.sleep(2.0)
except KeyboardInterrupt:
    pass
dht.exit()
del dht
```

ในการต่อวงจรทดลอง ได้เลือกใช้ขา **D9** สำหรับต่อกับขา **DATA** ของโมดูล **DHT22** (ใช้แรงดันไฟเลี้ยง **+3.3V**)&#x20;

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

## โค้ดตัวอย่างที่ 4: SI7021 Sensor Reading

ตัวอย่างถัดไปสาธิตการใช้ไลบรารี [`adafruit_si7021`](https://github.com/adafruit/Adafruit_CircuitPython_SI7021) ([**API**](https://circuitpython.readthedocs.io/projects/si7021/en/latest/api.html)) เพื่ออ่านค่าอุณหภูมิและความชื้นสัมพัทธ์จากโมดูล **SI7021 (Address=0x40)** โดยใช้วิธีบัส **I2C** (ขาสัญญาณ **SDL/D5** และ **SDA/D4** สำหรับบอร์ด **Seeeduino XIAO**)&#x20;

ไลบรารีต้องใช้ร่วมกับไลบรารี [**Adafruit Bus Device**](https://github.com/adafruit/Adafruit_CircuitPython_BusDevice) ซึ่งมีอยู่ใน [**CircuiPython Library Bundle**](https://github.com/adafruit/Adafruit_CircuitPython_Bundle) และเราต้องใช้สองไฟล์นี้ร่วมด้วย

* &#x20;`adafruit_bus_device/i2c_device`&#x20;
* &#x20;`adafruit_bus_device/spi_device`&#x20;

```python
import time
from busio import I2C
from board import SCL, SDA
import adafruit_si7021 as si7021

SI7021_ADDR = 0x40
i2c = I2C(SCL, SDA,frequency=100000) # 100kHz speed

addr_found = []
if i2c.try_lock():
    addr_found = i2c.scan()
    print("I2C found:", [hex(addr) for addr in addr_found])
    i2c.unlock()
if SI7021_ADDR not in addr_found:
    print('SI7021 not found')

dev = si7021.SI7021(i2c,address=SI7021_ADDR)
text = '{:.1f} deg.C, {:.1f} %RH'
try:
    while True: # press Ctrl+C to stop
        values = (dev.temperature,dev.relative_humidity)
        print( text.format(*values) )
        time.sleep(1.0)
except OSError:
    print('Sensor reading error!')
except KeyboardInterrupt:
    pass
finally:
    i2c.deinit()
print('Done')

```

![รูปภาพ: การเขียนโค้ด CircuitPython อ่านค่าจาก SI7021 โดยใช้ Thonny IDE](/files/-MPvkb_ciOPQmGofy4x0)

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

## โค้ดตัวอย่างที่ 5: BME680 Sensor Reading

ตัวอย่างนี้สาธิตการใช้ไลบรารี [`adafruit_bme680`](https://github.com/adafruit/Adafruit_CircuitPython_BME680) ([**API**](https://circuitpython.readthedocs.io/projects/bme680/en/latest/api.html)) เพื่ออ่านค่าจากเซ็นเซอร์ **BME680 (address=0x77)** ผ่านทางบัส **I2C** แล้วนำค่าของอุณหภูมิ ความชื้นสัมพัทธ์ และความดันบรรยากาศ มาแสดงผลเป็นข้อความเอาต์พุต&#x20;

```python
import time
from busio import I2C
from board import SCL, SDA
import adafruit_bme680 as bme680

BME680_ADDR = 0x77
i2c = I2C(SCL, SDA,frequency=100000) # 100kHz speed

addr_found = []
if i2c.try_lock():
    addr_found = i2c.scan()
    print("I2C found:", [hex(addr) for addr in addr_found])
    i2c.unlock()
if BME680_ADDR not in addr_found:
    print('BME680 not found')

dev = bme680.Adafruit_BME680_I2C(i2c,address=BME680_ADDR)
text = '{:.1f} deg.C, {:.1f} %RH, {:.2f} hPa'
try:
    while True: # press Ctrl+C to stop
        t = dev.temperature       # deg.C
        h = dev.relative_humidity # %RH
        p = dev.pressure          # hPa
        print( text.format(t,h,p) )
        time.sleep(1.0)
except OSError:
    print('Sensor reading error!')
except KeyboardInterrupt:
    pass
finally:
    i2c.deinit()
print('Done')
```

## โค้ดตัวอย่างที่ 6: DHT22 + SI7021 + BME680

ตัวอย่างนี้สาธิตการอ่านค่าจากเซ็นเซอร์จำนวน 3 ชนิดที่แตกต่างกันซึ่งได้มีการสาธิตการใช้งานแต่ละโมดูลในตัวอย่างก่อนหน้านี้ไปแล้ว&#x20;

ในการรันโค้ด **CircuitPython** แนะนำให้ใช้ **Mu Editor** แทนการใช้งาน **Thonny IDE** เนื่องจากใช้หน่วยความจำน้อยกว่า และจะไม่เกิด **Runtime Memory Error** (**SAMD21** มีหน่วยความจำ **SRAM** ค่อนข้างน้อย)

```python
import time
import gc
from busio import I2C
from board import SCL, SDA, D9

SI7021_ADDR = 0x40
BME680_ADDR = 0x77
i2c = I2C(SCL, SDA)

addr_found = []
if i2c.try_lock():
    addr_found = i2c.scan()
    print("I2C found:", [hex(addr) for addr in addr_found])
    i2c.unlock()
if BME680_ADDR not in addr_found:
    print('BME680 not found')
if SI7021_ADDR not in addr_found:
    print('SI7021 not found')

def read_si7021(dev):
    text = 'SI7021: {:.1f} deg.C, {:.1f} %RH'
    t = dev.temperature
    h = dev.relative_humidity
    print( text.format(t,h) )

def read_bme680(dev):
    text = 'BME680: {:.1f} deg.C, {:.1f} %RH, {:.2f} hPa'
    t = dev.temperature # deg.C
    h = dev.relative_humidity # %RH
    p = dev.pressure          # hPa
    print( text.format(t,h,p) )

def read_dht22(dev):
    text = 'DHT22 : {:.1f} deg.C, {:.1f} %RH'
    dev.measure()
    values = (dev.temperature, dev.humidity)
    print( text.format(*values) )

try:
    while True: # press Ctrl+C to stop
        ## read DHT22
        import adafruit_dht as dht
        dev = dht.DHT22(pin=D9,use_pulseio=True)
        read_dht22(dev)
        dev.pulse_in.deinit()
        del dev
        gc.collect() # call garbage collector
        time.sleep(1.0)

        ## read SI7021
        import adafruit_si7021 as si7021
        dev = si7021.SI7021(i2c,address=SI7021_ADDR)
        read_si7021(dev)
        del dev
        gc.collect() # call garbage collector
        time.sleep(1.0)
        
        ## read BME680
        import adafruit_bme680 as bme680
        dev = bme680.Adafruit_BME680_I2C(i2c,address=BME680_ADDR)
        read_bme680(dev)
        del dev
        gc.collect()  # call garbage collector
        time.sleep(1.0)
        print(50*'-')

except OSError:
    print('Sensor reading error!')
except KeyboardInterrupt:
    pass
finally:
    i2c.deinit()
print('Done')
```

![รูปภาพ: ตัวอย่างการรันโค้ดด้วย Mu Editor](/files/-MPwR_UzU5oLUF2WFBO1)

![รูปภาพ: ตัวอย่างการต่อวงจรทดลองบนเบรดบอร์ด](/files/-MPwdx67PFbJXbT6hrjN)

## โค้ดตัวอย่างที่ 7: Analog Key Switches

ตัวอย่างนี้สาธิตการอ่านค่าสถานะจากปุ่มกดแบบแอนะล็อก โดยใช้คำสั่งของไลบรารี `analogio` ของ **CircuitPython** แล้วนำมาตรวจสอบค่าเพื่อดูว่า ตรงกับการกดปุ่มในกรณีใด ซึ่งมีทั้งหมด 5 ปุ่ม (**SW1, ..., SW5**) การกดปุ่มใดปุ่มหนึ่งจะทำให้ค่าที่อ่านได้จากขาแอนะล็อกอินพุตไม่เท่ากันในแต่ละกรณี ข้อมูลที่อ่านได้จากการใช้คำสั่งของ **CircuitPython** จะมีขนาด 16 บิต (0..65535) แต่ในตัวอย่างนี้ ข้อมูลตัวเลขนี้ จะถูกสเกลค่าให้ลดลงเป็น 10 บิต หรือ 0..1023

```python
import time     
import board
from analogio import AnalogIn
from microcontroller import pin

## pin map for Seeeduino-XIAO
## A0=PA02, A1=PA04, A2=PA10, A3=PA11, A4=PA08, A5=PA09,
## A6=PB08, A7=PA09, A8=PA07, A9=PA05, A10=PA06

## For Seeeduino XIAO: A1 and PA04 are the same pin.
ain = AnalogIn(board.A1) # use A1 pin
#ain = AnalogIn(pin.PA04) # use PA04 pin

last_btn = 0
NUM_SAMPLES = 5
last_t = int(1000*time.monotonic())
try:
    while True:
        now = int(1000*time.monotonic())
        if now - last_t >= 50:
            last_t = now
        else:
            continue
        values = []
        for i in range(NUM_SAMPLES):
            # read a 16-bit value from the analog pin 
            value = ain.value >> 6 # reduce to 10 bits
            values.append(value) 
        # apply the median filter
        value = sorted(values)[NUM_SAMPLES//2]
        if value > 600: # no button press
            last_btn = 0
            continue
        btn = 0
        if value < 30:
            btn = 1
        elif value < 60:
            btn = 2
        elif value < 120:
            btn = 3
        elif value < 240:
            btn = 4
        elif value > 320 and value < 450:
            btn = 5
        if last_btn != btn and btn > 0:
            print( 'SW{} ({})'.format(btn, value) )
        last_btn = btn
except KeyboardInterrupt:
    pass
ain.deinit() # release the analog pin
print('Done')
```

โมดูลนี้มีขา **GND**, **VCC**, **OUT** ตามลำดับ

* **GND** ต่อกับ **GND** ของบอร์ด **XIAO**
* **VCC** ต่อกับ **+3.3V** ของบอร์ด **XIAO**
* **OUT** ต่อกับขา **A1/PA04** ของบอร์ด **XIAO**

![รูปภาพ: อุปกรณ์สำหรับการทดสอบการทำงานของโค้ด](/files/-MPxCVh8--nBoMmCOubb)

![รูปภาพ: ตำแหน่งขาของ XIAO (Pin Map)](/files/-MPyQy_7C82cqcCSDElD)

## โค้ดตัวอย่างที่ 8: PWM-based LED Fading

โค้ดสาธิตการสร้างสัญญาณ **PWM** ที่มีความถี่คงที่ เช่น **250Hz** แต่ปรับค่า **Duty Cycle** ได้ เพื่อกำหนดความสว่างของ **LED** โดยใช้คำสั่งในไลบรารี `pwmio`&#x20;

ในตัวอย่างนี้ได้เลือกใช้ขา **PA18** และ **PA19** สำหรับ **LED** จำนวน 2 ดวง (ทำงานแบบ **Active-Low**)&#x20;

ถ้าใช้บอร์ด **XIAO** ขา **PA18** และ **PA19** ต่อกับวงจร **LED** สีน้ำเงินที่มีอยู่แล้วบนบอร์ด

```python
import time
import pwmio
import board
from microcontroller import pin
import math

led_pins = [pin.PA18, pin.PA19]
pwm_objects = [
    pwmio.PWMOut(pin=pin,duty_cycle=65535,frequency=250)
    for pin in led_pins ]

# create a table of (N+1) sine wave values
N=64
sine_int = lambda i: int(65535*(1-math.sin(math.pi*i/N)))
values = [sine_int(i) for i in range(N+1)]

try:
    while True:
        for pwm in pwm_objects:
            for i in range(N+1):
                pwm.duty_cycle = values[i]
                time.sleep(0.04)
except KeyboardInterrupt:
    pass
for pwm in pwm_objects:
    pwm.deinit()
print('Done')
```

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

ตัวอย่างนี้สาธิตการใช้คำสั่งของไลบรารี `busio` ([**API**](https://circuitpython.readthedocs.io/en/latest/shared-bindings/busio/)) ที่เกี่ยวข้องกับการรับส่งข้อมูลผ่าน **UART** และทดสอบการส่งข้อมูลซึ่งเป็นข้อความทีละบรรทัด ออกทางขา **TX** แล้วรับข้อมูลกลับเข้ามาที่ขา **RX** ในลักษณะ **Loopback** (ต้องใช้สาย **Jumper Wire** เขื่อมต่อขา **TX** ไปยัง **RX)** และเลือกใช้ค่า **Baudrate** เท่ากับ **115200**

ถ้าทดสอบการทำงานกับบอร์ด **XIAO** ขา **TX** และ **RX** คือ ขา **D6/PB08** และ **D7/PB09** ตามลำดับ

```python
import board
import busio

# UART loopback test: connect TX pin to RX pin

uart = busio.UART(board.TX, board.RX,
    bits=8, parity=None, stop=1, timeout=0.1,
    baudrate=115200)
    
for i in range(10): # repeat 10 times
    text = 'count: {}\n'.format(i)
    uart.write( bytes(text, 'utf-8') ) # send string
    data = uart.readline()             # receive string
    print( str(data,'utf-8').strip() )
uart.deinit()
```

## โค้ดตัวอย่างที่ 10: Laser Dust Sensor Reading

ตัวอย่างนี้สาธิตการอ่านค่าจากเซ็นเซอร์ประเภท **Particulate Matter Concentration Sensor (Laser Dust Sensor)** เช่น รุ่น **PMS7003 หรือ PMS9003M (Plantower)** สำหรับวัดปริมาณฝุ่น **PM1.0 / PM2.5 / PM10** หน่วยเป็น **µg/m^3** (ไมโครกรัม/ลูกบาศก์เมตร)&#x20;

โมดูล **PMS7003** ใช้แรงดันไฟเลี้ยง **+5V** (แต่มีระดับ **Logic Level +3.3V**) และเมื่อเริ่มทำงาน จะส่งข้อมูลจำนวน 32 ไบต์ ต่อการอ่านค่าหนึ่งครั้ง ทุก ๆ 1 วินาที โดยอัตโนมัติ (**Default Mode**) ออกทาง **Serial** และใช้ความเร็ว **Baudrate** **9600** ในการรับส่งข้อมูล

```python
import board
import busio
import time

def pms7003( uart ):
    if uart.in_waiting < 32:
        return None
    try:
        data = uart.read(32) # read the next 32 bytes
        if data[0] != 0x42 and data[1] != 0x4d:
            uart.read(512) # invalid start bytes, flush input data
            return None
    except OSError:
        return None

    data = data[2:] # skip the first two bytes
    # get the data length (in bytes)
    data_len = (data[0] << 8) | data[1]
    # Standard particle concentration (CF-1)
    pm1_0 = (data[2] << 8) | data[3] 
    pm2_5 = (data[4] << 8) | data[5] 
    pm10  = (data[6] << 8) | data[7]
    values = {}        
    values['std'] = (pm10, pm2_5, pm1_0)
    # Atmospheric particulate concentration
    pm1_0 = (data[8]  << 8) | data[9] 
    pm2_5 = (data[10] << 8) | data[11] 
    pm10  = (data[12] << 8) | data[13]
    values['atm'] = (pm10, pm2_5, pm1_0)
    version  = data[26]
    err_code = data[27]
    cksum_calculated = (data[28] << 8) | data[29]
    cksum_expected   = sum(data[:-2]) + sum([0x42,0x4d])
    if cksum_calculated != cksum_expected or err_code !=0:
        return None
    text = '{{"pm10":{}, "pm2.5":{}, "pm1.0":{}, "unit":"ug/m^3"}}'
    return text.format(*values['std'])

uart = busio.UART( board.TX, board.RX,
    bits=8, parity=None, stop=1,
    timeout=0.1, baudrate=9600)

try:
    uart.read(512) # flush input data from serial
except OSError:
    print('Serial error')

try:
    while True:
        result = pms7003(uart)
        if result:
            print(result)
            time.sleep(5.0)
except KeyboardInterrupt:
    pass
uart.deinit()
print('Done')
```

ตัวอย่างข้อความเอาต์พุตหนึ่งบรรทัด

`{"pm10":41, "pm2.5":36, "pm1.0":23, "unit":"ug/m^3"}`

![รูปภาพ: โมดูล PMS9003M และ PCB Adapter สำหรับสายเคเบิลเชื่อมต่อ](/files/-MQ-mRDqdeJaemE-7LJL)

การเชื่อมต่อกับโมดูล **PSM9003M** จะเชื่อมต่อผ่าน **PCB Adapter** ที่ด้านหนึ่งเชื่อมต่อกับโมดูลดังกล่าวด้วยสายเคเบิล (มาพร้อมกับโมดูล) และอีกด้านหนึ่งจะเป็น **Pin Header** แบบ 6 ขา (**2.54mm spacing**) และมีขาตามลำดับดังนี้

* VCC (+5V)
* GND
* SET (N.C.)
* RXD (เชื่อมต่อกับขา TX ของบอร์ด XIAO)
* TXD (เชื่อมต่อกับขา RX ของบอร์ด XIAO)
* RST (ให้เชื่อมต่อแบบ Pull-up ไปยัง 3.3V)

![รูปภาพ: การทดสอบการทำงานของโค้ดตัวอย่าง (โมดูล PMS9003M)](/files/-MPz6rNG_x8r-MwhO5ye)

โค้ดตัวอย่างนี้ สามารถใช้ได้กับโมดูล **PMS7003** ของ **Plantower** ได้เช่นกัน

![รูปภาพ: การทดสอบการทำงานของโค้ดตัวอย่าง (โมดูล PMS7003)](/files/-MQ-yKm6MNDvfiWA0ez6)

## โค้ดตัวอย่างที่ 11: SHT31-D Sensor Reading

ตัวอย่างนี้สาธิตการอ่านค่าจากโมดูล **SHT31-D** ซึ่งเป็นเซ็นเซอร์วัดอุณหภูมิและความชื้นสัมพัทธ์ และใช้วิธีสื่อสารข้อมูลด้วย **I2C** อุปกรณีนี้มีหมายเลขแอดเดรสเท่ากับ `0x44`

การเขียนโค้ดเพื่ออ่านค่าจาก **SHT31-D** ก็ทำได้ง่าย เนื่องจากมีไลบรารีให้ใช้งานคือ [`adafruit_sht31d`](https://github.com/adafruit/Adafruit_CircuitPython_SHT31D) ([**API**](https://circuitpython.readthedocs.io/projects/sht31d/en/latest/api.html)) ดังนั้นถ้าจะรันโค้ดตัวอย่าง จะต้องนำไฟล์ `adafruit_sht31d.mpy` และไฟล์ของ **Adafruit Bus Device** ไปใส่ลงในไดเรกทอรี **/lib** ภายใน **Flash File Storage** ของ **CircuitPython** ด้วย

```python
import time
from busio import I2C
import board
from microcontroller import pin
import adafruit_sht31d

SHT31D_ADDR = 0x44
## 1) use board pins or
#scl_pin, sda_pin = board.SCL, board.SDA 
## 2) use microcontroller pins
scl_pin, sda_pin = pin.PA23, pin.PA22

i2c = I2C(scl_pin, sda_pin, frequency=100000)

addr_found = []
if i2c.try_lock():
    addr_found = i2c.scan() # scan I2C devices
    print("I2C found:", [hex(addr) for addr in addr_found])
    i2c.unlock()
if SHT31D_ADDR not in addr_found:
    print('SHT31D not found')
else:
    print('SHT31D found')

dev = adafruit_sht31d.SHT31D(i2c,address=SHT31D_ADDR)
text = '{:.1f} deg.C, {:.1f} %RH'
try:
    while True: # press Ctrl+C to stop
        values = (dev.temperature,dev.relative_humidity)
        print( text.format(*values) )
        time.sleep(2.0)
except OSError:
    print('Sensor reading error!')
except KeyboardInterrupt:
    pass
finally:
    i2c.deinit()
print('Done')
```

![รูปภาพ: การต่อวงจรทดลองใช้งาน SHT31D](/files/-MQ0Ui_N1d7PFL074aEI)

## โค้ดตัวอย่างที่ 12: Rotary Encode Input

ตัวอย่างนี้สาธิตการตรวจสอบการเปลี่ยนตำแหน่งของ **(Incremental)** **Rotary Encoder** ที่มีอินพุต-ดิจิทัล 2 ขา (**Channel A & B**) ซึ่งมีลักษณะเป็นสัญญาณแบบพัลส์ เมื่อมีการหมุนเปลี่ยนตำแหน่ง และนำไปต่อเข้าที่ขา `board.D0` และ `board.D1` ตามลำดับ

ในการตรวจสอบและอ่านค่าตำแหน่ง (**Postion**) จาก **Rotary Encoder** เราสามารถใช้คำสั่งของไลบรารี `rotaryio` ([**API**](https://circuitpython.readthedocs.io/en/latest/shared-bindings/rotaryio/index.html#module-rotaryio)) และใช้คลาส `IncrementalEncoder`&#x20;

การเปลี่ยนตำแหน่งดังกล่าว มีสองทิศทางที่เป็นไปได้คือ การหมุนทวนหรือการหมุนตามเข็มนาฬิกา และเราจะตรวจสอบทิศทางการหมุน เพื่อมาใช้ในการเพิ่มหรือลดระดับความสว่างของ **Neopixel** (แบบ **12 Pixels**) โดยเลือกใช้สีแดง และใช้ขา `board.D2` เป็นขาสัญญาณเอาต์พุต

**ข้อสังเกต:** ในตัวอย่างนี้จะต้องใช้ไฟล์ที่เป็นไลบรารีร่วมด้วยได้แก่ `neopixel.mpy` and `adafruit_pypixelbuf.mpy`&#x20;

```python
import time
from rotaryio import IncrementalEncoder
from board import *
import neopixel

NUM_PIXELS = 12
# bpp=3 (3 bytes per pixel), pixel order GRB 
pixels = neopixel.NeoPixel(D2, NUM_PIXELS,
    bpp=3, pixel_order=neopixel.GRB, auto_write=False)

enc = IncrementalEncoder(D0, D1)
last_position = enc.position
try:
    level = 127 # brightness level
    while True:
        # read current position
        position = enc.position
        if position != last_position:
            delta = position - last_position
            level = (level + 16*delta)
            level = min(255,level)
            level = max(0,level)
            pixels.fill( (level,0,0) )
            pixels.show()
            print('pos={}, level={}'.format(position,level))
        last_position = position
        time.sleep(0.01)
except KeyboardInterrupt:
    pass
enc.deinit()
pixels.deinit()
print('Done')
```

![รูปภาพ: ตัวอย่างการต่อวงจร Rotary Encoder ](/files/-MQ6R8XBqbR0L4BsismK)

**ข้อสังเกต**: ในการต่อวงจรบนเบรดบอร์ดเพื่อใช้งานโมดูล **Rotary Encoder** และ **NeoPixel** ได้ใช้แรงดันไฟเลี้ยง +**3.3V** จากโมดูล **Voltage Regulator (LM1117-3.3)** ที่แปลง **+5V (VUSB)** ให้เป็น **+3.3V**&#x20;

## กล่าวสรุป

เนื้อหาในส่วนนี้ได้นำเสนอตัวอย่างการเขียนโค้ด **CircuitPython** สำหรับไมโครคอนโทรลเลอร์ **SAMD21** เช่น การเชื่อมต่อและอ่านค่าอินพุตจากโมดูลเซ็นเซอร์ประเภทต่าง ๆ&#x20;

{% 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/circuitpython/atsamd21-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.
