# 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](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPvoVjnSNw-7-YPcbge%2F-MPvt_NxiOOUreQtxnS5%2Fneopixel_xiao_circuitpython.png?alt=media\&token=109db3ea-4ee0-40e9-b77b-a686a3965523)

## โค้ดตัวอย่างที่ 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](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPvoVjnSNw-7-YPcbge%2F-MPvtnmoODD-mQ4DJFaw%2Fdht22_xiao_circuitpython.png?alt=media\&token=b51ecf0c-712b-4c0c-b5ee-bb082da17dcc)

## โค้ดตัวอย่างที่ 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](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPvj2CXSLY7XPTDI6ec%2F-MPvkb_ciOPQmGofy4x0%2Fthonny_samd21_circuitpython_si7021.png?alt=media\&token=ec87f777-faf9-4f35-8a39-074f714a5593)

![รูปภาพ: ตัวอย่างการต่อวงจรทดลอง SI7021](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPvoVjnSNw-7-YPcbge%2F-MPvtvdnrP9PWOlersHl%2Fsi7021_xiao_circuitpython.png?alt=media\&token=98062094-7ac0-45d2-bee5-da202e3ed8d3)

## โค้ดตัวอย่างที่ 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](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPwRY2_XfGXlkw-rVe-%2F-MPwR_UzU5oLUF2WFBO1%2Fmu_dht22_bme680_si7021.png?alt=media\&token=dcaaf0bf-c4a6-4ac7-9d82-08314d1d73ac)

![รูปภาพ: ตัวอย่างการต่อวงจรทดลองบนเบรดบอร์ด](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPwSPeC_wxyhbCSv_F_%2F-MPwdx67PFbJXbT6hrjN%2Fxiao_bme280_si7021_dht22.png?alt=media\&token=d4d76c00-2837-4f1e-b1ef-460e823eb834)

## โค้ดตัวอย่างที่ 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**

![รูปภาพ: อุปกรณ์สำหรับการทดสอบการทำงานของโค้ด](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPxAXzh2cMGy2Xlag4V%2F-MPxCVh8--nBoMmCOubb%2Fxiao-analog_key_switches.png?alt=media\&token=af09076e-ca40-47d9-a18e-537b8a567fd8)

![รูปภาพ: ตำแหน่งขาของ XIAO (Pin Map)](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPxEz3v6-jp9MTY94rR%2F-MPyQy_7C82cqcCSDElD%2Fxiao_connectors.png?alt=media\&token=7b1e5a6f-7899-4ee2-970b-357a8ec1585e)

## โค้ดตัวอย่างที่ 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 สำหรับสายเคเบิลเชื่อมต่อ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MQ-kWITwofdnmPCCYO7%2F-MQ-mRDqdeJaemE-7LJL%2Fpms9003M_with_adapter.png?alt=media\&token=208f4447-eb9e-4772-9132-1da7a9230993)

การเชื่อมต่อกับโมดูล **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)](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MPz5_jrnK_4LpBroD8r%2F-MPz6rNG_x8r-MwhO5ye%2Fxiao_pms7003_demo.png?alt=media\&token=52c0d4c3-2ef1-494b-9b12-5f365d1863aa)

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

![รูปภาพ: การทดสอบการทำงานของโค้ดตัวอย่าง (โมดูล PMS7003)](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MQ-rBW9eAuZGBVcEjwa%2F-MQ-yKm6MNDvfiWA0ez6%2Fxiao_pms7003_demo-2.png?alt=media\&token=ea6426eb-4e20-49e7-bd95-13795809ef32)

## โค้ดตัวอย่างที่ 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](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MQ0TuaWnhXErmJusPH7%2F-MQ0Ui_N1d7PFL074aEI%2Fm0-mini_sht31d_demo.png?alt=media\&token=4959b7a2-c7ac-4f26-83eb-f98ca4ff48ca)

## โค้ดตัวอย่างที่ 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 ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MQ6QIv6O2Z6fzREQ3uE%2F-MQ6R8XBqbR0L4BsismK%2Fxiao_circuitpython_rotaryio_neopixel.jpg?alt=media\&token=b3121c63-891f-4919-b860-1bbd9ec276b5)

**ข้อสังเกต**: ในการต่อวงจรบนเบรดบอร์ดเพื่อใช้งานโมดูล **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 %}
