# Micro:bit Code Examples

การเรียนรู้จากตัวอย่างโค้ด ก็เป็นวิธีหนึ่งที่ช่วยทำให้รู้จักคำสั่งต่าง ๆ ของไมโครไพธอนสำหรับไมโครบิตได้ง่ายขึ้น  ลองมาดูตัวอย่างโค้ดที่สามารถนำไปทดลองกับบอร์ดไมโครบิตได้

{% hint style="info" %}
ตัวอย่างโค้ดไมโครไพธอนสำหรับไมโครบิตในเอกสารนี้ สามารถนำไปใช้ได้กับ [**MicroPython Online Editor** ](https://python.microbit.org/v/2.0)แต่ในบางกรณีแนะนำให้ลองใช้ซอฟต์แวร์ที่เป็น **Offline Editor** เช่น [**Mu Editor**](https://codewith.mu/en/download) หรือ [**Thonny IDE**](https://thonny.org/) เป็นต้น
{% endhint %}

## โค้ดตัวอย่างที่ 1: การแสดงข้อความและสัญลักษณ์บน 5x5 LED Display

การทำงานของโค้ดตัวอย่างนี้ เริ่มต้นด้วยการแสดงข้อความเป็นภาษาอังกฤษ **`"Hello, World!"`** ด้วยคำสั่ง [**`display.scroll()`**](https://microbit-micropython.readthedocs.io/en/latest/display.html#microbit.display.scroll) (ทำเพียงครั้งเดียว) เนื่องจากมีหลายตัวอักษร จึงใช้วิธีแสดงทีละตัว แล้วเลื่อนจากขวาไปซ้าย จนครบตัวอักษรสุดท้ายของข้อความ

ในการแสดงข้อความแบบเลื่อนไป โดยใช้คำสั่ง [**`display.scroll()`**](https://microbit-micropython.readthedocs.io/en/latest/display.html#microbit.display.scroll) เราสามารถกำหนดช่วงเวลาในการอัปเดตสกรีน **5x5 LED Display** ได้ด้วย เช่น **100** (หน่วยเป็นมิลลิวินาที) ถ้าต้องการให้เกิดการเปลี่ยนแปลงช้าลง ก็ให้เพิ่มค่าตัวเลข&#x20;

```python
from microbit import *
# display a text message
display.scroll('Hello, World!', 100 )
while True:
    # display the Heart icon (5x5 pixels)
    display.show(Image.HEART)
    # wait for 1 second before proceeding
    sleep(1000)
    # clear the display
    display.clear()
    sleep(1000)
```

ถัดไปมีประโยคคำสั่ง **while** เพื่อทำขั้นตอนซ้ำ ได้แก่ การแสดงรูปสัญลักษณ์ 'หัวใจ'  หรื&#xE2D;**`Image.HEART`** โดยใช้คำสั่ง [**`display.show()`**](https://microbit-micropython.readthedocs.io/en/latest/display.html#microbit.display.show) และให้เว้นช่วงเวลาประมาณ 1000 มิลลิวินาที โดยใช้คำสั่ง **`sleep()`** ตามด้วยคำสั่ง [**`display.clear()`**](https://microbit-micropython.readthedocs.io/en/latest/display.html#microbit.display.clear) เพื่อเคลียร์การแสดงผล

**ข้อสังเกต:** คำสั่งต่าง ๆ ที่เกี่ยวข้องกับไมโครไพธอนสำหรับไมโครบิต จะอยู่ภายใต้ชื่อของแพคเกจหรือโมดูลชื่อ **`microbit`** ดังนั้นในบรรทัดแรกของโค้ด จึงมีคำสั่ง **`from microbit import *`**&#x20;

ถ้าต้องการลองใช้รูปไอคอนของไมโครไพธอนที่ได้มีการกำหนดไว้แล้ว (**Built-in Images**) ก็สามารถดูได้จากเอกสารในหัวข้อ [**Images**](https://microbit-micropython.readthedocs.io/en/latest/tutorials/images.html) ตัวอย่างเช่น รูปกราฟิกแสดงอารมณ์ความรู้สึก

* **`Image.HAPPY`** (รู้สึกมีความสุข)
* **`Image.SMILE`** (รูปหน้ายิ้ม)
* **`Image.SAD`** (รู้สึกเศร้า)
* **`Image.CONFUSED`** (รู้สึกสับสน)
* **`Image.ANGRY`** (รู้สึกโกรธ)
* **`Image.ASLEEP`** (รู้สึกง่วงนอน)
* **`Image.SURPRISED`** (รู้สึกประหลาดใจ)

โค้ดต่อไปนี้ สาธิตการแสดงรูปสัญลักษณ์ โดยระบุไว้ในอาร์เรย์ (**Array**) หรือ **List** ในภาษาไพธอน และจะเลือกมาแสดงผลทีละรูปตามลำดับ แล้ววนซ้ำไปเรื่อย ๆ&#x20;

```python
from microbit import *

images_list = [ Image.HAPPY, Image.SMILE,
    Image.SAD, Image.CONFUSED, Image.ANGRY,
    Image.ASLEEP, Image.SURPRISED ]

while True:
    for image in images_list:
        display.show( image )
        sleep(1000)
        display.clear()
        sleep(1000)
```

เราสามารถกำหนดรูปกราฟิกขนาด 5x5 พิกเซลได้เอง (**User-defined Images**) สำหรับนำไปแสดงผลบน **LED Display** โดยใช้คำสั่ง **`Image()`** และ [**`display.show()`**](https://microbit-micropython.readthedocs.io/en/latest/display.html#microbit.display.show)

การใช้คำสั่ง **`Image()`** เป็นการกำหนดค่าพิกเซลขนาด 5x5  (หนึ่งพิกเซลสำหรับ **LED** หนึ่งดวง) มีค่าความสว่างได้ 0..9 (0=**LED OFF** และ 9=สว่างมากที่สุด) ในแต่ละแถว เรียงจากซ้ายไปขวา และเรียงจากแถวบนลงล่าง&#x20;

ยกตัวอย่าง เช่น ถ้าต้องการสร้างรูปกากบาท (**Cross Mark**) เราก็กำหนดรูปกราฟิกดังนี้

```python
from microbit import *

# define a cross-mark image 
image_x = Image("90009:09090:00900:09090:90009")

# display the image 
display.show( image_x )
```

![รูปภาพ: แสดงรูปกราฟิก X (Cross Mark)](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIMHI7WleskhaPkZAjK%2F-MIMMzqWvb6zwZAYOwPo%2Fmicrobit_cross_mark.jpg?alt=media\&token=8ab51766-5043-4986-ae92-7f00e5a5e5a7)

## โค้ดตัวอย่างที่ 2: การสุ่มเลข 1-6 เมื่อกดปุ่มหรือเขย่าบอร์ด

ถ้าต้องการทำให้บอร์ดไมโครบิต มีพฤติกรรมการทำงานเหมือน (**Electronic Dice**) เช่น เมื่อกดปุ่ม **A** หรือเขย่าบอร์ด (**Shaking**) จะทำให้เกิดการสุ่มเลข เหมือนการทอยลูกเต๋า จะเขียนโค้ดอย่างไร ?

โค้ดตัวอย่างนี้ เมื่อมีการกดปุ่ม **A** ในแต่ละครั้ง จะมีการสุ่มเลขที่เป็นจำนวนเต็ม (**Integer**) ที่จะได้ค่าอยู่ระหว่าง 1 ถึง 6 โดยใช้คำสั่ง [**`random.randint()`**](https://microbit-micropython.readthedocs.io/en/latest/random.html#random.randint) เก็บไว้ในตัวแปรชื่อ **`number`** แล้วจึงนำค่านี้ไปแสดงผล โดยเรียกใช้ฟังก์ชัน **`showRandomNumber()`** ที่ได้สร้างขึ้นเอง แต่ถ้ามีการกดปุ่ม **B** ก็จะเป็นการเคลียร์การแสดงผลบน **LED Display** ด้วยคำสั่ง [**`display.clear()`**](https://microbit-micropython.readthedocs.io/en/latest/display.html#microbit.display.clear)&#x20;

**ข้อสังเกต**: การใช้คำสั่งของ [**`random`**](https://microbit-micropython.readthedocs.io/en/latest/tutorials/random.html) เพื่อสุ่มเลขนั้น ไม่ใช่การสุ่มเลขที่แท้จริง (***True Random Number Generation***) แต่ใช้อัลกอริทึมทางคณิตศาสตร์ในการกำหนดตัวเลขในลำดับถัดไป (จึงเป็นแบบ ***Pseudo-random Number Generation***)&#x20;

```python
from microbit import *
import random
import time 

def showRandomNumber():
    # create a pseudo-random number between 1..6
    number = random.randint(1, 6)
    # display the random number
    display.show( number )

# read analog value from pin A0
seed = pin0.read_analog()
# set the random seed using P0 analog input value
random.seed( seed ) 

while True:
    if button_a.was_pressed():
        showRandomNumber()
    elif button_b.was_pressed():
        display.clear()
```

เมื่อโปรแกรมเริ่มทำงานในแต่ละครั้ง เราสามารถกำหนดค่าเริ่มต้น (หรือ **Seed Value**) โดยใช้คำสั่ง [**`random.seed()`**](https://microbit-micropython.readthedocs.io/en/latest/random.html#random.seed) ก่อนเริ่มใช้คำสั่งของ [**`random`**](https://microbit-micropython.readthedocs.io/en/latest/random.html) สำหรับการสุ่มตัวเลข ในตัวอย่างนี้ ได้ใช้ค่าเลขจำนวนเต็มที่อ่านได้จากขา **P0** แบบแอนะล็อกของบอร์ดไมโครบิต (แต่ที่ขา **P0** ไม่ได้ต่อใช้งาน) มาใช้เป็นค่าเริ่มต้น

การตรวจสอบเงื่อนไขไปตามลำดับนั้น เราได้ใช้ประโยคคำสั่งแบบ **if-elif** โดยมีเงื่อนไขในการตรวจสอบ&#x20;

* ดูว่า มีการกดปุ่ม **A** หรือไม่ ซึ่งเราจะใช้คำสั่ง [**`button_a.was_pressed()`**](https://microbit-challenges.readthedocs.io/en/latest/tutorials/buttons.html)&#x20;
* ถัดไปดูว่า มีการกดปุ่ม **B** หรือไม่ โดยใช้คำสั่ง [**`button_b.was_pressed()`**](https://microbit-challenges.readthedocs.io/en/latest/tutorials/buttons.html)

**คำถามสำหรับการทดลองโดยใช้ฮาร์ดแวร์:**&#x20;

ถ้าไม่ได้ต่อขา **P0** กับวงจรใด ๆ (ไม่มีการใช้งานกับวงจรภายนอก) และอ่านอินพุตแบบแอนะล็อกจากขา **P0** ของบอร์ดไมโครบิต จะได้ค่าที่แตกต่างกันไปหรือไม่ ?  ลองมาดูโค้ดตัวอย่าง เมื่อกดปุ่ม **A** แต่ละครั้ง จะอ่านค่าจากอินพุต **P0** เช่น ทั้งหมด 10 ครั้ง&#x20;

```python
from microbit import *

N = 10
while True:
    if button_a.was_pressed():
        value_strings = []
        for i in range(N):
            # read value from A0 pin 
            value = pin0.read_analog()
            # append the value as a string to the list
            value_strings.append( str( value ) ) 
        # show all values as a string and send to serial
        print( '>', ','.join( value_strings ), '\n' ) 
    sleep(100)
```

![รูปภาพ: ตัวอย่างการรับและแสดงข้อความผ่าน Serial ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MINHb-0hLLEut8nC5qn%2F-MINN9rdyo9zjvadLhUW%2Fmicropython_microbit_serial_output-1.png?alt=media\&token=02f204bc-6b51-4201-950f-a21dbf0df5fb)

เมื่ออัปโหลดโค้ดไปยังบอร์ด ให้กดปุ่ม **Open Serial** เพื่อรับค่าจากบอร์ด ซึ่งจะเป็นข้อความที่ถูกส่งกลับมายังคอมพิวเตอร์ โดยใช้คำสั่ง **`print()`** แต่ถ้าต้องการปิดการรับข้อมูลจาก **Serial** ให้กดปุ่ม **Close Serial**

ลองกดปุ่ม **A** หลายครั้ง โดยไม่ใช้นิ้วสัมผัสที่ขา **P0** ที่บริเวณ **Edge Connector** และอยู่ใกล้ปุ่ม **A**) และเปรียบเทียบกับการใช้นิ้วสัมผัส

โค้ดตัวอย่างถัดไป ได้เปลี่ยนจากการแสดงตัวเลข ให้เป็นการแสดงรูปกราฟิกแทน (ใช้จำนวนจุด หรือ **Dots** ที่มีจำนวนตามตัวเลขที่สุ่มได้) ในตัวอย่างนี้ เราสุ่มเลขที่มีค่าระหว่าง 1 ถึง 6 ไว้ในตัวแปร **`number`** แล้วนำค่าที่ได้นี้ (ลบออก 1) ไปใช้อ้างอิง **`Image`** จากตัวแปรแบบอาร์เรย์ **`image_list`**  ซึ่งเริ่มต้นนับที่ **`index`** เท่ากับ 0

ในการตรวจสอบเหตุการณ์หรือเงื่อนไข เพื่อดูว่า มีการเขย่าบอร์ดเกิดขึ้นหรือไม่ เราจะใช้คำสั่ง [**`accelerometer.is_gesture()`**](https://microbit-micropython.readthedocs.io/en/latest/accelerometer.html#microbit.accelerometer.was_gesture) ที่ระบุว่าเป็น "**shake**" ถ้ามีการเขย่าบอร์ด ให้ตรวจสอบดูเงื่อนไขถัดไปในการอัปเดตค่าสุ่มตัวเลขถัดไปคือ จะต้องเกิดขึ้นหลังจากครั้งที่แล้วอย่างน้อย 1000 มิลลิวินาที (**msec**)

โค้ดตัวอย่างมีการใช้คำสั่ง **`time.ticks_ms()`** เพื่ออ่านค่าเวลาของระบบ (หน่วยเป็น **msec**) และใช้คำสั่ง **`time.ticks_diff()`** เพื่อหาผลต่างระหว่างช่วงเวลา (**Time Difference**) ผลต่างของค่าตัวเลขเวลาในปัจจุบันกับค่าตัวเวลาในอดีตที่บันทึกเก็บไว้ (ผลต่างระหว่าง **`now_shake`** กับ **`last_shake`**) จะต้องมีค่ามากกว่า 1000 มิลลิวินาที

```python
from microbit import *
import random, time 

images_list = [
  Image("00000:00000:00900:00000:00000"), # Number=1
  Image("00000:00090:00000:09000:00000"), # Number=2
  Image("00009:00000:00900:00000:90000"), # Number=3
  Image("00000:09090:00000:09090:00000"), # Number=4
  Image("90009:00000:00900:00000:90009"), # Number=5
  Image("09090:00000:09090:00000:09090")] # Number=6

def showRandomNumber():
    # create a pseudo-random number between 1..6
    number = random.randint(1, 6)
    # select the image from list (indexing 0..5)
    image = images_list[ number-1 ]
    # display the selected image (random number)
    display.show( image )
    
last_shake = time.ticks_ms()
while True:
    if accelerometer.is_gesture("shake"):
        now_shake = time.ticks_ms()
        # check the difference timestamp >= 1000 msec
        if time.ticks_diff(now_shake,last_shake) >= 1000:
            last_shake = now_shake
            showRandomNumber()
```

![รูปภาพ: แสดงการสุ่มตัวเลข 5 (มี 5 จุด)](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIMVdPcK7rQmUMlDm1s%2F-MIMa7qqB5xFUQ0aW1fo%2Fmicrobit_5dots.jpg?alt=media\&token=d2bc8192-ec45-48d4-a236-1cb9d33319c8)

## โค้ดตัวอย่างที่ 3: สุ่มตำแหน่งเพื่อทำให้ LED สว่าง 5 จุด

ตัวอย่างถัดไปสาธิตการสุ่มหาตำแหน่ง **(x,y)** บน **LED Matrix Display** ซึ่งจะมีทั้งหมด 5 ตำแหน่งที่ทำให้ **LED** สว่าง (**ON**) โดยมีเงื่อนไขว่า ในแต่ละแถวแนวนอนหรือแนวตั้ง จะต้องไม่ซ้ำกัน&#x20;

ในการกำหนดสถานะ **ON/OFF** หรือกำหนดค่าความสว่าง (0..9) ให้ **LED** ในตำแหน่งหรือพิกัดที่ต้องการ เราจะใช้คำสั่ง [**`display.set_pixel()`**](https://microbit-micropython.readthedocs.io/en/latest/display.html#microbit.display.set_pixel)  การกดปุ่ม **B** จะเปลี่ยนโหมดการสุ่มตำแหน่งและอัปเดตไปเรื่อย ๆ (**Auto mode**) หรือ โหมดที่ต้องกดปุ่ม **A** เพื่อสุ่มตำแหน่งใหม่ในแต่ละครั้ง (**Manual Mode**) ในโค้ดตัวอย่างนี้ จะเห็นได้ว่า มีการสาธิตการใช้โครงสร้างข้อมูลในภาษาไพธอน อย่างเช่น เซต (**Set**) และอาร์เรย์สองมิติ (**Two-dimensional Array**)

```python
from microbit import *
import random

# create an n x n matrix with random elements from {0,1}
def create_matrix( n ):
    # create a matrix filled with zeros
    m = [n*[0] for i in range(n)] 
    # use a set to keep the row numbers already selected
    selected = set()
    
    # for each column of the matrix
    for col in range(n): 
        while True:
            # randomize a new row number
            row = random.randint(0,n-1)
            if row not in selected: 
               # if the row number is not in the set,
               # add the row number to the set
               selected.add( row )  
               # set the element at (row,col) to 1
               m[row][col] = 1 
               # stop the while loop
               break
    return m

# read analog value from pin A0
seed = pin0.read_analog()
# set the random seed using P0 analog input value
random.seed( seed )

N = 5
autorunning = True
while True:
    if button_b.was_pressed():
        autorunning = not autorunning
    if button_a.is_pressed() or autorunning:
        display.clear()
        pixels = create_matrix(N)
        for r in range(N): # for each row
          for c in range(N): # for each column
            display.set_pixel( c, r, 9*(pixels[r][c]) )
        sleep( 500 )
    else:
        sleep( 100 )
```

![รูปภาพ: การสุ่มตำแหน่งแสดงสถานะของ LED จำนวน 5 จุด](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIMhfZCJwhgfMjOwhXt%2F-MIMi2yObK8Ber3W7Sdr%2Fmicrobit_random_5dots.jpg?alt=media\&token=64c9338e-5790-49b5-aadc-16d6094d65bb)

## โค้ดตัวอย่างที่ 4: การเอียงบอร์ดเพื่อทำให้ LED สว่างทุกดวง

ตัวอย่างถัดไปสาธิตการทำให้ **LED** สว่าง ทีละดวง เริ่มต้นจากดวงแรกที่พิกัด (2,2) ซึ่งอยู่ตรงกลางบอร์ด และมีการกระพริบที่ **LED** เพื่อระบุตำแหน่งพิกัดขณะนั้น&#x20;

ถ้ามีการเอียงบอร์ดไปในทิศทางใด จะทำให้ตำแหน่งของ **LED** ที่กระพริบเปลี่ยนไป และ **LED** ในตำแหน่งก่อนหน้านั้น จะเปลี่ยนจาก **OFF** (0) เป็น **ON** (1) และจะเห็นว่า มีจำนวน **LED** ที่อยู่ในสถานะ **ON** เพิ่มมากขึ้น

เมื่อทำให้ **LED** ทุกดวงสว่างครบแล้ว (5\*5 = 25 ดวง) โดยการเอียงบอร์ดตามแกนอ้างอิง **x** หรือ **y** จะเห็นได้ว่า หลังจากนั้น **LED** ทุกดวงกระพริบ ถ้าต้องการจะเริ่มต้นใหม่ ให้กดปุ่ม **A**&#x20;

การตรวจสอบดูว่า มีการเอียงบอร์ดไปทางใด เราจะใช้คำสั่งของ [**`accelerometer`**](https://microbit-micropython.readthedocs.io/en/latest/accelerometer.html) เช่น [**`get_x()`**](https://microbit-micropython.readthedocs.io/en/latest/accelerometer.html#microbit.accelerometer.get_x) และ [**`get_y()`**](https://microbit-micropython.readthedocs.io/en/latest/accelerometer.html#microbit.accelerometer.get_y) อ่านค่าสำหรับแกน **x** และ **y**  ถ้าวางบอร์ดในแนวราบ ค่าที่อ่านได้สำหรับแกน **x** และ **y** จะเข้าใกล้ 0 แต่

ถ้ามีการเอียงบอร์ดไปทางด้านหนึ่ง ค่าจะเพิ่มเป็นบวกมากขึ้น แต่ถ้าเอียงไปอีกด้านหนึ่งค่าจะลดลง ได้ค่าตัวเลขเป็นลบ (น้อยกว่า 0)

```python
from microbit import *

LEVEL = 250
N = 5

def initialize():
    global leds, x, y, last_pos, count_ones 
    # create an array with N*N elements, filled with 0's
    leds = [ 0 for i in range(N*N) ]
    x,y = (N//2,N//2) # center position (x,y)
    last_pos = (x,y)
    leds[N*y+x] = 1   # the center LED is ON
    count_ones  = 1
    display.clear()   # clear display
    display.on()      # turn on display
    display.set_pixel(x,y,9) # the center LED is ON

sleep(1000)
initialize()

while True:
    if button_a.was_pressed():
        initialize()
        sleep(1000)

    a_x = accelerometer.get_x() # read x-value from accelerator
    a_y = accelerometer.get_y() # read y-value from accelerator
    
    if a_x > LEVEL:
        x = min( x + 1, N-1 )
    elif a_x < -LEVEL:
        x = max( x - 1, 0 )
        
    if a_y > LEVEL:
        y = min( y + 1, N-1 )
    elif a_y < -LEVEL:
        y = max( y - 1, 0 )

    if leds[N*y + x] == 0:
        leds[N*y + x] = 1
        count_ones = count_ones+1
 
    if last_pos != (x,y):
        last_x, last_y = last_pos
        if leds[N*last_y + last_x] == 1:
            display.set_pixel(last_x,last_y,9)
        last_pos = (x,y)
    else:
        if count_ones == N*N:
            if display.is_on():
                display.off()
            else:
                display.on()
        else: 
            display.set_pixel(x,y,9-display.get_pixel(x,y))
    sleep(150)
```

## โค้ดตัวอย่างที่ 5: การกำหนดสีให้โมดูล RGB LED (WS2812 NeoPixel)

โมดูล **RGB LED** ที่ใช้ชิป **WS2812B (SMD 5050)** สามารถโปรแกรมค่าสีแบบ **RGB (Red, Green, Blue)** ได้ โดยกำหนดค่าในช่วง **0..255** สำหรับแต่ละสี

เราสามารถใช้คำสั่งจากไลบรารี [**neopixel** ](https://microbit-micropython.readthedocs.io/en/latest/neopixel.html)สำหรับไมโครบิต โดยสามารถระบุได้ว่า จะใช้แถบ **RGB LED** จำนวนกี่ดวง หรือระบุตำแหน่งทั้งหมด ในตัวอย่างนี้ จะใช้เพียงหนึ่งตำแหน่ง (ดวงเดียว) โดยทำคำสั่ง **`NeoPixel(pin1, 1)`** และเลือกใช้ขา **P1** ต่อไปยังขาสัญญาณอินพุตควบคุมของโมดูล **NeoPixel** ที่มีเพียงหนึ่งพิกเซล **RGB** (หนึ่งตำแหน่ง)

```python
from microbit import *
from neopixel import *
import random 

np = NeoPixel( pin1, 1 )
values = [
    (0,0,0),(127,0,0),(0,127,0),(0,0,127),
    (127,127,0),(127,0,127),(0,127,127),(255,255,255) ]
while True:
    rgb = random.choice( values )
    np[0] = rgb  # set the RGB value (first position)
    np.show()    # update the RGB LED 
    sleep(1000)
```

การกำหนดค่าให้แต่ละตำแหน่งจะใช้ **Tuple** ที่มีข้อมูลตัวเลข 3 ตัว สำหรับ **(R,B,G)** ตามลำดับ เลือกสุ่มมาจากอาร์เรย์ **`values`** โดยใช้คำสั่ง [**`random.choice()`**](https://microbit-micropython.readthedocs.io/en/latest/random.html#random.choice)&#x20;

![รูปภาพ: การต่อโมดูล NeoPixel WS2812B (หนึ่งดวง) เข้ากับบอร์ดไมโครบิต](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MINoXwD0LsGgNiHvNoB%2F-MINsECBp6xlXnVSYHnS%2Fsingle_neopixel_microbit.jpg?alt=media\&token=75a14c5f-b0a4-47a5-a630-8634ece3df97)

จากรูปภาพจะเห็นได้ว่า มีการใช้โมดูลของ **Keyestudio** นำมาต่อขาสำหรับ **Edge Connector** และมีขาแบบ **Male Pin Headers**  การเชื่อมต่อทางไฟฟ้า ก็ใช้วิธีเสียบสายไฟ 3 เส้น เชื่อมต่อไปยังโมดูล **NeoPixel** (ควรตรวจสอบการต่อวงจรให้ถูกต้องก่อนเสียบสาย **USB** เพื่อจ่ายไฟให้บอร์ดไมโครบิต)

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

## โค้ดตัวอย่างที่ 6: การสร้างสัญญาณ PWM จำนวน 3 ช่องเอาต์พุต

ถัดไปเป็นการสาธิตการเขียนโค้ด เพื่อสร้างสัญญาณแบบ **PWM (Pulse Width Modulation)** ซึ่งเป็นสัญญาณรูปคลื่นสี่เหลี่ยมแบบมีคาบ **(Periodic Rectangular Waveform)** แต่สามารถปรับค่าความกว้างช่วงที่เป็น **High Pulse** (หรือเรียกว่า **Duty Cycle**) ได้ 0% ถึง 100%&#x20;

บอร์ดไมโครบิต สามารถสร้างสัญญาณ **PWM** ที่กำหนดค่า **Duty Cycle** ได้ มีขนาดความละเอียด **10 บิต** ซึ่งเป็นค่าในช่วง **0..1023 (Duty Cycle 0% .. 100%)** และสามารถกำหนดคาบสัญญาณได้เช่นกัน (ต่ำสุดคือ 256 ไมโครวินาที) ถ้าเพิ่มคาบสัญญาณให้กว้างขึ้น ก็จะได้สัญญาณ **PWM** ที่มีความถี่ต่ำลง

ในตัวอย่างนี้ เราจะใช้คำสั่ง [**`write_analog()`**](https://microbit-micropython.readthedocs.io/en/latest/pin.html#microbit.MicroBitAnalogDigitalPin.write_analog) สำหรับ [**Microbit Pin**](https://microbit-micropython.readthedocs.io/en/latest/pin.html) สร้างสัญญาณ **PWM** และกำหนดให้มีความถี่ **500 Hz** (มีคาบ **2 msec** หรือ **2000 usec**) จำนวน 3 ช่องสัญญาณ (ใช้ขา **P0**, **P1**, **P2**  เป็นเอาต์พุต) เพื่อนำไปขับวงจรหรือโมดูล **RGB LED** ทำให้เปลี่ยนสีได้

```python
from microbit import *
import math

pins = [pin0, pin1, pin2] # pin list for output 

N = 64 # N+1 = the number of values in the table
# create a table of values using the cos() function
sin_table = []
for i in range(0,N+1):
    value = 1+math.cos(2*math.pi*i/N)
    value = math.floor( value*512 )
    sin_table.append( min(1023,value) )

# set the period of the PWM signal (2msec)
for pin in pins:
  pin.set_analog_period_microseconds(2000)
    
while True:
  for pin in pins:
    for value in sin_table:
      # set PWM duty cycle 0..1023 (10-bit)
      pin.write_analog( value )
      sleep(50)
    sleep(200)
    
```

สัญญาณ **PWM** จำนวน 3 ช่อง จะถูกใช้ในการกำหนดค่าสีของโมดูล **RGB LED** นั้น ในโค้ดตัวอย่างได้กำหนดให้ค่า **Duty Cycle** ของแต่ละช่องสัญญาณเพิ่มขึ้นหรือลดลงตามรูปแบบของฟังก์ชัน **cosine** แบ่งเป็นทั้งหมด **N=64** ระดับ และสเกลค่าให้อยู่ในช่วง **0..1023**

**ข้อสังเกต**: โมดูล **RGB LED** ที่ได้เลือกนำมาใช้งานนั้น มีขา **Pin Headers** ได้แก่ **V (VCC)**, **R (Red)**, **B (Blue)**, **G (Green)** ตามลำดับ และทำงานแบบ **Active-Low**&#x20;

![รูปภาพ: การต่อวงจรเพื่อใช้งานโมดูล RGB LED](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIOQ1On50QSKyhNZrr-%2F-MIOQjjyW8Got_7r7DJ0%2Fmicrobit_rgb_module.jpg?alt=media\&token=822e46ea-ea97-4bfd-b0d8-4ec10949dde2)

## โค้ดตัวอย่างที่ 7: การอ่านค่าจากขาแอนะล็อกอินพุต

ถัดไปเป็นตัวอย่างการอ่านค่าจากขา **P0** ที่สามารถใช้เป็นขาอินพุตแอนะล็อกได้ โดยนำมาใช้อ่านค่าจากโมดูล **Key-Switch** แบบแอนะล็อก (หรือ **ADKeypad**) หลักการทำงานของโมดูลประเภทนี้คือ เมื่อป้อนแรงดันไฟเลี้ยง **VCC** กับ **GND** แล้ว จะได้แรงดันไฟฟ้าที่ขาสัญญาณเอาต์พุตที่ขึ้นอยู่กับสถานะการกดปุ่ม (โดยทั่วไป ก็จะกดเพียงปุ่มเดียวในแต่ละช่วงเวลา)&#x20;

```python
from microbit import *
import time 

analog_pin = pin0
N = 7
TP_MSEC = 50
ts_prev = running_time()
last_btn = 0

while True:
  ts_now = running_time()
  if time.ticks_diff(ts_now, ts_prev) >= TP_MSEC:
    ts_prev = ts_now
    values = []
    for i in range(N): # repeat N times
       # read the value from the analog pin P0 
       value = analog_pin.read_analog()
       values.append(value)
    # apply the median filter
    value = sorted(values)[N//2]
    if value > 600: # no button press
      last_btn = 0
      continue
    btn = 0
    if value < 25:
      btn = 1
    elif value < 50:
      btn = 2
    elif value < 100:
      btn = 3
    elif value < 150:
      btn = 4
    elif value > 250 and value < 300:
      btn = 5
    if last_btn != btn and btn > 0:
      print( 'SW{} {}'.format(btn, value) )
      display.show( btn, 10 )
    last_btn = btn
```

![รูปภาพ: ข้อความที่ได้รับทาง Serial แสดงสถานะการกดปุ่มที่เกิดขึ้น](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIOSVzFQWXQrUVhxWmj%2F-MIOft7TALdb7DRrX3Pv%2Fmicropython_microbit_read_keys_analog.png?alt=media\&token=ec2079bc-b609-431b-b97a-9bb35bc977a9)

โมดูลที่ได้เลือกมาใช้งาน มีทั้งหมด 5 ปุ่ม มีข้อความเขียนกำกับเอาไว้คือ **SW1**, **SW2**, ..., **SW5** ถ้าอ่านค่าจากขา **P0** แบบแอนะล็อก โดยใช้คำสั่ง [**`read_analog()`**](https://microbit-micropython.readthedocs.io/en/latest/pin.html#microbit.MicroBitAnalogDigitalPin.read_analog) จะได้ค่าในช่วง **0..1023** ซึ่งก็ขึ้นอยู่กับสถานะการกดปุ่ม และนำมาใช้ในการจำแนกหรือตรวจสอบดูว่า ปุ่มใดกำลังถูกกดอยู่ในขณะนั้น&#x20;

ให้ทดลองอ่านค่าแล้วส่งออกมาทาง **Serial** ด้วยคำสั่ง **`print()`** และทำซ้ำไปเรื่อย ๆ แล้วกดไปทีละปุ่ม เราจะเห็นค่าอินพุตที่อ่านได้สำหรับแต่ละกรณี ยกตัวอย่างเช่น ถ้ายังไม่กดปุ่มใด ๆ เลย ค่าที่อ่านได้จะอยู่ในช่วงประมาณ 800 ถึง 850 เป็นต้น

![รูปภาพ: การต่อวงจรสำหรับโมดูล Analog Key Switches](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIOSVzFQWXQrUVhxWmj%2F-MIOhuEfo_7oXX_fQNn3%2Fmicrobit_analog_key_button_module.jpg?alt=media\&token=749486bc-9fe6-4945-afc1-359bc69bbfcc)

## โค้ดตัวอย่างที่ 8: การตรวจสอบการใช้นิ้วสัมผัสที่ Touch Pad

ตัวอย่างนี้สาธิตการตรวจสอบดูว่า มีการใช้นิ้วสัมผัสที่บริเวณ **Touch Pad 0** ของบอร์ดไมโครบิตหรือไม่ (และให้ใช้อีกนิ้วหนึ่ง สัมผัสที่ **Pad GND** ของบอร์ด) โดยใช้คำสั่ง **`pin0.is_touched()`** ซึ่งจะได้ค่าแบบ **`boolean`**

ถ้ามีการสัมผัสด้วยนิ้วในแต่ละครั้งที่ขา **P0** จะทำให้สถานะของ **LED Display** เปลี่ยนไป ซึ่งมีอยู่ 4 รูปแบบ หรือ ระดับ ดังนี้

* ระดับที่ 1 เริ่มต้น ไม่มี **LED** อยู่ในสถานะ **ON**&#x20;
* ระดับที่ 2 มี **LED** เพียง 1ดวง (ตรงกลาง) ในสถานะ **ON**&#x20;
* ระดับที่ 3 มี **LED** จำนวน 3x3 ดวงในสถานะ **ON**&#x20;
* ระดับที่ 4 มี **LED** จำนวน 5x5 ดวง (ทุกดวง) ในสถานะ **ON**&#x20;

```python
from microbit import *

LED_PATTERNS = [
    Image("00000:00000:00900:00000:00000"),
    Image("00000:09990:09990:09990:00000"),
    Image("99999:99999:99999:99999:99999") ]

display.on()
display.clear()
level = 0

print('Please touch Pad 0 with one finger')
print('and use another finger to touch pad GND.')

while True:
    if pin0.is_touched():
        while pin0.is_touched():
            sleep(10)
        if level == 0:
            display.off()
        else:
            display.on()
            display.show( LED_PATTERNS[level-1] )       
            print('Display on')
        level = (level+1) % 4      
```

## โค้ดตัวอย่างที่ 9: การตรวจสอบการเชื่อมต่ออุปกรณ์ I2C Slave

บอร์ดไมโครบิต สามารถเชื่อมต่อกับอุปกรณ์อื่นในระบบบัส [**I2C**](https://tech.microbit.org/hardware/i2c/) ที่ใช้สายสัญาณเพียง 2 เส้นคือ **SDA (Serial Data Line)** และ **SCL (Serial Clock Line)** ไมโครบิตจะทำหน้าที่เป็นอุปกรณ์ **I2C Master** ซึ่งเป็นฝ่ายเริ่มต้น หรือควบคุมการทำงานของอุปกรณ์อีกฝ่ายหนึ่งคือ **I2C Slave** เช่น ต้องการจะเขียน **(Write Operation)** หรืออ่านข้อมูล **(Read Operation)** เป็นต้น  นอกจากนั้นจะต้องมีการระบุแอดเดรสขนาด 7 บิต (**7-bit Slave Address**) ของอุปกรณ์แต่ละตัวในระบบบัสเดียวกัน

การเขียนโค้ดเพื่อใช้งานบัส **I2C** ก็มีคำสั่งในกลุ่ม [**i2c**](https://microbit-micropython.readthedocs.io/en/latest/i2c.html) ไว้ให้ใช้งาน โดยทั่วไป เราก็ใช้ความเร็ว **100kHz** หรือ **400kHz** ในการสื่อสารสำหรับบัส **I2C**&#x20;

บอร์ดไมโครบิตมีวงจรหรือไอซีที่เชื่อมต่อด้วยบัส **I2C** อยู่แล้ว ได้แก่ ไอซีวัดความเร่ง **(Accelerometer IC)** และไอซีเข็มทิศดิจิทัล **(Digital Compass IC)** เป็นต้น  แต่ถ้าเราจะนำอุปกรณ์ **I2C Slave** ตัวอื่น มาต่อเพิ่ม ก็ให้เลือกใช้ขา **Pin 20** และ **Pin 19** ของบอร์ดไมโครบิต เป็นขาสัญญาณ **SDA** และ **SCL** ตามลำดับ และอุปกรณ์เหล่านั้นจะต้องทำงานที่แรงดันไฟฟ้า **+3.3V** เช่นกัน

ลองมาดูตัวอย่างการเขียนโค้ด เพื่อตรวจสอบหรือสแกนอุปกรณ์ (**Device** **Scan**) และแสดงหมายเลขแอดเดรส (ฐานสิบหก) ของอุปกรณ์ **Slave Device** ในระบบบัส **I2C** ของบอร์ดไมโครบิต ส่งเป็นข้อความออกทาง **Serial** โดยแบ่งเป็นสองวิธี

1. สร้างฟังก์ชัน **`scan_i2c()`** ที่เราสร้างขึ้นเองแล้วเรียกใช้ หรือ
2. เรียกใช้ฟังก์ชัน [**`i2c.scan()`**](https://microbit-micropython.readthedocs.io/en/latest/i2c.html#microbit.i2c.scan) ซึ่งมีไว้ให้แล้ว

```python
from microbit import *

def scan_i2c():
    dev_list = []
    for addr in range(0x01, 0x7f):
        try:
           # try to read one byte
           i2c.read(addr, 1)
        except OSError:
            pass
        else:
            dev_list.append( addr )
    return dev_list

i2c.init( freq=100000, sda=pin20, scl=pin19)

while True:
    print("Scanning I2C bus... Method 1")
    # Method 1
    dev_list = scan_i2c()
    for dev in dev_list:
        print("> Found device at 0x{0:02x}".format(dev) )
    print( 40*'-' )
    # Method 2
    print("Scanning I2C bus... Method 2")
    dev_list = i2c.scan()
    for dev in dev_list:
        print("> Found device at 0x{0:02x}".format(dev) )
    print( 40*'-' )
    sleep(5000)
       
```

![รูปภาพ: ตัวอย่างข้อความเอาต์พุตที่รับทาง Serial](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIY4CaijiMHLw_MNbhc%2F-MIYQPngUBqMKtVdm7TE%2Fmicropython_microbit_i2c_scan.png?alt=media\&token=936f2944-2356-4499-a5ec-3bbcff271fd6)

ถ้าทำงานได้ถูกต้อง จะต้องพบอุปกรณ์หมายเลข **`0x0e` (Compass)** และ **`0x1d` (Accelerometer)** สำหรับบอร์ดไมโครบิตเวอร์ชัน **1.3B**

## โค้ดตัวอย่างที่ 10: การอ่านค่าจากโมดูลเซ็นเซอร์แสง BH1750&#x20;

ตัวย่างถัดไปเป็นการอ่านข้อมูลจากโมดูลเซ็นเซอร์วัดแสง **BH1750 (GY-302)** ซึ่งจะให้ค่าที่มีความละเอียด 16 บิต (**0..65535**) หน่วยเป็นลักซ์ (**Lux**) เมื่อเริ่มต้นก่อนใช้งาน จะต้องส่งคำสั่งไปกำหนดโหมดการทำงานของ **BH1750**&#x20;

ในตัวอย่างนี้ ได้ใช้โหมด **Continuous Measurement** ความละเอียด **1.0 Lux** ต่อหนึ่งบิต (รายละเอียดเชิงเทคนิคเกี่ยวกับ **BH1750** แนะนำให้ศึกษาจากเอกสาร [**Datasheet**](https://www.mouser.com/datasheet/2/348/bh1750fvi-e-186247.pdf) ของผู้ผลิต)

โมดูลที่นำมาใช้งานนั้น เชื่อมต่อแบบบัส **I2C** ใช้แรงดันไฟเลี้ยง **3.3V** และมีหมายเลขแอดเดรสของอุปกรณ์คือ **`0x23` (= 35 decimal)** ซึ่งเป็น **Default Address** &#x20;

![รูปภาพ: โมดูล GY-302 (BH1750) ที่ได้นำมาใช้งาน](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIZ1Xo916jFcIRZ4z2H%2F-MIZ4T433faUcl48StXA%2Fbh1750_gy-302.jpg?alt=media\&token=bfbbca4e-d242-493b-bee5-d27e640ca3f5)

โมดูลนี้มีขา **ADDR** ถ้าต่อขาดังกล่าวด้วยสายไฟไปยังขา **VCC (3.3V)** จะได้แอดเดรสเป็น **`0x5C` (92 dec)** แต่ถ้าต่อไปยังขา **GND** หรือปล่อยไว้ไม่ต้องต่อขา (**Not Connected**) จะได้แอดเดรสเป็น **`0x23` (25 dec)**

ตัวอย่างโค้ดนี้ จะอ่านข้อมูลจากโมดูลเซ็นเซอร์ทั้งหมด 10 ครั้ง ในแต่ละครั้งจะส่งข้อความแสดงค่าที่อ่านได้ออกทาง **Serial** โดยใช้คำสั่ง **`print()`**

```python
from microbit import *
import time 

BH1750_ADDR = 0x23 # or 35 (dec) 

i2c.init( freq=100000, sda=pin20, scl=pin19)
print( 'Scan I2C devices..')
print( 'List of found devices:', i2c.scan() )

def bh1750_init(addr):
    try:
        # power on the BH1750
        i2c.write(addr, bytearray([0x01]) )
        # reset the BH1750
        i2c.write(addr, bytearray([0x07]) )
        time.sleep_ms(200)
        # set mode to 1.0x high-resolution,
        # continuous measurement
        i2c.write(addr, bytearray([0x10]) )
        time.sleep_ms(150)
        return True
    except Exception:
        return False

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

NUM_READINGS = 10
if bh1750_init(BH1750_ADDR):
    for i in range(NUM_READINGS): # repeat 10 times
        value = bh1750_read(BH1750_ADDR)
        print( 'Light level: [{0:>7.1f}] lx'.format(value) )
        time.sleep(1)
    print('Done....\n\n')
else:
    print('BH1750 not found...\n\n')

```

![รูปภาพ: ตัวอย่างอุปกรณ์ฮาร์ดแวร์ที่ใช้เพื่อทดสอบการทำงานโค้ดตัวอย่าง](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIZ1Xo916jFcIRZ4z2H%2F-MIZ4OSeB95LtAQ4iqxl%2Fmicrobit_micropython_gy-302.jpg?alt=media\&token=b460bffe-c345-4855-9c7c-efa8903df572)

![รูปภาพ: ตัวอย่างข้อความที่ได้รับผ่านทาง Serial](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIYyhAbt0arC3dEmbJn%2F-MIZ0otrFYrD2tWNdC0L%2Fmicrobit_micropython_bh1750_output.png?alt=media\&token=a6026755-51ea-49e3-874f-550b7f6c71fc)

## โค้ดตัวอย่างที่ 11: การอ่านค่าและแสดงสถานะการทำงานของ Wii Nunchuk&#x20;

โมดูล **Wii Nunchuk** เป็นอุปกรณ์เสริมที่ใช้ร่วมกับ **Wii Remote** ของ **Nintendo** แต่ก็สามารถนำมาใช้งานเพื่อต่อเข้ากับบอร์ดไมโครคอนโทลเลอร์ได้ โดยใช้วิธีสื่อสารแบบบัส **I2C** แต่จะต้องมี **PCB Adapter** ใช้ในการแปลงคอนเนกเตอร์ให้เป็นแบบ **Pin Headers** (หรือจะดัดแปลงสายไฟและเปลี่ยนชนิดของคอนเนกเตอร์ใหม่ก็ได้)

![รูปภาพ: อุปกรณ์ Wii Nunchuk และ PCB Adapter ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIYiqq46pemz0-hZWOV%2F-MIYk4eCZTbsyjHzYB94%2Fnunchuk_pcb_adapter.jpg?alt=media\&token=7c121a2f-b3c6-4191-afd3-90b5c6939bd1)

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

โดยสรุป ข้อมูลไบต์ที่อ่านได้ มีดังนี้

* ไบต์แรกและไบต์ที่สอง เป็นค่าของคันโยกควบคุม (**Joystick**) ในแกน x และแกน y ตามลำดับ ถ้าอยู่ในตำแหน่งตรงกลาง จะได้ค่าใกล้เคียง 127
* สามไบต์ถัดไปคือ ค่าที่ได้จากตัววัดความเร่ง 3 แกน คือ แกน x, y และ z ตามลำดับ อย่างละหนึ่งไบต์
* ไบต์ที่หกเป็นตัวระบุสถานะของปุ่มกด **C** (บิตที่ 1) และ **Z** (บิตที่ 0) ถ้าบิตมีค่าเป็น 0 หมายความว่า ปุ่มที่เกี่ยวข้องกับบิตดังกล่าว กำลังถูกกดอยู่ในขณะนั้น

![รูปภาพ: การเชื่อมต่ออุปกรณ์ระหว่างบอร์ไมโครบิตและ Wii Nunchuk](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIYiqq46pemz0-hZWOV%2F-MIYkC_w9tolg4IoVqTj%2Fmicrobit_nunchuck.jpg?alt=media\&token=e185ee8c-2eff-45f5-baca-25f4d67812c2)

โค้ดตัวอย่างนี้ สาธิตการอ่านค่าจาก **Wii Nunchuk** มีดังนี้

```python
from microbit import *

# 7-bit address of the Wii Nunchuck 
addr = 0x52  

def nunchuk_init():
    try:
        # send command to initialize the device
        i2c.write( addr, bytearray([0x40,0x00]) )
        # send command to disable data encryption
        i2c.write( addr, bytearray([0xF0,0x55]) )
        i2c.write( addr, bytearray([0xFB,0x00]) )
    except Exception:
        return False
    return True

def nunchuk_read():
    rawdata = None
    try:
        # read data
        i2c.write( addr, bytearray([0x00]) )
        sleep(50)
        # read raw data (expect 6 bytes)
        rawdata = i2c.read( addr, 6 )
    except Exception:
        return None
    if rawdata:
        data = rawdata
        # (x,y) position
        x,y = data[0],data[1]
        # accelerometer value for the x-,y- and z-axis
        ax,ay,az = data[2],data[3],data[4]
        btn_z = ((data[5] & 0x01) == 0)
        btn_c = ((data[5] & 0x02) == 0)
        return (x,y,ax,ay,az,btn_z,btn_c)

print( 'Micro:Bit Wii Nunchuck Interfacing (I2C)' )
print( 'Press the button A to start!' )
display.show( Image.ARROW_W )

while True:
    if button_a.was_pressed():
        display.show( ' ' )
        break
    
if nunchuk_init() == False:
    print('Nunchuk initialization failed..')
    while True:
        sleep(100)

while True:
    sleep(100)
    data = nunchuk_read()
    if data is None:
        continue
    print( 'x=%d, y=%d' % (data[0],data[1]) )
    print( 'ax=%d, ay=%d, az=%d' % (data[2],data[3],data[4]) )
    print( 'button Z pressed=%s' % (str(data[5])) )
    print( 'button C pressed=%s' % (str(data[6])) )
    print( 40*'-' )
```

![รูปภาพ: ตัวอย่างข้อความที่ได้รับผ่าน Serial ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MIYghUJSyoX4J68TAgs%2F-MIYiNYeaBsDbCBoENJL%2Fmicropython_microbit_i2c_nunchuck.png?alt=media\&token=8e4c1fad-e3c2-4d7d-8075-707e09ad15a2)

จากโค้ดตัวอย่างนี้ เราสามารถนำไปประยุกต์ใช้ในการเล่นหรือควบคุมเกมคอมพิวเตอร์ เช่น ใช้คันโยกแกน **X** และ **Y** รวมถึงปุ่มกด **C** และ **Z** เป็นต้น

## โค้ดตัวอย่างที่ 12: การอ่านค่าจากโมดูลเซ็นเซอร์ SHT31

โค้ดในตัวอย่างนี้ สาธิตการอ่านค่าจากโมดูลเซ็นเซอร์ [**SHT31-DIS**](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) สำหรับวัดอุณหภูมิและความชื้นสัมพัทธ์ (ใช้ไอซีที่ผลิตโดยบริษัท [**SENSIRION**](https://www.sensirion.com/)) และแสดงค่าที่อ่านได้เป็นข้อความเอาต์พุตทาง **Serial** โดยใช้คำสั่ง **`print()`**

&#x20;

```python
# File: main.py
from microbit import *
from sht3x import *

i2c.init( freq=100000, sda=pin20, scl=pin19 )
print( 'Scan I2C devices..')
print( 'List of found devices:', i2c.scan() )

sht3x = SHT3x( i2c, 0x44 )
try:
    sht3x.reset()
except Exception as ex:
    print('Error', ex)

for i in range(10):
    sht3x.measure()
    temp, humid = sht3x.read()
    text = "T: {:.1f} deg.C".format(temp)
    text = text + ', ' + "H: {:.1f} % RH ".format(humid)
    print( text )
    sleep( 1000 )
```

ในโค้ดตัวอย่างนี้ มีการสร้างออบเจ็กต์จากคลาส **`SHT3x`** ที่ได้มีการสร้างไว้ในไฟล์ **sht3x.py** และสามารถเรียกใช้คำสั่ง **`measure()`** เพื่อสั่งตัวเซ็นเซอร์ให้อัปเดตค่าครั้งถัดไป และใช้คำสั่ง **`read()`** เพื่ออ่านค่าตัวเลขสำหรับอุณหภูมิและความชื้นสัมพัทธ์&#x20;

โค้ดอีกหนึ่งไฟล์ (**sht3x.py**) ที่จะต้องนำไปบันทึกลงใน **Flash Storage** ของไมโครบิต เพื่อใช้ร่วมกับโค้ดตัวอย่างข้างบน มีดังนี้

```python
# File: sht3x.py (MicroPython for BBC Micro:bit)
from micropython import const
import time

class SHT3x:
    def __init__(self, i2c, addr):
        self.i2c  = i2c
        self.addr = addr
    
    def reset(self):
        try: 
            self.i2c.write( self.addr, bytearray([0x30,0xa2]) )
        except OSError:
            raise RuntimeError('SHT3x: I2C write failed!')

    def measure(self):
        try: 
            # send command: measurement, high repeatability, with clock stretching 
            self.i2c.write( self.addr, bytearray([0x2c,0x06]) )
        except OSError:
            raise RuntimeError('SHT3x: I2C write failed!')
 
    def read(self):
        try:
            raw = self.i2c.read(self.addr, 6)
        except OSError:
            raise RuntimeError('SHT3x: I2C write failed!')
        
        # check CRC8 mismatch ?
        if (self.crc8(raw[0:2]) != raw[2]) or (self.crc8(raw[3:5]) != raw[5]):
            raise RuntimeError('SHT3x: CRC mismatch!')
        
        temp = -45 + (175 * ((raw[0] << 8) + raw[1]) / 65535.0)
        humid = 100 * ((raw[3] << 8) + raw[4]) / 65535.0
        return (temp,humid)

    def crc8(self,buf):
        """ Polynomial 0x31 (x8 + x5 +x4 +1) """
        polynom = const(0x31)
        crc = 0xff;
        for i in range(0, len(buf)):
            crc ^= buf[i]
            for j in range(8):
                if crc & 0x80:
                    crc = (crc << 1) ^ polynom
                else:
                    crc = (crc << 1)
        return (crc & 0xff)
        
```

สำหรับการทดลองต่อวงจร โมดูลนี้ใช้แรงดันไฟเลี้ยงที่ **3.3V** จากบอร์ดไมโครบิตได้ การเชื่อมต่อกับโมดูลเซ็นเซอร์ **SHT31-DIS** ใช้วิธี **I2C Bus** ที่มีสายสัญญาณ 2 เส้น **SCL** และ **SDA** (ขา **SCL** และ **SDA** นำไปต่อกับขา **P19** และ **P20** ของบอร์ดไมโครบิตตามลำดับ) และมีการกำหนดแอดเดรสไว้เท่ากับ **`0x44`**&#x20;

{% hint style="info" %}
แนะนำให้ใช้โปรแกรม **Mu Editor** ในการเขียนโค้ด แล้วให้กดปุ่มไอคอน **Files** จะมีการแสดงรายการแบ่งเป็น 2 กลุ่ม (แบ่งเป็นด้านซ้ายกับขวามือ) เราสามารถเลือกไฟล์จากกลุ่มหนึ่ง ลากไปวาง (**Drag & Drop**) ใส่อีกกลุ่มหนึ่ง ซึ่งเป็นการสำเนาไฟล์ที่เลือก ระหว่างคอมพิวเตอร์กับอุปกรณ์ไมโครบิต
{% endhint %}

ลำดับขั้นตอนการทดสอบโค้ดกับบอร์ดไมโครบิตโดยใช้ **Mu Editor**

* สร้างไฟล์ใหม่ โดยกดปุ่ม **New** แล้วเขียนโค้ดตามตัวอย่างและบันทึกลงในไฟล์ชื่อ **sht3x.py** ในคอมพิวเตอร์ของผู้ใช้
* กดปุ่ม **Files** แล้วเลือกไฟล์ **sht3x.py** จากรายการไฟล์ในคอมพิวเตอร์ของผู้ใช้ แล้วลากไปยังบอร์ดไมโครบิต (**Drag & Drop** เพื่อสำเนาไฟล์)&#x20;
* เขียนโค้ดตัวอย่าง **main.py** แล้วอัปโหลดไปยังบอร์ดไมโครบิต โดยกดปุ่ม **Flash** แล้วจึงกดปุ่ม **REPL**&#x20;
* ในบริเวณช่องรับคำสั่งของ **REPL** ให้กดปุ่มบนแป้นพิมพ์ **Ctrl+D** เพื่อเริ่มต้นการทำงานของไมโครไพธอนใหม่อีกครั้ง และรันโค้ด **main.py** โดยอัตโนมัติ
* สังเกตข้อความเอาต์พุตที่ปรากฏในบริเวณ **REPL**&#x20;

![รูปภาพ: การสำเนาไฟล์ sht3x.py จากคอมพิวเตอร์ผู้ใช้ไปยังบอร์ดไมโครบิต](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJFMh9kPyshyy8jr6aS%2F-MJFN3EkIa8mGENOi-lg%2Fmu_editor_file_copy.png?alt=media\&token=59907dca-1466-44f1-aabb-06d3bdb8c3a5)

{% hint style="info" %}
ข้อสังเกต: ในโปรแกรม **Mu Editor** (v1.1.0alpha2) การเลือกทำคำสั่งจากปุ่มไอคอน **Flash**, **Files** และ **REPL** จะทำพร้อมกันไม่ได้ จะต้องเลือกอย่างใดอย่างหนึ่งในแต่ละช่วงเวลา เช่น ถ้าเปิดใช้ **REPL** อยู่ในขณะนั้น จะไม่สามารถทำขั้นตอน **Flash** หรือ **Files** ได้
{% endhint %}

![รูปภาพ: ข้อความเอาต์พุตแสดงค่าอุณหภูมิและความชื้นสัมพัทธ์ที่อ่านได้](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJFKC9mzALQ0QILmhUf%2F-MJFKhgkIPseAPzc04s_%2Fmu_editor_microbit_sht3x.png?alt=media\&token=484df18d-bb72-4556-b776-8641e0fb911c)

![รูปภาพ: การต่อวงจรเพื่อใช้งานโมดูล SHT31](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJFwldhKjz2ZmVquoEk%2F-MJFz-FVsx5jdT3YGJFU%2Fmicrobit_sht31_i2c.jpg?alt=media\&token=12ca4491-b160-4023-9bd8-fe17cc62472b)

## โค้ดตัวอย่างที่ 13: SHT31 + I2C 16x2 LCD&#x20;

โค้ดตัวอย่างนี้สาธิตการใช้งานโมดูล **16x2 LCD**  (แสดงผลข้อความแบบ **Alphanumeric** มี 2 แถว ๆ ละ 16 ตัวอักษร) แบบ **I2C** ที่ใช้ไอซี **PCF8574** เป็นตัวควบคุมการทำงาน และอ่านค่าจากโมดูลเซ็นเซอร์ **SHT31** ผ่านทาง **I2C** เช่นเดียวกันแล้วนำค่าที่ได้มาแสดงเป็นข้อความบนจอโมดูล **LCD**&#x20;

โมดูล **SHT31** มีการกำหนดแอดเดรสไว้เท่ากับ **`0x44`** ในขณะที่โมดูล **16x2 LCD-PCF8574** มีแอดเดรสเท่ากับ **`0x3F`**

```python
from microbit import *
from sht3x import *
from lcd_pcf8574 import LCD
import gc 

i2c.init( freq=400000, sda=pin20, scl=pin19 )
print( 'Scan I2C devices..')
print( 'List of found devices:', 
    [hex(d) for d in i2c.scan()] )

sht3x = SHT3x( i2c, 0x44 )
lcd   = LCD(i2c, 0x3f)

try:
    sht3x.reset()
    lcd.reset() # reset the LCD module first
    lcd.clear() # clear LCD 
    lcd.goto_line( 0 ) # goto the first line
    lcd.print('MicroPython...')
    lcd.goto_line( 1 ) # goto the second line
    lcd.print('Micro:bit')
except Exception as ex:
    print('Error', ex)

t_last = time.ticks_ms()
state = 0
try:
    while True:
        t_now = time.ticks_ms()
        if time.ticks_diff( t_now, t_last ) >= 500:
            t_last = t_now 
            if state == 0:
                sht3x.measure()
                temp, humid = sht3x.read()
            elif state == 1:
                lcd.goto_line( 0 ) # goto the first line
                text = ' T: {:2.1f} deg.C'.format(temp)
                text = text + max(16-len(text),0)*' '
                lcd.print( text )
            else:
                lcd.goto_line( 1 ) # goto the second line
                text = ' H: {:2.1f} %RH'.format(humid)
                text = text + max(16-len(text),0)*' '
                lcd.print( text )
            state = (state+1) % 3
            gc.collect()
            
except KeyboardInterrupt:
    pass
finally:
    print('Done..')
```

โค้ดไมโครไพธอนสำหรับไฟล์ **lcd\_pcf8574.py** มีดังนี้

```python
# file: lcd_pcf8574.py
from micropython import const
import utime as time

_RS = const(0x01) # PCF8574 Pin 0 (RS)
_RW = const(0x02) # PCF8574 Pin 1 (RW)
_CS = const(0x04) # PCF8574 Pin 2 (CS or EN)
_BL = const(0x08) # PCF8574 Pin 3
_CURSOR_BLINK         = const(0x01)
_CURSOR_ON            = const(0x02)
_DISP_ON              = const(0x04)
CMD_CLEAR_DISP        = const(0x01)
CMD_RETURN_HOME       = const(0x02)
CMD_CSTRY_MODE_SET    = const(0x04)
CMD_DISP_CTRL         = const(0x08)
CMD_CURSOR_DISP_SHIFT = const(0x10)
CMD_FUNC_SET          = const(0x20)
CMD_SET_CGRAM_ADDR    = const(0x40)
CMD_SET_DDRAM_ADDR    = const(0x80)

class LCD():
    def __init__(self, i2c, addr):
        self._i2c  = i2c
        self._addr = addr
        self._disp_mode = 0
        
    def _pcf8574_write( self, data ):
        self._i2c.write( self._addr, bytearray(data) )

    def _write4bits( self, data ):
        data = data | _BL
        self._pcf8574_write( [data | _CS] )
        time.sleep_us(300)
        self._pcf8574_write( [data] )
        time.sleep_us(300)
        
    def write( self, data, cmd=True ):
        _h = data & 0xf0        # high nibble
        _l = (data << 4) & 0xf0 # low nibble
        _mode = 0 if cmd else _RS
        self._write4bits( _h | _mode )
        self._write4bits( _l | _mode )
        
    def reset( self ):
        self._write4bits( 0x03 << 4 )
        time.sleep_ms(5)
        self._write4bits( 0x03 << 4)
        time.sleep_us(150)
        self._write4bits( 0x03 << 4)
        self._write4bits( 0x02 << 4)
        # function set: 4-bit data lines, 2 text lines, 5x8 dots
        self.write( CMD_FUNC_SET | 0x08 )
        # display ctrl: display on, cursor off
        self._disp_mode = _DISP_ON 
        self.write( CMD_DISP_CTRL | self._disp_mode )
        # go home position (move cursor to the first line)
        self.write( CMD_RETURN_HOME )
        
    def clear(self):
        self.write( CMD_CLEAR_DISP )
        
    def return_home( self ):
        self.write( CMD_RETURN_HOME )
        
    def goto_line( self, line ):
        addr = (0 if line==0 else 0x40) 
        self.write( CMD_SET_DDRAM_ADDR | addr )
        
    def blink_cursor( self, blink=True ):
        if blink:
            self._disp_mode |= _CURSOR_BLINK
        else:
            self._disp_mode &= ~_CURSOR_BLINK
        self.write( CMD_DISP_CTRL | self._disp_mode )
        
    def show_cursor( self, show=True ):
        if show:
            self._disp_mode |= _CURSOR_ON
        else:
            self._disp_mode &= ~_CURSOR_ON
        self.write( CMD_DISP_CTRL | self._disp_mode )
        
    def print( self, text ):
        for ch in text:
            self.write( ord(ch), False )
            
```

![รูปภาพ: ตัวอย่างโมดูล 16x2 LCD + PCF8574 Adapter](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJzEaxKsZdblr4Ssq_M%2F-MJzFKWNPvkTolhx8veb%2Fpcf8574_lcd16x2_modules-1.jpg?alt=media\&token=8c691fa7-86ff-4f9a-b3c4-d47dfc3800f3)

![รูปภาพ: การต่อวงจรเพื่อใช้งานโมดูล SHT31 และ LCD16x2 PCF8574](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJFwldhKjz2ZmVquoEk%2F-MJFzACeqtTlTlpoEege%2Fmicrobit_sht31_i2c_lcd16x2.jpg?alt=media\&token=6c770571-e3e2-42f6-ab21-c7add4689aad)

## โค้ดตัวอย่างที่ 14: การสร้างรูปกราฟิกบน LED Matrix

โค้ดตัวอย่างต่อไปนี้ สาธิตการสร้างรูปกราฟิกตามรูปแบบที่กำหนดโดยเงื่อนไข โดยใช้ค่าของพิกัด **(x,y)** บนแผง **5x5 LED Matrix (N=5)**

ฟังก์ชัน **`create_pattern(..)`** ใช้สำหรับสร้างข้อมูลอาร์เรย์ **`data`** ที่มีข้อมูลสมาชิกเป็น 0 หรือ 1 และมีจำนวนเท่ากับ **5x5 (25)**  ค่าของอาร์กิวเมนต์ **`i`** สำหรับฟังก์ชันนี้ เป็นเลขจำนวนเต็ม **`i=0,1,...,6`** จะเป็นตัวเลือกว่า ต้องการสร้างข้อมูลในอาร์เรย์เป็นแบบใด&#x20;

ฟังก์ชัน **`conditions(..)`** ทำหน้าที่ระบุสถานะที่พิกัด **(x,y)** ของ **LED Matrix** ตามเงื่อนไขที่กำหนดไว้ และ **`i`** เป็นอาร์กิวเมนต์ เพื่อกำหนดรูปแบบ หรือ กรณีในการสร้างรูปกราฟิก ฟังก์ชันนี้จะถูกเรียกใช้ในฟังก์ชัน **`create_pattern(..)`**

ฟังก์ชัน **`show_pattern(..)`** ใช้สำหรับนำค่าที่ได้จากอาร์เรย์ **`data`** ไปใช้เพื่อแสดงสถานะของ **LED Matrix** บนบอร์ดไมโครบิต ถ้ามีสถานะเป็น 0 หมายถึง **OFF** แต่ถ้ามีค่าเท่ากับ 1 หมายถึง **ON** (มีค่าของความสว่าง 9)

เมื่อทดสอบการทำงานของโค้ด ให้กดปุ่ม **A** เพื่อเปลี่ยนรูปแบบกราฟิกในลำดับถัดไป&#x20;

```python
from microbit import * 
from micropython import const
import time, random

N  = const(5) # number of rows and columns (NxN)
BL = const(9) # max. brightness level 

def conditions( i, x, y ):
    if i==0:
        return int(x <= y)
    elif i==1:
        return int(not(x < y))
    elif i==2:
        return int(x >= N-1-y)
    elif i==3:
        return int(x < N-y)
    elif i==4:
        dx = abs(x-N//2)
        return int((y-1) > dx or (N-y-2) > dx) 
    elif i==5:
        dy = abs(y-N//2)
        return int((x-1) > dy or (N-x-2) > dy)  
    elif i==6: # random 
        return int(random.randint(0,BL) > BL//2)
    return 0

def create_pattern( i ):
    data = []
    for y in range(N): # for each row
        for x in range(N): # for each column
            data.append( conditions(i,x,y) )
    return data

def show_pattern( data ):
    for y in range(N): # for each row
        for x in range(N): # for each column
            # set the brightness at (x,y)
            display.set_pixel(x, y, BL*data[x+y*N] )

display.show( Image.ARROW_W )
while True:
    for i in range(7):
        while not button_a.was_pressed():
            pass
        data = create_pattern(i)
        show_pattern( data )

```

![รูปภาพ: ตัวอย่างรูปกราฟิก 5x5 LED Matrix](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJV9Si_0ocujTTxPM8P%2F-MJVBEJLmoNK2zmpM5AQ%2Fmicrobit_pattern-4.jpg?alt=media\&token=e177829b-405f-4bd7-be74-0e128e70dca9)

![รูปภาพ: ตัวอย่างรูปกราฟิก 5x5 LED Matrix](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJV9Si_0ocujTTxPM8P%2F-MJVBHbjqI__i7K1AqtM%2Fmicrobit_pattern-3.jpg?alt=media\&token=e9ead15f-9763-4668-97c9-539ba4261d80)

![รูปภาพ: ตัวอย่างรูปกราฟิก 5x5 LED Matrix](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJV9Si_0ocujTTxPM8P%2F-MJVBLFTXvWt8VKhHGNf%2Fmicrobit_pattern-2.jpg?alt=media\&token=a90a2f9d-ad0e-4d98-8887-d0567a69a230)

![รูปภาพ: ตัวอย่างรูปกราฟิก 5x5 LED Matrix](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJV9Si_0ocujTTxPM8P%2F-MJVBPAdykLRt0YYidgC%2Fmicrobit_pattern-1.jpg?alt=media\&token=cb052273-60a3-43f6-ad4f-87d6ae6333b0)

## โค้ดตัวอย่างที่ 15: Conway's Game of Life

โค้ดตัวอย่างถัดไป สาธิตการจำลองสถานการณ์ที่เรียกว่า [**Conway's Game of Life**](https://en.wikipedia.org/wiki/Conway's_Game_of_Life) ซึ่งกล่าวถึง ระบบที่ประกอบด้วยเซลล์ (**Cells**) ที่ถูกจัดเรียงแบบเมตริกซ์ (อาร์เรย์สองมิติ) แต่ละเซลล์ที่พิกัด **(x,y)** มีสถานะเป็น 0 หรือ 1 ซึ่ง 0 หมายถึง ไม่มีชีวิต (**dead**) และ 1 หมายถึง เซลล์กำลังมีชีวิตอยู่ (**alive**)&#x20;

สถานะเริ่มต้นของเซลล์ อาจได้จากการสุ่ม (**Randomization of Cell States**) และสถานะของเซลล์ที่เปลี่ยนแปลงไปตามเวลา (เวลาเป็นแบบ **discrete-time**) ขึ้นอยู่กับสถานะของเซลล์โดยรอบ (**Neighbouring Cells**) โดยทั่วไปแต่ละเซลล์ในอาร์เรย์สองมิติ จะมีเซลล์ที่อยู่รอบ ๆ ติดกัน ไม่เกิน 8 เซลล์ (มีอยู่รอบทิศ)&#x20;

การกำหนดสถานะของเซลล์ในลำดับเวลาถัดไป เป็นไปตามกฎ (**Rules**) ได้ดังนี้

1. ถ้าเซลล์นั้นมีชีวิต และมีจำนวนเซลล์รอบ ๆ ที่มีชีวิตอยู่ เท่ากับ 2 หรือ 3 ให้เซลล์นั้นยังคงมีชีวิตต่อไป (**Healthy Population**)
2. ถ้าเซลล์นั้นมีชีวิต แต่มีจำนวนเซลล์รอบ ๆ ที่มีชีวิตอยู่ น้อยกว่า 2 หรือ มากกว่า 3 ให้เซลล์นั้นตายไป (กรณีนี้เรียกว่า **Underpopulation** และ **Overpopulation** ตามลำดับ)
3. ถ้าเซลล์นั้นไม่มีชีวิตหรือตายไปแล้ว แต่มีจำนวนเซลล์รอบ ๆ ที่มีชีวิตอยู่ เท่ากับ 3 ให้เซลล์นั้นเริ่มต้นหรือกลับมามีชีวิตใหม่อีกครั้ง (กรณีนี้เรียกว่า **Reproduction**)

ในโค้ดตัวอย่างนี้ ขนาดของเมตริกซ์เท่ากับ **5x5 (N=5)** แต่การเก็บสถานะของเซลล์ จะใช้อาร์เรย์มิติเดียว (**One-dimensional Array** หรือ **List**) โดยใช้ชื่อตัวแปรว่า **`cells`** ดังนั้นสถานะของเซลล์ที่พิกัด **(x,y)** ในอาร์เรย์ดังกล่าวคือ **`cells[x + N*y]`** &#x20;

{% hint style="info" %}
ตัวอย่างการเขียนโค้ดเพื่อสาธิต **Conway's Game of Life** ด้วย **MakeCode Static TypeScript** สำหรับบอร์ดไมโครบิต สามารถศึกษาได้จาก <https://makecode.microbit.org/examples/gameofLife>
{% endhint %}

```python
# Conway's Game of Life
# based on https://www.hackster.io/ivo-ruaro/conway-s-game-of-life-e383e3

from microbit import * 
from micropython import const
import random
from random import randint
import time 
import gc

N  = const(5) # number of rows and colums (NxN)
BL = const(9) # brightness level 
cells = []

def show():
    global cells
    for y in range(N):
        for x in range(N):
            display.set_pixel(x, y, BL*cells[x + N*y])

def count_neighbours(x, y):
    global cells
    n = 0
    # count the number of live neighbouring cells
    for dy in [-1, 0, 1]:
        for dx in [-1, 0, 1]:
            _x = (x + dx) % N
            _y = (y + dy) % N
            n += cells[_x + N*_y]
    n -= cells[x + N*y] # excludes the cell itself
    return n

def update():
    global cells
    new_cells = []
    for y in range(N):
        for x in range(N):
            num_neighbours = count_neighbours(x, y)
            is_alive = (cells[x + N*y] == 1)
            if is_alive:
                if num_neighbours < 2: # underpopulation
                    new_cells.append(0)
                elif num_neighbours == 2 or num_neighbours == 3:
                    new_cells.append(1) 
                elif num_neighbours > 3: # overpopulation
                    new_cells.append(0)
            else:
                if num_neighbours == 3: # reproduction
                    new_cells.append(1)
                else:
                    new_cells.append(0) # dies
    return new_cells

def reset():
    global cells
    random.seed( time.ticks_ms() )
    cells = [int(randint(0,BL) > BL//2) for i in range(N*N)]

# press button A to start
display.show( Image.ARROW_W )
while True:
    if button_a.was_pressed():
        break

reset()
show()
time.sleep(1.0)

while True:
    if button_a.was_pressed():
        reset()
    else:
        cells = update()
    show()
    gc.collect()
    time.sleep(0.5)
```

## โค้ดตัวอย่างที่ 16: การวัดความกว้างของสัญญาณพัลส์

โค้ดตัวอย่างนี้ สาธิตการวัดความกว้างของสัญญาณบแบบพัลส์ (**Pulse**) เช่น สัญญาณประเภท **PWM (Pulse Width Modulation)** เช่น ถ้าเราต้องการทราบความกว้างของพัลส์ช่วงที่เป็นลอจิก 1 (**High**) เราจะสามารถใช้บอร์ดไมโครบิตวัดค่าได้หรือไม่

ตัวอย่างนี้สร้างสัญญาณ **PWM** ที่ความถี่ **50Hz** หรือ มีความกว้าง **20 msec** เป็นสัญญาณแบบ **PWM** โดยเลือกใช้ขา **`pin8`** และใช้คำสั่ง **`write_analog()`** ซึ่งจะต้องระบุค่าตัวเลข (ขนาด 10 บิต) ในช่วง **0..1023** (หรือเท่ากับ **0% .. 100%** สำหรับค่า **Duty Cycle** ของสัญญาณดังกล่าว)&#x20;

ถ้ากำหนดให้ **Duty Cycle = 25% 50%** และ **75%**  โดยทางทฤษฏี จะได้ความกว้างเท่ากับ **5000**, **10000** และ **15000** ไมโครวินาที ตามลำดับ

สัญญาณเอาต์พุตที่ขา **`pin8`** จะถูกป้อนกลับเข้าที่ขา **`pin11`** ซึ่งถูกใช้เป็นขาอินพุต-ดิจิทัล  (ในการทดลอง สามารถใช้ลวดสายไฟ **Jumper Wire** เชื่อมต่อระหว่างขาทั้งสอง)

การวัดความกว้างของพัลส์ช่วงที่เป็น 1 สามารถทำได้ง่ายโดยใช้คำสั่ง [**`time_pulse_us()`**](https://microbit-micropython.readthedocs.io/en/latest/machine.html#machine.machine.time_pulse_us) ของกลุ่มคำสั่ง [**`machine`**](https://microbit-micropython.readthedocs.io/en/latest/machine.html)&#x20;

การเรียกใช้คำสั่งนี้ จะต้องระบุขา (**Pin**) ที่ต้องการใช้ เลือกประเภทของพัลส์เป็น **High (1)** หรือ **Low (0)** และกำหนดค่าตัวเลขสำหรับ **Timeout** (หน่วยเป็นไมโครวินาที)

```python
from microbit import * 
import machine 
import time 

period_ms = 20
display.off() # disable LED matrix display 

pin8.set_analog_period( period_ms ) # set period = 20msec
pin8.write_analog(0) # set duty cycle to 0

values = [25, 50, 75] # PWM duty cycles in percents

while True:
    print( 'Measured : Expected' )
    for v in values:
        pin8.write_analog( 1023*v//100 )
        t = machine.time_pulse_us( pin11, 1, 100000 )
        pin8.write_analog( 0 )
        args = (t, v*(period_ms*1000)//100 )
        print( '   {:5d} : {:5d} us'.format( *args ) )
        
    print( 40*'-' )
    time.sleep(5.0)
```

![รูปภาพ: ข้อความเอาต์พุตแสดงค่าตัวเลขที่วัดได้](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJVLFFKHJMIH0_AZU8K%2F-MJVTJfVxykic6Aec_fw%2Fmicrobit_micropython_pulse_measurement.png?alt=media\&token=31b995f3-d72b-4529-88ab-080c8a759680)

![รูปภาพ: ตัวอย่างการเชื่อมต่อด้วยสายไฟจากขา pin8 ไปยัง pin11](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJVq_cEFOACSaj_nqM5%2F-MJVqzuo2uXmHMSgqMTJ%2Fmicrobit_micropython_pulse_gen_loopback.jpg?alt=media\&token=799961dc-712e-478f-8c7e-dd51621dabdc)

## โค้ดตัวอย่างที่ 17: การวัดระยะห่างจากสิ่งกีดขวางโดยใช้โมดูล Ultrasonic Sensor HC-SR04P

**HC-SR04P** เป็นโมดูลเซ็นเซอร์ที่ใช้สัญญาณคลื่นเสียงอัลตราโซนิค (ความถี่สูง ประมาณ **40kHz**) ในการตรวจสอบและวัดระยะห่างจากวัดถุกีดขวาง&#x20;

หลักการทำงานของเซ็นเซอร์ประเภทนี้คือ การใช้ตัวส่ง (**Transmitter**) สร้างสัญญาณที่เป็นคลื่นเสียงออกไป เมื่อไปกระทบวัตถุ จะเกิดคลื่นสะท้อนกลับมายังตัวรับ (**Receiver**) เมื่อจับเวลาการเดินทางของคลื่นเสียงในทิศทางไปและกลับ และกำหนดอัตราเร็วของคลื่นเสียงในอากาศ (เช่น 340 เมตร/วินาที โดยประมาณ) ก็จะสามารถคำนวณระยะห่างจากวัตถุได้

โมดูล **HC-SR04P** สามารถวัดระยะห่างจากวัตถุได้สูงสุด ประมาณ 4 เมตร และใช้แรงดันไฟเลี้ยง **3.3V** หรือ **5V** ได้ โมดูลนี้มีขาอินพุต **TRIG (Trigger)** และขาเอาต์พุต **ECHO** เมื่อได้รับสัญญาณพัลส์ (ความกว้างอย่างน้อย 10 ไมโครวินาที) ที่ขา **TRIG** จะมีการสร้างสัญญาณคลื่นเสียงออกไป หลังจากนั้นจะเกิดสัญญาณพัลส์ที่ขา **ECHO** ความกว้างของสัญญาณพัลส์ที่ขา **ECHO** จะเป็นตัวระบุระยะเวลาการเดินทางของคลื่นเสียงและสะท้อนกลับมาถึงตัวรับ

```python
from microbit import *
from machine import *
from micropython import const
import time

TRIG_PIN, ECHO_PIN = pin13, pin12
TIMEOUT_US  = const(20000)
SOUND_SPEED = const(340*100) # cm/sec

def measure(trig_pin, echo_pin, timeout=TIMEOUT_US):
    trig_pin.write_digital(1)
    time.sleep_us(20)
    trig_pin.write_digital(0)
    tp = time_pulse_us( echo_pin, 1, timeout )
    return tp

display.off() # disable LED matrix display
TRIG_PIN.write_digital(0)

while not button_a.was_pressed():
    dt = measure( TRIG_PIN, ECHO_PIN )
    if dt != -1:
        # convert time duration to distance
        distance = (SOUND_SPEED*dt)//(2*100000)
        print( 'Distance: {} cm'.format(distance/10.0) )
    time.sleep_ms(200)
    
print('Done')
```

การวัดความกว้างของพัลส์ ก็ทำได้ง่าย โดยใช้คำสั่ง **`time_pulse_us()`** ในชุดคำสั่ง **`machine`**  จากนั้นจะต้องแปลงค่าที่ได้วัดได้สำหรับระยะเวลา (มีหน่วยเป็นไมโครวินาที) ให้เป็นระยะทาง (มีหน่วยเป็นเซนติเมตร)&#x20;

โค้ดตัวอย่างนี้เลือกใช้ขา **`pin12`** สำหรับสัญญาณ **ECHO** และ **`pin13`** สำหรับ **TRIG** ตามลำดับ โดยจะทำการวัดค่าที่เป็นระยะห่างจากวัตถุซ้ำไปเรื่อย ๆ จนกว่าจะหยุดเมื่อมีการกดปุ่ม **Button A**

![รูปภาพ: การทดลองใช้งานโมดูล HC-SR04P Ultrasonic Sensor](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJg7hvKGmSctn_Fzg2N%2F-MJg9r6znKJ1VyQUx1aV%2Fmicrobit_hc_sr04p.jpg?alt=media\&token=1af92931-0f09-4663-8c2c-8f0c6716695e)

## โค้ดตัวอย่างที่ 18: การใช้งานไอซี MCP492**1** SPI DAC

[**MCP4921**](http://ww1.microchip.com/downloads/en/devicedoc/21897b.pdf) ของบริษัท **Microchip** เป็นไอซีประเภท **DAC (Digital-to-Analog Converter)** มีเพียงเอาต์พุต 1 ช่องสัญญาณ (แต่ถ้าเป็น **MCP4922** จะมี 2 ช่อง) มีความละเอียดของข้อมูล เท่ากับ 12 บิต (**4096** ระดับ) และรับข้อมูลโดยใช้บัส **SPI**&#x20;

ตัวอย่างนี้ สาธิตการสร้างสัญญาณรูปไซน์ (**Sinusoidal Waveform**) หนึ่งคาบ โดยใช้ไอซี **MCP4921 DAC** สร้างสัญญาณเอาต์พุตแบบแอนะล็อก ใช้แรงดันไฟเลี้ยงและแรงดันอ้างอิง (**Reference Voltage**) เท่ากับ +**3.3V**&#x20;

![รูปภาพ: ตัวถัง (IC Package) ของ MCP4921 ที่มี 8 ขา ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJfqKTZLLByczZAZ8OR%2F-MJfr-V9wp9We-KkUuBB%2Fmcp4921_dip-8.png?alt=media\&token=2168299e-9d28-4eac-a6ee-a54f0aaec713)

ถ้าจะลองต่อวงจรบนเบรดบอร์ด ก็ให้ใช้ตัวถังของไอซีแบบ **PDIP-8** มี 8 ขา ดังนี้

* **VDD** เป็นขาแรงดันไฟเลี้ยง
* **/CS** เป็นขาสัญญาณอินพุต **Chip-Select** ทำงานแบบ **Active-Low**&#x20;
* **SCK** เป็นขาสัญญาณอินพุต **Serial Clock Input** (ความถี่สูงสุดที่ใช้ได้คือ **20MHz**)
* **SDI** เป็นขาอินพุตสำหรับข้อมูลที่เลื่อนเข้าทีละบิต **Serial Data Input**&#x20;
* **/LDAC** เป็นขาอินพุตสำหรับสัญญาณควบคุม **DAC Ouput Latch** ทำงานแบบ **Active-Low โ**ดยทั่วไป ให้ต่อกับ **GND** ของวงจร เพื่อให้เอาต์พุตที่การเปลี่ยนแปลงโดยอัตโนมัติหลังจากเขียนข้อมูล 16 บิต **(**&#xE40;มื่อ **/CS** เปลี่ยนจาก **LOW** เป็น **HIGH)**
* **VREFA** เป็นขาแรงดันไฟฟ้าอ้างอิงสำหรับการสร้างสัญญาณแบบแอนะล็อก
* **AVSS** เป็นขา **Ground** **(GND)**
* **VOUTA** เป็นขาสัญญาณเอาต์พุตแบบแอนะล็อก (**DAC Output**)

การเขียนข้อมูลไปยัง **MCP4921** มีครั้งละ 2 ไบต์ สำหรับข้อมูล 16 บิต (ส่งข้อมูลบิตที่ **15..8** และบิตที่ **7..0** ตามลำดับ) ส่งผ่านบัส **SPI** ไมโครบิตทำหน้าที่เป็น **SPI Master** และไอซี **MCP4921** เป็น **SPI Slave** เลือกใช้โหมดการทำงานของ **SPI** เป็น **(CPOL=1, CPHA=1)** หรือโหมด **3** และลองใช้ความถี่ เช่น  **1MHz**&#x20;

ข้อมูล 16 บิต มีการแบ่งออกเป็น 2 ส่วน คือ ส่วนแรกมี 4 บิต เรียกว่า **Config Bits** และส่วนที่สองมี 12 บิต เรียกว่า **Data Bits** ซึ่งเป็นตัวกำหนดระดับแรงดันเอาต์พุตที่ขา **VOUTA**

![รูปภาพ: ไดอะแกรมเชิงเวลาสำหรับสัญญาณของ MCP4921](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJg-dZHrVF0AN8bNjv6%2F-MJg-gwfVZwisigqI541%2Fmcp4921_timing_diagram.png?alt=media\&token=3b8b1529-a874-4023-9625-cf8034badb2a)

กลุ่มคำสั่งที่เกี่ยวข้องกับการใช้งานบัส **SPI** คือ [**`machine.spi`**](https://microbit-micropython.readthedocs.io/en/latest/spi.html) และเริ่มต้นโดยการใช้คำสั่ง [**`spi.init()`**](https://microbit-micropython.readthedocs.io/en/latest/spi.html#microbit.spi.init) ซึ่งมีอาร์กิวเมนต์ดังนี้&#x20;

* ความเร็วในการส่งข้อมูล (**baudrate**)&#x20;
* ขนาดข้อมูลหนึ่งเฟรม (**bits** = จำนวนบิตในการเลื่อนข้อมูล) เช่น 8 บิต&#x20;
* โหมดการทำงานของ **SPI** ที่ต้องการเลือกใช้งาน เลือกโหมด **(0,0)** หรือ **(1,1)**
* ขา **GPIO** ที่ใช้งานสำหรับสัญญาณ **SCK (Serial Clock)**, **MOSI (Master-Out / Slave-In)** และ **MISO (Master-In / Slave-Out)** ของบัส **SPI**

ถ้าต้องการส่งข้อมูลออกไป ก็ใช้คำสั่ง [**`spi.write()`**](https://microbit-micropython.readthedocs.io/en/latest/spi.html#microbit.spi.spi.write)  ซึ่งจะต้องระบุอาร์กิวเมนต์ที่เป็นบัฟเฟอร์ข้อมูล เช่น มีชนิดข้อมูลเป็น **`bytearray`** และมีขนาดหรือความยาวตามจำนวนไบต์ที่ต้องการส่ง

```python
from microbit import *
from micropython import const
import time
import math

SCK_PIN  = pin13 # -> MCP4921 SCK pin
MOSI_PIN = pin15 # -> MCP4921 SDI pin
MISO_PIN = pin14 # not used
CS_PIN   = pin16 # -> MCP4921 /CS pin

CS_PIN.write_digital(1)
spi.init( baudrate=1000000, bits=8, mode=3,
         sclk=SCK_PIN,
         mosi=MOSI_PIN,
         miso=MISO_PIN )

spi_buf = bytearray(2)

def write_output( value ):
    global spi_buf
    spi_buf[0] = 0x70 | ((value >> 8) & 0x0f)
    spi_buf[1] = value
    CS_PIN.write_digital(0) # /CS low
    spi.write( spi_buf )    # SPI data transfer 
    CS_PIN.write_digital(1) # /CS high

N_BITS = const(12)
MAX_VALUE = const(2**N_BITS - 1)
N_STEPS = const(128)

for i in range(N_STEPS):
    v = math.sin( 2*math.pi*i/N_STEPS )
    write_output( int((1+v)*MAX_VALUE/2) )
    time.sleep_us( 500 )

write_output( MAX_VALUE//2 )
```

![รูปภาพ: รูปคลื่นสัญญาณเอาต์พุตจาก MCP4921](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJfgoKB8Clh1R8v8L12%2F-MJfjUhCXKNt17pLe0jO%2Fmicrobit_mcp4921_dac_waveform.png?alt=media\&token=eb351534-361f-4f22-8736-123821776116)

![รูปภาพ: รูปคลื่นสัญญาณ SPI SCK (ความถี่ 1MHz)](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJfgoKB8Clh1R8v8L12%2F-MJfjgiUJ2FWbPzvrjb8%2Fmicrobit_mcp4921_sclk_waveform_1mhz.png?alt=media\&token=b63377c1-9a13-4b8d-b6e8-db7fb88bf13f)

![รูปภาพ: การต่อวงจรทดลองใช้งาน MCP4921](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJfulQV3t5uimVqS-ia%2F-MJfw9f7Fosjh9aVpbFa%2Fmicrobit_mcp4921_demo.jpg?alt=media\&token=cb8f354b-e3b6-49ac-afa1-c163c2a41ab0)

อ้างอิงจากเอกสาร **Datasheet** ของ **MCP4921** ขาเอาต์พุต **VOUTA** สามารถจ่ายหรือรับกระแสได้ไม่เกิน **25mA** ดังนั้น ถ้าต่อวงจร **LED** อนุกรมกับตัวต้านทานจำกัดกระแส เช่น **330** โอห์ม ที่ขาเอาต์พุตดังกล่าว ก็สามารถสังเกตการเปลี่ยนแปลงของระดับแรงดันไฟฟ้าได้ (ซึ่งจะส่งผลต่อความสว่างของ **LED**) ในกรณีที่ไม่มีเครื่องวัดคลื่นสัญญาณ เช่น ออสซิลโลสโคป

## โค้ดตัวอย่างที่ 19: การใช้ไอซี PCF8574 แสดงสถานะลอจิกของโมดูล LED ที่มี 8 ดวง

ตัวอย่างนี้ เป็นการแสดงสถานะติดหรือดับ (**ON** หรือ **OFF**) สำหรับโมดูล **LED Bar** ที่มี 8 ดวง โดยใช้ไอซี **PCF8574** (**8-bit I/O Expander**) เป็นตัวกำหนดสถานะเอาต์พุต 8 บิต (ในตัวอย่างนี้ **PCF8574** ทำงานในโหมดเอาต์พุตเท่านั้น)

โมดูลหรือไอซี [**PCF8574**](https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf) ใช้วิธีสื่อสารข้อมูลกับไมโครบิตได้ด้วยบัส **I2C** และในตัวอย่างนี้ได้กำหนดให้โมดูลนี้ มีแอดเดรสเท่ากับ **`0x20`** และในการเชื่อมต่อสายกับไมโครบิต ก็ได้ใช้ขา **`pin19`** และ **`pin20`** สำหรับสัญญาณ **SCL** และ **SDA** ของบัส **I2C** ตามลำดับ

ไมโครบิตทำหน้าที่เป็น **I2C Master** และโมดูล **PCF8574** ทำหน้าที่เป็น **I2C Slave** คอยรับข้อมูลหนึ่งไบต์ เพื่อนำมากำหนดสถาะลอจิกสำหรับเอาต์พุต 8 บิต ที่นำไปต่อกับโมดูล **8-Bit LED Bar**

```python
from microbit import *
from micropython import const 
import time

PCF8574_ADDR = const(0x20) 
i2c.init( freq=100000, scl=pin19, sda=pin20)

display.clear() # clear display

if PCF8574_ADDR not in i2c.scan():
    print('Device not found!')
    display.show( Image.NO )

data = 0x01 # set the initial value 
buf  = bytearray(1) # single-byte buffer array

while True:
    buf[0] = ~data # invert logic 
    try:
        i2c.write( PCF8574_ADDR, bytes(buf) )
    except Exception:
        break 
    # rotate shift-left
    data = (data << 1) | (data >> 6)
    time.sleep_ms(100)
```

ข้อสังเกต: โมดูล **LED Bar** ที่ได้เลือกมาใช้งานนั้น มีขาอินพุต-ดิจิทัล 8 ขา **LED0 .. LED7** ใช้กำหนดสถานะของ **LED** แต่ละดวง ทำงานแบบ **Active-Low** และมีขา **VCC** สำหรับป้อนแรงดันไฟเลี้ยง (ใช้ **3.3V**)

เมื่อทดสอบการทำงานของโค้ดกับอุปกรณ์จริง จะเห็นได้ว่า จะมีเพียง **LED** หนึ่งดวงในแต่ละช่วงเวลา ที่อยู่ในสถานะ **ON** (สว่าง) และตำแหน่งของ **LED** ที่สว่าง จะถูกเลื่อนไปเรื่อย ๆ แล้ววนซ้ำใหม่

![รูปภาพ: ตัวอย่างอุปกรณ์ที่ใช้ในการทดลอง](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJyv1pOJ67GuuAW0vcz%2F-MJywBxurqq5e7aVwi_V%2Fpcf8574_8-bit_led_bar-1.jpg?alt=media\&token=882a31b3-a401-4434-b064-dffdfa804a07)

ข้อสังเกต: โมดูล **PCF8574(A)** ที่ได้เลือกมาใช้งานนั้น มีขา **A2..A0** สำหรับเอาไว้กำหนดค่าบิตของแอดเดรส ดังนั้นแต่ละอุปกรณ์จึงมีแอดเดรสแตกต่างกันได้ในบัสเดียวกัน ในรูปตัวอย่าง มีการใช้ **Jumper** (สีเหลือง 3 อัน)  กำหนดค่าบิตเป็น 0 หรือ 1 ให้กับขา **A2..A0**

![รูปภาพ: การต่อวงจรทดลองใช้โมดูล PCF8574 และ 8-bit LED Bar](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJykqxIUCfZF5Ji1-8E%2F-MJylMR8vvSzBBM8oCK3%2Fmicrobit_8-led_pcf8574-1.jpg?alt=media\&token=5e14cbbb-41ae-4374-aa50-6f029d46622e)

## โค้ดตัวอย่างที่ 20: การกำหนดสถานะลอจิกของโมดูล LED ที่มี 8 ดวง โดยใช้ค่าอินพุตแอนะล็อก

ตัวอย่างนี้สาธิตการอ่านค่าจากโมดูลที่มีตัวต้านทานปรับค่าและเลื่อนตำแหน่งได้แบบเชิงเส้น (**Linear Potentiometer**) โดยนำมาใช้เป็นวงจรแบ่งแรงดัน (**Voltage Divider**) ใช้แรงดันไฟเลี้ยง **3.3V** และเลือกใช้ขา **`pin0`** สำหรับอ่านค่าแรงดันอินพุต (อยู่ในช่วง **0V** ถึง **3.3V**) ที่ได้จากวงจรแบ่งแรงดันดังกล่าว

ค่าที่อ่านได้จากขาอินพุต **`pin0`** จะอยู่ในช่วง **0..1023** (เนื่องจาก **ADC** มีความละเอียด 10 บิต) แต่จะถูกนำมาแปลงให้เป็นเลขจำนวนเต็ม ให้มีค่าอยู่ในช่วง **0..9** และเก็บไว้ในตัวแปร **`value`** จากนั้นจะนำไปใช้กำหนดสถานะการทำงานของโมดูล **LED Bar** ที่มีจำนวน 8 ดวง&#x20;

ค่าของตัวแปร **`value`** จะถูกใช้ในการกำหนดจำนวนหรือระดับของ **LED** ที่อยู่ในสถานะ **ON** เช่น ถ้า **`value`** มีค่าเท่ากับ 0 ซึ่งเป็นค่าต่ำสุด ก็จะไม่มีดวงใด **LED** สว่าง หรือถ้าใช้ค่าสูงสุดคือ 9 จะทำให้ **LED** ทุกดวงสว่าง เป็นต้น

```python
from microbit import *
from micropython import const 
import time

ANALOG_PIN = pin0 
PCF8574_ADDR = const(0x20)

i2c.init( freq=100000, scl=pin19, sda=pin20)

buf = bytearray(1)
last_value = 0

def update_leds(value):
    data = 0
    for i in range(8):
        data = (data << 1) | int(i < value)
    buf[0] = ~data
    try:
        i2c.write( PCF8574_ADDR, bytes(buf) )
    except Exception:
        return False # error
    return True # ok
    
while True:
    # read analog value and map it to a range 0..9
    value = ANALOG_PIN.read_analog()//120 
    if last_value != value:
        print( value )
        if not update_leds(value):
            break
        last_value = value
    time.sleep_ms(10)
```

![รูปภาพ: ตัวอย่างอุปกรณ์ที่ใช้ในการทดลอง](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJysBHdIqWMXinTRv7l%2F-MJytaLzp6zDtqTCuy4c%2Fpotentiometer_pcf8574_led_bar-1.jpg?alt=media\&token=21be8ecb-c89c-423b-b0a8-fa96495aaa7f)

![รูปภาพ: การต่อวงจรทดลองใช้โมดูล Potentiometer และ 8-bit LED Bar / PCF8574 Driver](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJymTfm4taVcraKNrrB%2F-MJympjz7TUGAaT4cLag%2Fmicrobit_analog_8-led_pcf8574-2.jpg?alt=media\&token=124c3793-57ae-4415-8740-5c134d41e430)

## โค้ดตัวอย่างที่ 21: การใช้โมดูล Rotary Encoder เป็นอุปกรณ์อินพุต

โดยทั่วไปแล้ว โมดูล **Rotary Encoder** มีขาสัญญาณเอาต์พุต **A** และ **B** (หรือเรียกแบบอื่น เช่น  **CLK** กับ **DT** ตามลำดับ) และมีหลักการทำงานเหมือนสวิตช์ปุ่มกดคือ สถานะปรกติเป็น **High (H)** แต่เป็น **Low (L)** ถ้ามีการกดสวิตช์ในช่วงเวลาหนึ่ง

โดยปรกติขาสัญญาณ **A** และ **B** ของโมดูล **Rotary Encoder** มีสถานะเป็นลอจิก **High (H)** แต่เมื่อมีการหมุนในทิศทางตามหรือทวนเข็มนาฬิกา จะทำให้เกิดสัญญาณแบบ **Pulse** ที่ขา **A** และ **B** กล่าวคือ มีการเปลี่ยนจาก **H->L** (ขอบขาลง) หรือ **L->H** (ขอบขาขึ้น) ที่เกิดขึ้นตามสเต็ปการหมุน แต่ทั้งสองสัญญาณ **A** และ **B** จะมีเฟสต่างกัน (ขอบขาขึ้นหรือขาลง เกิดขึ้นไม่พร้อมกันทั้งสองสัญญาณ)

![รูปภาพ: ตัวอย่างโมดูล Rotary Encoder Switch](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJyuX8T7j7vxg1PwjpM%2F-MJyuw72IVqQX4_qAfOJ%2Frotary_encoder_switch-1.jpg?alt=media\&token=626ff814-322d-4a27-a7de-f66a1fd7a0b3)

![รูปภาพ: สัญญาณ A และ B ที่ได้จากการวัด เมื่อหมุนไปหนึ่งสเต็ป](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJz3wBiQ_8iFtQv_PQG%2F-MJz4hkSJUYtLL8y0RVC%2Frotary_waveform-3.png?alt=media\&token=3196986a-a39a-4bb0-88e6-2274ffddb747)

![รูปภาพ: สัญญาณ A และ B ที่ได้จากการวัด เมื่อหมุนไปมากกว่าหนึ่งสเต็ป](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJz3wBiQ_8iFtQv_PQG%2F-MJz4o3E3q5Kf7L9XDXp%2Frotary_waveform-2.png?alt=media\&token=abc6afe8-9381-4e4c-aeab-7cf7ba368643)

โค้ดตัวอย่างนี้ สาธิตการตรวจสอบดูว่ามีการหมุนเกิดขึ้นหรือไม่ โดยดูจากสัญญาณพัลส์ที่ขา **A**  เกิดพัลส์แบบ **High** และมีความกว้าง (**Pulse Width**) ตามเงื่อนไขที่กำหนดไว้หรือไม่ จำนวนพัลส์ที่เกิดขึ้นขึ้นอยู่กับจำนวนสเต็ปที่หมุนไป &#x20;

เมื่อเกิดพัลส์ที่สัญญาณ **A** ก็ตรวจสอบดูด้วยว่า สถานะลอจิกของสัญญาณ **B** เป็นอย่างไร เพื่อจำแนกว่า เป็นการหมุนในทิศทางใด เช่น ถ้าให้ทิศทางการหมุนเป็นตัวกำหนดว่า จะเพิ่มหรือลดค่าของตัวแปร **`level`** ที่เป็นเลขจำนวนเต็ม และให้ตัวแปร **`level`** ถูกจำกัดให้มีค่าอยู่ในช่วง 0 ถึง 9 เท่านั้น

ในตัวอย่างนี้ ได้ใช้โมดูล **WS2812 Neopixel** เพื่อใช้ **RGB LED** จำนวน 8 ดวง แสดงค่าของตัวแปร **`level`** ในขณะนั้น ค่าของตัวแปร **`level`** เป็นตัวกำหนดว่า จะมี **LED** ทั้งหมดกี่ดวงที่อยู่ในสถานะ **ON** (สว่าง)

ขา **`pin16`** ของไมโครบิต ได้ถูกเลือกใช้เป็นขาเอาต์พุตสำหรับขา **DIN** ของโมดูล **Neopixel** และใช้แรงดันไฟเลี้ยง **+3.3V** จากโมดูลเสริมที่นำมาต่อกับ **Edge Connector** ของบอร์ดไมโครบิต

```python
from microbit import *
from machine import *
from neopixel import NeoPixel
from micropython import const
import time

GREEN = (0,255,0)    # default color
NUM_LEDS = const(8)  # number of WS2812 LEDs
T_MIN = const(10000) # in microseconds
T_MAX = const(30000) # in microseconds

display.off() # turn off display

WS2812_PIN = pin16
PIN_A = pin13
PIN_B = pin14 

PIN_A.set_pull( PIN_A.NO_PULL )
PIN_B.set_pull( PIN_B.NO_PULL )

np = NeoPixel( WS2812_PIN, NUM_LEDS )
for i in range(NUM_LEDS):
    np[i] = GREEN
    np.show()
    time.sleep_ms(200)
    
np.clear() # turn off all LEDs
np.show() 

level = last_level = 0

while True:
    t = time_pulse_us( PIN_A, 1, T_MAX )
    if t >= T_MIN:
        if PIN_B.read_digital():
            level = level-1 # decrement level
        else:
            level = level+1 # increment level
        level = max(0, min(level, NUM_LEDS))
    if last_level != level:
        # if the level was changed, 
        # then update the colors of the LEDs.
        last_level = level
        for i in range(NUM_LEDS):
            if i < level:
                np[i] = GREEN
            else:
                np[i] = (0,0,0)
            np.show()
        print (level, t)
```

![รูปภาพ: การต่อวงจรทดลองใช้โมดูล Rotary Encoder และ Neopixel](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJyc4IYZsY9CF5iAMsN%2F-MJyiOqtLftg-8fJT_xK%2Fmicrobit_rotary_encoder_neopixel-1.jpg?alt=media\&token=7396333b-1dfe-4507-ad1a-d401479d061e)

![รูปภาพ: แสดงค่าแบบระดับ (Level) โดยใช้ LED แบบ 8 ดวง](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MJyc4IYZsY9CF5iAMsN%2F-MJyiVcFDZGaocdr61bp%2Fmicrobit_rotary_encoder_neopixel-2.jpg?alt=media\&token=e0e49101-d050-411d-8c82-63d6a5199544)

## โค้ดตัวอย่างที่ 22: การรับส่งข้อมูลแบบ UART Loopback&#x20;

บอร์ดไมโครบิตสามารถสื่อสารแบบ **Serial** กับอุปกรณ์อื่นได้ เนื่องจากมีวงจร **UART** อยู่ภายใน **nRF51822** โดยจะต้องเลือกใช้ขา **Pin** จำนวน 2 ขา สำหรับใข้งานเป็นขา **TXD** (ส่งข้อมูลออก) และ **RXD** (รับข้อมูลเข้ามา)&#x20;

โค้ดตัวอย่างนี้ สาธิตการใช้อุปกรณ์หรือโมดูล **USB-to-Serial** (ทำงานที่ระดับ **+3.3V** และใช้ไฟเลี้ยงจากพอร์ต **USB**) นำมาต่อเข้ากับขา **`pin0`** และ **`pin1`** เพื่อใช้เป็นขา **TXD** และ **RXD** ตามลำดับ และคอยรับข้อความจากคอมพิวเตอร์ของผู้ใช้ จากนั้นเมื่อได้รับแล้วก็ส่งข้อความนั้นกลับไป ดังนั้นจึงเป็นการทดสอบการใช้งานในรูปแบบที่เรียกว่า **UART Loopback** แต่ถ้าได้รับข้อความว่า **`'exit'`** หรือกดปุ่ม **Button A** ของบอร์ดไมโครบิต จะจบการทำงานของโปรแกรม

| Microbit Pin | USB-to-Serial Pin |
| ------------ | ----------------- |
| pin0 (TXD)   | RX                |
| pin1 (RXD)   | TX                |
| GND          | GND               |

```python
from microbit import *

uart.init(baudrate=9600, tx=pin0, rx=pin1)
uart.write( b'UART loopback test...\r\n' )

while True:
    if button_a.was_pressed():
        break
    data = uart.read() # read next line as bytes
    if data:
        # strip and convert received data to lowercase
        data = data.strip().lower()
        if data.find(b'exit') == 0:
            uart.write( b'Switching back to REPL...\r\n' )
            break
        else:
            uart.write( data )
            uart.write( b'\r\n' )

# switch back to REPL console
sleep(1.0)
uart.init(baudrate=115200)
print('Done...')
```

โปรแกรมสำหรับคอมพิวเตอร์ของผู้ใช้ที่ได้นำมาใช้ตัวอย่างนี้คือ **Arduino IDE - Serial Monitor** เช่น ส่งข้อความและรับข้อความตอบกลับจากไมโครบิตได้ (ตั้งค่า **Baudrate** ให้ตรงกับ **9600** สำหรับตัวอย่างนี้)

ข้อสังเกต: โดยปรกติ วงจร **UART** จะถูกใช้ในการสื่อสารกับ **REPL** ของไมโครไพธอน แต่หลังจากได้ใช้คำสั่ง **`uart.init()`** ที่มีการระบุขาสำหรับ **TXD** และ **RXD** และกำหนดค่า **Baudrate** หรือความเร็วในการรับส่งข้อมูลแล้ว จะไม่สามารถสื่อสารผ่านทาง **REPL Console** ได้ต่อไป แต่ถ้าจะกลับไปสื่อสารกับ **REPL Console** ได้อีกครั้ง ก็ให้ทำคำสั่งดังนี้

```python
uart.init(baudrate=115200)
```

![รูปภาพ: การต่อวงจรทดลองร่วมกับโมดูล USB-to-Serial (CP2104)](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MKx1WfPz7Yt3FnWNFMN%2F-MKx1sAJJrmd0DLL29gX%2Fmicrobit_uart-1.jpg?alt=media\&token=1a600509-9697-4c6b-b644-9be1a4e16428)

## โค้ดตัวอย่างที่ 23: การอ่านค่าจาก Analog Input แล้วส่งออกทาง UART

โค้ดตัวอย่างถัดไป สาธิตการอ่านค่าจากขาอินพุตแบบแอนะล็อก (**Analog Input**) ของบอร์ดไมโครบิต โดยเลือกใช้ขา **`pin2`** และได้รับสัญญาณอินพุตจากโมดูลเซ็นเซอร์วัดความชื้นในดินแบบคาปาซิทีฟ (**Capacitive Soil Moisture Sensor**) แล้วนำค่าที่อ่านได้ (อยู่ในช่วง 0 ถึง1023) ส่งออกเป็นข้อความผ่านทาง **UART** โดยเลือกใช้ขาภายนอก **`pin0`** และ **`pin1`** สำหรับ **TXD** และ **RXD** ตามลำดับ

```python
from microbit import *
import time

analog_pin = pin2

print( 'Analog read with UART output...' )
print( 'Press button A to stop...' )

# use UART with external pins (pin0 and pin1)
uart.init(baudrate=9600, tx=pin0, rx=pin1)

last_time = time.ticks_ms()
while True:
    if button_a.was_pressed():
        break
    now = time.ticks_ms()
    if time.ticks_diff(now, last_time) >= 500:
        last_time = now
        values = []
        for i in range(9):
            values.append( analog_pin.read_analog() )
        value = sorted(values)[4]
        uart.write( str(value) + '\r\n' )

# switch back to REPL serial-console
sleep(1.0)
uart.init(baudrate=115200)
print('Done...')
```

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

ข้อความที่ถูกส่งออกไปนั้น จะมีหนึ่งค่าตัวเลขต่อหนึ่งบรรทัด และถ้าใช้โปรแกร  **Arduino IDE - Serial Plotter** ก็จะเห็นกราฟเชิงเส้นจากข้อมูลที่ได้รับมาตามลำดับ

![รูปภาพ: การต่อวงจรทดลองใช้โมดูลเซ็นเซอร์](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MKxRRsQKl3TvIWsV57m%2F-MKxWDHx5LFIcUagL5Fi%2Fmicrobit_soil_sensor-1.jpg?alt=media\&token=a99199c7-8f5d-434c-acef-de61e2d2f6b5)

![รูปภาพ: ทดลองวัดความชื้นในดินในกระถางต้นไม้](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MKxRRsQKl3TvIWsV57m%2F-MKxWLzACvTjMIZqj5uu%2Fmicrobit_soil_sensor-2.jpg?alt=media\&token=6664673f-0f66-488a-b3e3-51fde1b3df39)

![รูปภาพ: การแสดงกราฟจากข้อมูลที่ได้รับ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MKxWUgpjs7PPMV_6TgC%2F-MKxWtizfNrjHVyCpQoT%2Fmicrobit_adc_uart_read-2.png?alt=media\&token=676b82fd-e7fe-4015-a057-163469748124)

จากรูปกราฟตัวอย่างที่ได้จากการทดลอง สังเกตได้ว่า มีช่วงเวลาหนึ่งที่ค่าตัวเลขลดต่ำลงอย่างรวดเร็วแล้วเพิ่มขึ้น ซึ่งเกิดขึ้นเนื่องจากได้มีการเทน้ำในช่วงเวลาสั้น ๆ ลงในกระถางบริเวณที่มีแท่งวัดของโมดูลเซ็นเซอร์เสียบอยู่ในดิน

{% hint style="info" %}
คำเตือน: การนำอุปกรณ์มาทดลองใช้งานนั้น เป็นไปเพื่อการสาธิตการทำงานของโค้ดตัวอย่างเท่านั้น ไม่เหมาะกับการนำไปใช้งานจริง ในสภาพแวดล้อมทั่วไป
{% endhint %}

## โค้ดตัวอย่างที่ 24: การตรวจสอบปริมาณการใช้หน่วยความจำแบบ Heap&#x20;

เมื่อมีการทำคำสั่งต่าง ๆ สร้างตัวแปรและอ็อปเจกต์สำหรับไมโครไพธอน จะมีการใช้หน่วยความจำ **SRAM** ของระบบที่ถูกจัดสรรไว้และเรียกว่า **Heap** ('ฮีป') ถ้าอ็อปเจกต์ใด ไม่ถูกอ้างอิงโดยตัวแปรหรือใช้งานอีกต่อไป เช่น โดยการใช้คำสั่ง `del` จะเป็นหน้าที่ของส่วนที่เรียกว่า **Garbage Collector** เพื่อจัดการและคืนหน่วยความจำ&#x20;

โค้ดตัวอย่างนี้ สาธิตการใช้คำสั่งเพื่อตรวจสอบสถานะการใช้หน่วยความจำสำหรับ **Heap** และเรียกใช้ [**Garbage Collector**](https://docs.micropython.org/en/latest/library/gc.html) (`gc`) ของไมโครไพธอน

```python
from microbit import *
import gc # use the garbage collector

def check_mem():
    # heap memory allocted and free memory for heap
    return gc.mem_alloc(), gc.mem_free()

mem_info_text = 'Used: {:4d}, Free: {:4d} bytes'
print( mem_info_text.format( *check_mem() ) )

gc.collect() # force the garbage collector to run
print( mem_info_text.format( *check_mem() ) )
```

มาดูผลการรันโค้ดและความแตกต่างระหว่างการใช้โปรแกรม **Mu Editor** และ **Thonny IDE** ตามลำดับ

เฟิร์มแวร์สำหรับไมโครไพธอนที่ใช้คือ

```python
MicroPython v1.9.2-34-gd64154c73 on 2017-09-01; micro:bit v1.0.1 with nRF51822
```

ถ้าใช้ **Mu-Editor v1.0.2** จะได้ข้อความเอาต์พุตดังนี้

```python
Used: 1104, Free: 8944 bytes
Used: 1056, Free: 8992 bytes
```

แต่ถ้าใช้ **Thonny IDE v3.2.6** จะเป็นดังนี้

```python
Used: 6192, Free: 3856 bytes
Used: 1424, Free: 8624 bytes
```

เราจะสังเกตเห็นความแตกต่างของปริมาณหน่วยความจำที่ใช้&#x20;

## โค้ดตัวอย่างที่ 25: การอ่านและแสดงค่าจากโมดูล SDS011 Dust Sensor

[**SDS011**](http://inovafitness.com/en/a/chanpinzhongxin/95.html) เป็นโมดูลเซ็นเซอร์ราคาถูก ประเภท **Air Quality Sensor / Laser Dust Sensor** ที่ได้มีการพัฒนาโดยบริษัท **Nova Fitness Co.Ltd.** ในประเทศจีน สามารถตรวจจับและวัดความเข้มข้นของฝุ่นละอองขนาดเล็ก **PM2.5** และ **PM10** ได้ มีหน่วยวัดเป็นไมโครกรัมต่อลูกบาศก์เมตร (µg/m^3) มีความละเอียดในการวัดค่า 0.3 μg/m^3

โมดูล **SDS011** ใช้แรงดันไฟเลี้ยง **+5Vdc (4.7\~5.3V)** และรับส่งข้อมูลผ่าน **Serial** โดยใช้ขา **TXD** และ **RXD** (วงจรลอจิกทำงานที่ระดับแรงดันไฟฟ้า 3.3V) และตั้งค่า **Baudrate** ไว้เท่ากับ **9600 8N1**

{% hint style="info" %}
ผู้อ่านควรศึกษาคู่มือหรือเอกสาร Datasheet ของโมดูล SDS011 ก่อนนำอุปกรณ์ไปต่อทดลองใช้งาน เพื่อให้ใช้งานได้อย่างถูกต้องและไม่เกิดความเสียหาย
{% endhint %}

โดยปรกติ โมดูลเซ็นเซอร์จะส่งข้อมูลออกมา 10 ไบต์ ทุก ๆ 1 วินาที โดยอัตโนมัติ มีลำดับข้อมูลไบต์ดังนี้&#x20;

* ไบต์ที่ 0: `0xaa`&#x20;
* ไบต์ที่ 1: `0xc0`&#x20;
* ไบต์ที่ 2 และ 3: ค่า **PM2.5** (low byte and high byte) นำไปหารด้วย 10
* ไบต์ที่ 4 และ 5: ค่า **PM10** (low byte and high byte) นำไปหารด้วย 10
* &#x20;ไบต์ที่ 6 และ 7: **Reserved**&#x20;
* ไบต์ที่ 7: **Checksum**&#x20;
* ไบต์ที่ 8: `0xab`

สูตรการคำนวณค่า **PM2.5** หรือ **PM10** จากข้อมูล 2 ไบต์ในแต่ละกรณี

$$
\mbox{PM value (ug/m^3)} = \frac{(\mbox{high byte} \times 256 ) +  \mbox{low byte}}{10}
$$

การเชื่อมต่อกับโมดูล **SDS011** มีขาของคอนเนกเตอร์ดังนี้

| Pin Name | Description                   |
| -------- | ----------------------------- |
| NC       | Not Connected                 |
| 1μm      | PM2.5: 0-999μg/m3, PWM Output |
| 5V       | 5V DC Input                   |
| 2.5μm    | PM10: 0-999 μg/m3, PWM Output |
| GND      | Ground                        |
| RXD      | RXD (3.3V logic level)        |
| TXD      | TXD (3.3V logic level)        |

ให้ใช้แรงดันไฟเลี้ยง **5V USB** สำหรับโมดูล **SDS011** และเชื่อมต่อกับบอร์ดไมโครบิตดังนี้

| SDS011 Pin | Microbit Pin          |
| ---------- | --------------------- |
| GND        | GND                   |
| RXD        | Pin0 (used as Tx pin) |
| TXD        | Pin1 (used as Rx pin) |

โค้ดตัวอย่างต่อไปนี้สาธิตการอ่านค่าจากโมดูล **SDS011** แล้วนำมาแสดงผลบนจอ **LCD 16x2 I2C (PCF8574)**

```python
from microbit import *
from lcd_pcf8574 import LCD
import time
import gc 

# initialize the I2C
i2c.init( freq=400000, sda=pin20, scl=pin19 )
lcd = LCD( i2c, 0x3f ) # set the address of PCF8574
lcd.reset() # reset LCD 
lcd.clear() # clear LCD

# use the UART with external pins (pin0 & pin1)
uart.init(baudrate=9600, tx=pin0, rx=pin1)

def get_data():
    # switch to UART with external pins
    uart.init(baudrate=9600, tx=pin0, rx=pin1)
    uart.read(64)    # flush serial input
    data = bytes([]) # clear data buffer
    display.show(Image.HEART_SMALL)
    while True:
        if button_a.is_pressed():
            break
        b = uart.read()
        if b:
            data += b
            if len(data) >= 10:
                break
    if data and len(data)==10:
        data = data[2:] # remove the first two bytes
        pm25 = ((data[1] << 8) | data[0])/10
        pm10 = ((data[3] << 8) | data[2])/10
        # calculate the checksum byte
        checksum = sum(b for b in data[0:6]) % 256
        if checksum == data[6] and data[7] == 0xab:
            return [pm25,pm10]
        else:
            return ['error']
    return None

data = None
while True:
    if button_a.is_pressed():
        break
    pm = get_data() # read sensor values
    if pm:
        uart.init(baudrate=115200) # switch to serial-REPL
        display.show(Image.HEART)
        text = []
        if len(pm)==2:
           text.append( 'pm2.5 {} ug/m3'.format(pm[0]) )
           text.append( ' pm10 {} ug/m3\n'.format(pm[1]) )
        elif len(pm)==1 and pm[0]=='error':
           text.append( 'checksum error' )
           text.append( 16*' ' )
        # show text lines on LCD
        for i in range(len(text)): 
            lcd.goto_line( i ) 
            lcd.print( text[i] )
            print( text[i] )
        time.sleep_ms(500)
        data = None

    time.sleep_ms(500)
    display.clear()

uart.init(baudrate=115200) # switch to serial-REPL
time.sleep_ms(500)
print('Done...')
```

การทำงานของโค้ดตัวอย่างนี้ จะต้องใช้ **UART** โดยเลือกใช้ขา `pin0` และ `pin1` ที่เชื่อมต่อและรับข้อมูลจากโมดูล **SDS011** จากนั้นเมื่อรับข้อมูลไบต์ได้ครบ 10 ไบต์แล้ว และตรวจสอบข้อมูลที่ได้รับว่าถูกต้อง จึงแปลงข้อมูลไบต์ให้เป็นค่าตัวเลขสำหรับ **PM2.5** และ **PM10** แล้วนำไปแสดงบนบนหน้าจอ **LCD** **16x2 แบบ I2C** (มีแอดเดรสของอุปกรณ์ตรงกับ `0x3f`) ถัดไปจึงมีการเปลี่ยนไปใช้ **UART** ที่เชื่อมต่อกับพอร์ต **USB** คอมพิวเตอร์ของผู้ใช้ เพื่อส่งข้อความไปยังหน้าต่าง **REPL Terminal** ของโปรแกรม **Mu Editor** ด้วยคำสั่ง `print()`&#x20;

ถ้าต้องการจบการทำงานของโปรแกรม ให้กดปุ่ม **Button A** ค้างไว้

![รูปภาพ: การใช้ Mu Editor ในการเขียนและอัปโหลดไฟล์ไมโครไพธอน](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MLAL1-SLCscXlYU5_SM%2F-MLAPgxNbfpvmkKWk3Cb%2Fmu-editor_sds011-1.png?alt=media\&token=e8268219-4994-4004-9f0e-c9a975e9cf6d)

![รูปภาพ: ตัวอย่างข้อความเอาต์พุตที่ได้รับผ่านทาง REPL ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MLAL1-SLCscXlYU5_SM%2F-MLAPonLnwCXqRHBQIb5%2Fmu-editor-sds011-2.png?alt=media\&token=9818958f-fc81-42c0-b92d-71414963725f)

![รูปภาพ: การต่อวงจรทดลองสาธิตการทำงานของโค้ดตัวอย่าง](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MLASWIKgiz_XBI_ftpV%2F-MLAZQ-6B1-zwN4ZUZTE%2Fsds011_microbit_demo.jpg?alt=media\&token=31bde880-855e-49a5-93ad-e250b82629c4)

## โค้ดตัวอย่างที่ 26: การอ่านค่าจากเซ็นเซอร์วัดอุณหภูมิแบบอินฟราเรด MLX90614

[**MLX90614**](https://www.melexis.com/-/media/files/documents/datasheets/mlx90614-datasheet-melexis.pdf) ของบริษัท **Melexis** เป็นเซ็นเซอร์วัดอุณหภูมิแบบไม่ต้องสัมผัส โดยใช้การตรวจจับรังสีอินฟราเรดจากวัตถุ (**Non-Contact Infrared Temperature Sensor**) แล้วนำมาคำนวณเพื่อให้ได้ค่าตัวเลขสำหรับอุณหภูมิ สื่อสารข้อมูลด้วยบัส **I2C** (ความเร็ว **100kHz** และมีแอดเดรสตรงกับ **0x5a**)

อุปกรณ์ที่ได้เลือกมาทดลองใช้งานคือ รุ่น **MLX90614ESF-BAA** **TO-39 Package** (โมดูล **GY-906 / HW-691**)  สามารถวัดอุณหภูมิอากาศแวดล้อม (**Ambient Temperature**) ในช่วง -40 °C…+85 ˚C และอุณหภูมิของวัตถุ (**Object Temperature**) ในช่วง -70 °C…+380 ˚C มีความแม่นยำในการวัด +/- 0.5 ˚C และความละเอียด 0.02 °C (อ้างอิงจากเอกสาร [**Datasheet**](https://www.melexis.com/-/media/files/documents/datasheets/mlx90614-datasheet-melexis.pdf))

การอ่านข้อมูลสำหรับอุณหภูมิ จะต้องระบุแอดเดรสของรีจิสเตอร์ที่เก็บข้อมูลแต่ละตัวขนาด 16 บิตภายใน **RAM** ขนาด **32x16** เช่น&#x20;

* `0x06` = TA (Ambient Temperature)
* `0x07` = TOBJ1 (Object Temperature, Zone 1)

ข้อมูลที่อ่านได้ขนาด 2 ไบต์ จะต้องนำมาแปลงให้เป็น **16-bit signed** แล้วคูณด้วย **0.02** และแปลงจากหน่วย **Kelvin** ให้เป็น **Celsius** ตามลำดับ

```python
from microbit import *
import time
from mlx90614 import *

i2c.init(freq=100000, sda=pin20, scl=pin19)
# print( [hex(a) for a in i2c.scan()] )

MLX_ADDR = 0x5a
mlx = MLX90614( MLX_ADDR )

while not button_a.is_pressed():
    ta, tobj = mlx.read_temp()
    if ta != None and tobj != None:
        print( '({:.2f},{:.2f})'.format(ta,tobj) )
    time.sleep_ms(1000)
```

โค้ดสำหรับไฟล์ **mlx90614.py** ที่จำเป็นต้องใช้ร่วมกับโค้ดสาธิตการทำงาน มีดังนี้

```python
from micropython import const
from microbit import i2c
import ustruct

class MLX90614:
    def __init__(self, addr=0x5a):
        self.addr = addr
        
    def _read_temp(self,reg):
        try:
            i2c.write(self.addr, bytes([reg]), repeat=True)
            data = i2c.read(self.addr, 3) # read 3 bytes
        except OSError as e:
            return None
        value  = ustruct.unpack('<H', data[0:2])[0]
        pec    = data[2]
        addr_w = (self.addr << 1)
        addr_r = (self.addr << 1) | 1
        byte_seq  =  [addr_w, reg, addr_r, data[0],  data[1]]
        crc8   = MLX90614._crc8( byte_seq, len(byte_seq) )
        if crc8 == pec:
            # note: 0.02 degrees per LSB
            return ((value * 0.02) - 273.15) # in Celsius
        else: # CRC8 failed
            return None
            
    def read_temp(self): 
        # read ambient and object temperature values
        return [self._read_temp(0x06), self._read_temp(0x07)]
        
    @staticmethod
    def _crc8(data, n):
        POLYNOMIAL = 0x07 # P(x)=x^8+x^2+x^1+1 = 100000111
        crc = 0x00 # init-value
        for i in range(n):
            crc ^= data[i]
            for j in range(8):
                if crc & 0x80:
                    crc = (crc << 1) ^ POLYNOMIAL
                else:
                    crc = (crc << 1)
        return crc & 0xff
```

จากการทำงานของโค้ดตัวอย่าง ข้อมูลที่ถูกส่งออกมาเป็นข้อความเอาต์พุต แสดงค่าตัวเลขสำหรับอุณหภูมิอากาศแวดล้อม และอุณหภูมิของวัตถุ เช่น สำหรับการทดลอง ได้นำแก้วที่ใส่กาแฟร้อน มาวางอยู่ห่างจากโมดูลเซ็นเซอร์ประมาณ 1 cm.&#x20;

![รูปภาพ: การต่อวงจรทดลองเพื่อสาธิตการทำงานของโค้ดตัวอย่าง](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MLG-Vtc2I2xE_0LN74R%2F-MLG4qzefW1xPmQey2ww%2Fmicrobit_mlx90614_demo-1.jpg?alt=media\&token=bd7e8e78-afdc-478c-8258-682451d92a6e)

เราสามารถใช้โปรแกรม **Mu Editor** รับข้อความและแสดงรูปกราฟได้ง่าย เนื่อจากข้อความแต่ละบรรทัดที่ถูกส่งออกมา มีตัวเลข 2 ค่า ได้แก่ อุณหภูมิอากาศแวดล้อม และอุณหภูมิของวัตถุ ตามลำดับ เราจะมองเห็นกราฟ 2 เส้น ในหน้าต่าง **Plotter** ของ **Mu Editor**&#x20;

![รูปกราฟ: การแสดงผลกราฟจากข้อความเอาต์พุตที่ได้รับ](https://969412697-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MIHfYo9IV3uTFm2tkDn%2F-MLFrRSlaty0aYA32u59%2F-MLFv3ttLb_sqsubyZiN%2Fmu_mlx90614_plotter.png?alt=media\&token=c862eb43-d960-44e8-9807-872a6fe30a4d)

เราพอจะมองเห็นการนำอุปกรณ์นี้ไปประยุกต์ใช้งาน เช่น การตรวจจับการเปลี่ยนแปลงอุณหภูมิของวัตถุ เปรียบเทียบกับอุณหภูมิอากาศแวดล้อม เป็นต้น&#x20;

{% hint style="info" %}
ข้อสังเกต: ปัจจัยอย่างเช่น ระยะห่างของวัตถุจากเซ็นเซอร์ สัมประสิทธิ์การแผ่รังสีความร้อน (**Emissivity**) ของวัตถุ มีผลต่อค่าของอุณหภูมิที่วัดได้
{% endhint %}

## กล่าวสรุป

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

อย่างไรก็ตาม เนื่องด้วยข้อจำกัดของตัวประมวลผลหลัก (**nRF51822**) ของบอร์ดไมโครบิต (บอร์ดเวอร์ชันแรก **v1.3** หรือ **v1.5**)  อย่างเช่น ความเร็วในการประมวลผล (**16MHz**) ความจุของหน่วยความจำ **Flash** และ **SRAM** ที่ค่อนข้างน้อย อีกทั้งเฟิร์มแวร์ของไมโครไพธอนที่ใช้งานได้กับบอร์ดไมโครบิต (เป็นเวอร์ชัน **MicroPython** **v1.9.2 Build 2017-09-01 / microbit v1.0.1**) ดังนั้นการนำไปใช้งานที่มีความซับซ้อน อาจจะไม่เหมาะสม เมื่อเปรียบเทียบกับบอร์ดไมโครคอนโทรลเลอร์ที่เป็นตัวเลือกอื่น เช่น **STM32** หรือ **ESP32** เป็นต้น

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