Micro:bit Code Examples
ตัวอย่างโค้ดไมโครไพธอนสำหรับบอร์ดไมโครบิต
การเรียนรู้จากตัวอย่างโค้ด ก็เป็นวิธีหนึ่งที่ช่วยทำให้รู้จักคำสั่งต่าง ๆ ของไมโครไพธอนสำหรับไมโครบิตได้ง่ายขึ้น ลองมาดูตัวอย่างโค้ดที่สามารถนำไปทดลองกับบอร์ดไมโครบิตได้
โค้ดตัวอย่างที่ 1: การแสดงข้อความและสัญลักษณ์บน 5x5 LED Display
การทำงานของโค้ดตัวอย่างนี้ เริ่มต้นด้วยการแสดงข้อความเป็นภาษาอังกฤษ "Hello, World!"
ด้วยคำสั่ง display.scroll()
(ทำเพียงครั้งเดียว) เนื่องจากมีหลายตัวอักษร จึงใช้วิธีแสดงทีละตัว แล้วเลื่อนจากขวาไปซ้าย จนครบตัวอักษรสุดท้ายของข้อความ
ในการแสดงข้อความแบบเลื่อนไป โดยใช้คำสั่ง display.scroll()
เราสามารถกำหนดช่วงเวลาในการอัปเดตสกรีน 5x5 LED Display ได้ด้วย เช่น 100 (หน่วยเป็นมิลลิวินาที) ถ้าต้องการให้เกิดการเปลี่ยนแปลงช้าลง ก็ให้เพิ่มค่าตัวเลข
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 เพื่อทำขั้นตอนซ้ำ ได้แก่ การแสดงรูปสัญลักษณ์ 'หัวใจ' หรือImage.HEART
โดยใช้คำสั่ง display.show()
และให้เว้นช่วงเวลาประมาณ 1000 มิลลิวินาที โดยใช้คำสั่ง sleep()
ตามด้วยคำสั่ง display.clear()
เพื่อเคลียร์การแสดงผล
ข้อสังเกต: คำสั่งต่าง ๆ ที่เกี่ยวข้องกับไมโครไพธอนสำหรับไมโครบิต จะอยู่ภายใต้ชื่อของแพคเกจหรือโมดูลชื่อ microbit
ดังนั้นในบรรทัดแรกของโค้ด จึงมีคำสั่ง from microbit import *
ถ้าต้องการลองใช้รูปไอคอนของไมโครไพธอนที่ได้มีการกำหนดไว้แล้ว (Built-in Images) ก็สามารถดูได้จากเอกสารในหัวข้อ Images ตัวอย่างเช่น รูปกราฟิกแสดงอารมณ์ความรู้สึก
Image.HAPPY
(รู้สึกมีความสุข)Image.SMILE
(รูปหน้ายิ้ม)Image.SAD
(รู้สึกเศร้า)Image.CONFUSED
(รู้สึกสับสน)Image.ANGRY
(รู้สึกโกรธ)Image.ASLEEP
(รู้สึกง่วงนอน)Image.SURPRISED
(รู้สึกประหลาดใจ)
โค้ดต่อไปนี้ สาธิตการแสดงรูปสัญลักษณ์ โดยระบุไว้ในอาร์เรย์ (Array) หรือ List ในภาษาไพธอน และจะเลือกมาแสดงผลทีละรูปตามลำดับ แล้ววนซ้ำไปเรื่อย ๆ
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()
การใช้คำสั่ง Image()
เป็นการกำหนดค่าพิกเซลขนาด 5x5 (หนึ่งพิกเซลสำหรับ LED หนึ่งดวง) มีค่าความสว่างได้ 0..9 (0=LED OFF และ 9=สว่างมากที่สุด) ในแต่ละแถว เรียงจากซ้ายไปขวา และเรียงจากแถวบนลงล่าง
ยกตัวอย่าง เช่น ถ้าต้องการสร้างรูปกากบาท (Cross Mark) เราก็กำหนดรูปกราฟิกดังนี้
from microbit import *
# define a cross-mark image
image_x = Image("90009:09090:00900:09090:90009")
# display the image
display.show( image_x )

โค้ดตัวอย่างที่ 2: การสุ่มเลข 1-6 เมื่อกดปุ่มหรือเขย่าบอร์ด
ถ้าต้องการทำให้บอร์ดไมโครบิต มีพฤติกรรมการทำงานเหมือน (Electronic Dice) เช่น เมื่อกดปุ่ม A หรือเขย่าบอร์ด (Shaking) จะทำให้เกิดการสุ่มเลข เหมือนการทอยลูกเต๋า จะเขียนโค้ดอย่างไร ?
โค้ดตัวอย่างนี้ เมื่อมีการกดปุ่ม A ในแต่ละครั้ง จะมีการสุ่มเลขที่เป็นจำนวนเต็ม (Integer) ที่จะได้ค่าอยู่ระหว่าง 1 ถึง 6 โดยใช้คำสั่ง random.randint()
เก็บไว้ในตัวแปรชื่อ number
แล้วจึงนำค่านี้ไปแสดงผล โดยเรียกใช้ฟังก์ชัน showRandomNumber()
ที่ได้สร้างขึ้นเอง แต่ถ้ามีการกดปุ่ม B ก็จะเป็นการเคลียร์การแสดงผลบน LED Display ด้วยคำสั่ง display.clear()
ข้อสังเกต: การใช้คำสั่งของ random
เพื่อสุ่มเลขนั้น ไม่ใช่การสุ่มเลขที่แท้จริง (True Random Number Generation) แต่ใช้อัลกอริทึมทางคณิตศาสตร์ในการกำหนดตัวเลขในลำดับถัดไป (จึงเป็นแบบ Pseudo-random Number Generation)
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()
ก่อนเริ่มใช้คำสั่งของ random
สำหรับการสุ่มตัวเลข ในตัวอย่างนี้ ได้ใช้ค่าเลขจำนวนเต็มที่อ่านได้จากขา P0 แบบแอนะล็อกของบอร์ดไมโครบิต (แต่ที่ขา P0 ไม่ได้ต่อใช้งาน) มาใช้เป็นค่าเริ่มต้น
การตรวจสอบเงื่อนไขไปตามลำดับนั้น เราได้ใช้ประโยคคำสั่งแบบ if-elif โดยมีเงื่อนไขในการตรวจสอบ
ดูว่า มีการกดปุ่ม A หรือไม่ ซึ่งเราจะใช้คำสั่ง
button_a.was_pressed()
ถัดไปดูว่า มีการกดปุ่ม B หรือไม่ โดยใช้คำสั่ง
button_b.was_pressed()
คำถามสำหรับการทดลองโดยใช้ฮาร์ดแวร์:
ถ้าไม่ได้ต่อขา P0 กับวงจรใด ๆ (ไม่มีการใช้งานกับวงจรภายนอก) และอ่านอินพุตแบบแอนะล็อกจากขา P0 ของบอร์ดไมโครบิต จะได้ค่าที่แตกต่างกันไปหรือไม่ ? ลองมาดูโค้ดตัวอย่าง เมื่อกดปุ่ม A แต่ละครั้ง จะอ่านค่าจากอินพุต P0 เช่น ทั้งหมด 10 ครั้ง
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)

เมื่ออัปโหลดโค้ดไปยังบอร์ด ให้กดปุ่ม Open Serial เพื่อรับค่าจากบอร์ด ซึ่งจะเป็นข้อความที่ถูกส่งกลับมายังคอมพิวเตอร์ โดยใช้คำสั่ง print()
แต่ถ้าต้องการปิดการรับข้อมูลจาก Serial ให้กดปุ่ม Close Serial
ลองกดปุ่ม A หลายครั้ง โดยไม่ใช้นิ้วสัมผัสที่ขา P0 ที่บริเวณ Edge Connector และอยู่ใกล้ปุ่ม A) และเปรียบเทียบกับการใช้นิ้วสัมผัส
โค้ดตัวอย่างถัดไป ได้เปลี่ยนจากการแสดงตัวเลข ให้เป็นการแสดงรูปกราฟิกแทน (ใช้จำนวนจุด หรือ Dots ที่มีจำนวนตามตัวเลขที่สุ่มได้) ในตัวอย่างนี้ เราสุ่มเลขที่มีค่าระหว่าง 1 ถึง 6 ไว้ในตัวแปร number
แล้วนำค่าที่ได้นี้ (ลบออก 1) ไปใช้อ้างอิง Image
จากตัวแปรแบบอาร์เรย์ image_list
ซึ่งเริ่มต้นนับที่ index
เท่ากับ 0
ในการตรวจสอบเหตุการณ์หรือเงื่อนไข เพื่อดูว่า มีการเขย่าบอร์ดเกิดขึ้นหรือไม่ เราจะใช้คำสั่ง accelerometer.is_gesture()
ที่ระบุว่าเป็น "shake" ถ้ามีการเขย่าบอร์ด ให้ตรวจสอบดูเงื่อนไขถัดไปในการอัปเดตค่าสุ่มตัวเลขถัดไปคือ จะต้องเกิดขึ้นหลังจากครั้งที่แล้วอย่างน้อย 1000 มิลลิวินาที (msec)
โค้ดตัวอย่างมีการใช้คำสั่ง time.ticks_ms()
เพื่ออ่านค่าเวลาของระบบ (หน่วยเป็น msec) และใช้คำสั่ง time.ticks_diff()
เพื่อหาผลต่างระหว่างช่วงเวลา (Time Difference) ผลต่างของค่าตัวเลขเวลาในปัจจุบันกับค่าตัวเวลาในอดีตที่บันทึกเก็บไว้ (ผลต่างระหว่าง now_shake
กับ last_shake
) จะต้องมีค่ามากกว่า 1000 มิลลิวินาที
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()

โค้ดตัวอย่างที่ 3: สุ่มตำแหน่งเพื่อทำให้ LED สว่าง 5 จุด
ตัวอย่างถัดไปสาธิตการสุ่มหาตำแหน่ง (x,y) บน LED Matrix Display ซึ่งจะมีทั้งหมด 5 ตำแหน่งที่ทำให้ LED สว่าง (ON) โดยมีเงื่อนไขว่า ในแต่ละแถวแนวนอนหรือแนวตั้ง จะต้องไม่ซ้ำกัน
ในการกำหนดสถานะ ON/OFF หรือกำหนดค่าความสว่าง (0..9) ให้ LED ในตำแหน่งหรือพิกัดที่ต้องการ เราจะใช้คำสั่ง display.set_pixel()
การกดปุ่ม B จะเปลี่ยนโหมดการสุ่มตำแหน่งและอัปเดตไปเรื่อย ๆ (Auto mode) หรือ โหมดที่ต้องกดปุ่ม A เพื่อสุ่มตำแหน่งใหม่ในแต่ละครั้ง (Manual Mode) ในโค้ดตัวอย่างนี้ จะเห็นได้ว่า มีการสาธิตการใช้โครงสร้างข้อมูลในภาษาไพธอน อย่างเช่น เซต (Set) และอาร์เรย์สองมิติ (Two-dimensional Array)
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 )

โค้ดตัวอย่างที่ 4: การเอียงบอร์ดเพื่อทำให้ LED สว่างทุกดวง
ตัวอย่างถัดไปสาธิตการทำให้ LED สว่าง ทีละดวง เริ่มต้นจากดวงแรกที่พิกัด (2,2) ซึ่งอยู่ตรงกลางบอร์ด และมีการกระพริบที่ LED เพื่อระบุตำแหน่งพิกัดขณะนั้น
ถ้ามีการเอียงบอร์ดไปในทิศทางใด จะทำให้ตำแหน่งของ LED ที่กระพริบเปลี่ยนไป และ LED ในตำแหน่งก่อนหน้านั้น จะเปลี่ยนจาก OFF (0) เป็น ON (1) และจะเห็นว่า มีจำนวน LED ที่อยู่ในสถานะ ON เพิ่มมากขึ้น
เมื่อทำให้ LED ทุกดวงสว่างครบแล้ว (5*5 = 25 ดวง) โดยการเอียงบอร์ดตามแกนอ้างอิง x หรือ y จะเห็นได้ว่า หลังจากนั้น LED ทุกดวงกระพริบ ถ้าต้องการจะเริ่มต้นใหม่ ให้กดปุ่ม A
การตรวจสอบดูว่า มีการเอียงบอร์ดไปทางใด เราจะใช้คำสั่งของ accelerometer
เช่น get_x()
และ get_y()
อ่านค่าสำหรับแกน x และ y ถ้าวางบอร์ดในแนวราบ ค่าที่อ่านได้สำหรับแกน x และ y จะเข้าใกล้ 0 แต่
ถ้ามีการเอียงบอร์ดไปทางด้านหนึ่ง ค่าจะเพิ่มเป็นบวกมากขึ้น แต่ถ้าเอียงไปอีกด้านหนึ่งค่าจะลดลง ได้ค่าตัวเลขเป็นลบ (น้อยกว่า 0)
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 สำหรับไมโครบิต โดยสามารถระบุได้ว่า จะใช้แถบ RGB LED จำนวนกี่ดวง หรือระบุตำแหน่งทั้งหมด ในตัวอย่างนี้ จะใช้เพียงหนึ่งตำแหน่ง (ดวงเดียว) โดยทำคำสั่ง NeoPixel(pin1, 1)
และเลือกใช้ขา P1 ต่อไปยังขาสัญญาณอินพุตควบคุมของโมดูล NeoPixel ที่มีเพียงหนึ่งพิกเซล RGB (หนึ่งตำแหน่ง)
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()

จากรูปภาพจะเห็นได้ว่า มีการใช้โมดูลของ 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%
บอร์ดไมโครบิต สามารถสร้างสัญญาณ PWM ที่กำหนดค่า Duty Cycle ได้ มีขนาดความละเอียด 10 บิต ซึ่งเป็นค่าในช่วง 0..1023 (Duty Cycle 0% .. 100%) และสามารถกำหนดคาบสัญญาณได้เช่นกัน (ต่ำสุดคือ 256 ไมโครวินาที) ถ้าเพิ่มคาบสัญญาณให้กว้างขึ้น ก็จะได้สัญญาณ PWM ที่มีความถี่ต่ำลง
ในตัวอย่างนี้ เราจะใช้คำสั่ง write_analog()
สำหรับ Microbit Pin สร้างสัญญาณ PWM และกำหนดให้มีความถี่ 500 Hz (มีคาบ 2 msec หรือ 2000 usec) จำนวน 3 ช่องสัญญาณ (ใช้ขา P0, P1, P2 เป็นเอาต์พุต) เพื่อนำไปขับวงจรหรือโมดูล RGB LED ทำให้เปลี่ยนสีได้
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

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

โมดูลที่ได้เลือกมาใช้งาน มีทั้งหมด 5 ปุ่ม มีข้อความเขียนกำกับเอาไว้คือ SW1, SW2, ..., SW5 ถ้าอ่านค่าจากขา P0 แบบแอนะล็อก โดยใช้คำสั่ง read_analog()
จะได้ค่าในช่วง 0..1023 ซึ่งก็ขึ้นอยู่กับสถานะการกดปุ่ม และนำมาใช้ในการจำแนกหรือตรวจสอบดูว่า ปุ่มใดกำลังถูกกดอยู่ในขณะนั้น
ให้ทดลองอ่านค่าแล้วส่งออกมาทาง Serial ด้วยคำสั่ง print()
และทำซ้ำไปเรื่อย ๆ แล้วกดไปทีละปุ่ม เราจะเห็นค่าอินพุตที่อ่านได้สำหรับแต่ละกรณี ยกตัวอย่างเช่น ถ้ายังไม่กดปุ่มใด ๆ เลย ค่าที่อ่านได้จะอยู่ในช่วงประมาณ 800 ถึง 850 เป็นต้น

โค้ดตัวอย่างที่ 8: การตรวจสอบการใช้นิ้วสัมผัสที่ Touch Pad
ตัวอย่างนี้สาธิตการตรวจสอบดูว่า มีการใช้นิ้วสัมผัสที่บริเวณ Touch Pad 0 ของบอร์ดไมโครบิตหรือไม่ (และให้ใช้อีกนิ้วหนึ่ง สัมผัสที่ Pad GND ของบอร์ด) โดยใช้คำสั่ง pin0.is_touched()
ซึ่งจะได้ค่าแบบ boolean
ถ้ามีการสัมผัสด้วยนิ้วในแต่ละครั้งที่ขา P0 จะทำให้สถานะของ LED Display เปลี่ยนไป ซึ่งมีอยู่ 4 รูปแบบ หรือ ระดับ ดังนี้
ระดับที่ 1 เริ่มต้น ไม่มี LED อยู่ในสถานะ ON
ระดับที่ 2 มี LED เพียง 1ดวง (ตรงกลาง) ในสถานะ ON
ระดับที่ 3 มี LED จำนวน 3x3 ดวงในสถานะ ON
ระดับที่ 4 มี LED จำนวน 5x5 ดวง (ทุกดวง) ในสถานะ ON
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 ที่ใช้สายสัญาณเพียง 2 เส้นคือ SDA (Serial Data Line) และ SCL (Serial Clock Line) ไมโครบิตจะทำหน้าที่เป็นอุปกรณ์ I2C Master ซึ่งเป็นฝ่ายเริ่มต้น หรือควบคุมการทำงานของอุปกรณ์อีกฝ่ายหนึ่งคือ I2C Slave เช่น ต้องการจะเขียน (Write Operation) หรืออ่านข้อมูล (Read Operation) เป็นต้น นอกจากนั้นจะต้องมีการระบุแอดเดรสขนาด 7 บิต (7-bit Slave Address) ของอุปกรณ์แต่ละตัวในระบบบัสเดียวกัน
การเขียนโค้ดเพื่อใช้งานบัส I2C ก็มีคำสั่งในกลุ่ม i2c ไว้ให้ใช้งาน โดยทั่วไป เราก็ใช้ความเร็ว 100kHz หรือ 400kHz ในการสื่อสารสำหรับบัส I2C
บอร์ดไมโครบิตมีวงจรหรือไอซีที่เชื่อมต่อด้วยบัส I2C อยู่แล้ว ได้แก่ ไอซีวัดความเร่ง (Accelerometer IC) และไอซีเข็มทิศดิจิทัล (Digital Compass IC) เป็นต้น แต่ถ้าเราจะนำอุปกรณ์ I2C Slave ตัวอื่น มาต่อเพิ่ม ก็ให้เลือกใช้ขา Pin 20 และ Pin 19 ของบอร์ดไมโครบิต เป็นขาสัญญาณ SDA และ SCL ตามลำดับ และอุปกรณ์เหล่านั้นจะต้องทำงานที่แรงดันไฟฟ้า +3.3V เช่นกัน
ลองมาดูตัวอย่างการเขียนโค้ด เพื่อตรวจสอบหรือสแกนอุปกรณ์ (Device Scan) และแสดงหมายเลขแอดเดรส (ฐานสิบหก) ของอุปกรณ์ Slave Device ในระบบบัส I2C ของบอร์ดไมโครบิต ส่งเป็นข้อความออกทาง Serial โดยแบ่งเป็นสองวิธี
สร้างฟังก์ชัน
scan_i2c()
ที่เราสร้างขึ้นเองแล้วเรียกใช้ หรือเรียกใช้ฟังก์ชัน
i2c.scan()
ซึ่งมีไว้ให้แล้ว
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)

ถ้าทำงานได้ถูกต้อง จะต้องพบอุปกรณ์หมายเลข 0x0e
(Compass) และ 0x1d
(Accelerometer) สำหรับบอร์ดไมโครบิตเวอร์ชัน 1.3B
โค้ดตัวอย่างที่ 10: การอ่านค่าจากโมดูลเซ็นเซอร์แสง BH1750
ตัวย่างถัดไปเป็นการอ่านข้อมูลจากโมดูลเซ็นเซอร์วัดแสง BH1750 (GY-302) ซึ่งจะให้ค่าที่มีความละเอียด 16 บิต (0..65535) หน่วยเป็นลักซ์ (Lux) เมื่อเริ่มต้นก่อนใช้งาน จะต้องส่งคำสั่งไปกำหนดโหมดการทำงานของ BH1750
ในตัวอย่างนี้ ได้ใช้โหมด Continuous Measurement ความละเอียด 1.0 Lux ต่อหนึ่งบิต (รายละเอียดเชิงเทคนิคเกี่ยวกับ BH1750 แนะนำให้ศึกษาจากเอกสาร Datasheet ของผู้ผลิต)
โมดูลที่นำมาใช้งานนั้น เชื่อมต่อแบบบัส I2C ใช้แรงดันไฟเลี้ยง 3.3V และมีหมายเลขแอดเดรสของอุปกรณ์คือ 0x23
(= 35 decimal) ซึ่งเป็น Default Address

โมดูลนี้มีขา ADDR ถ้าต่อขาดังกล่าวด้วยสายไฟไปยังขา VCC (3.3V) จะได้แอดเดรสเป็น 0x5C
(92 dec) แต่ถ้าต่อไปยังขา GND หรือปล่อยไว้ไม่ต้องต่อขา (Not Connected) จะได้แอดเดรสเป็น 0x23
(25 dec)
ตัวอย่างโค้ดนี้ จะอ่านข้อมูลจากโมดูลเซ็นเซอร์ทั้งหมด 10 ครั้ง ในแต่ละครั้งจะส่งข้อความแสดงค่าที่อ่านได้ออกทาง Serial โดยใช้คำสั่ง print()
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')


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

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

โค้ดตัวอย่างนี้ สาธิตการอ่านค่าจาก Wii Nunchuk มีดังนี้
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*'-' )

จากโค้ดตัวอย่างนี้ เราสามารถนำไปประยุกต์ใช้ในการเล่นหรือควบคุมเกมคอมพิวเตอร์ เช่น ใช้คันโยกแกน X และ Y รวมถึงปุ่มกด C และ Z เป็นต้น
โค้ดตัวอย่างที่ 12: การอ่านค่าจากโมดูลเซ็นเซอร์ SHT31
โค้ดในตัวอย่างนี้ สาธิตการอ่านค่าจากโมดูลเซ็นเซอร์ SHT31-DIS สำหรับวัดอุณหภูมิและความชื้นสัมพัทธ์ (ใช้ไอซีที่ผลิตโดยบริษัท SENSIRION) และแสดงค่าที่อ่านได้เป็นข้อความเอาต์พุตทาง Serial โดยใช้คำสั่ง print()
# 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()
เพื่ออ่านค่าตัวเลขสำหรับอุณหภูมิและความชื้นสัมพัทธ์
โค้ดอีกหนึ่งไฟล์ (sht3x.py) ที่จะต้องนำไปบันทึกลงใน Flash Storage ของไมโครบิต เพื่อใช้ร่วมกับโค้ดตัวอย่างข้างบน มีดังนี้
# 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
ลำดับขั้นตอนการทดสอบโค้ดกับบอร์ดไมโครบิตโดยใช้ Mu Editor
สร้างไฟล์ใหม่ โดยกดปุ่ม New แล้วเขียนโค้ดตามตัวอย่างและบันทึกลงในไฟล์ชื่อ sht3x.py ในคอมพิวเตอร์ของผู้ใช้
กดปุ่ม Files แล้วเลือกไฟล์ sht3x.py จากรายการไฟล์ในคอมพิวเตอร์ของผู้ใช้ แล้วลากไปยังบอร์ดไมโครบิต (Drag & Drop เพื่อสำเนาไฟล์)
เขียนโค้ดตัวอย่าง main.py แล้วอัปโหลดไปยังบอร์ดไมโครบิต โดยกดปุ่ม Flash แล้วจึงกดปุ่ม REPL
ในบริเวณช่องรับคำสั่งของ REPL ให้กดปุ่มบนแป้นพิมพ์ Ctrl+D เพื่อเริ่มต้นการทำงานของไมโครไพธอนใหม่อีกครั้ง และรันโค้ด main.py โดยอัตโนมัติ
สังเกตข้อความเอาต์พุตที่ปรากฏในบริเวณ REPL



โค้ดตัวอย่างที่ 13: SHT31 + I2C 16x2 LCD
โค้ดตัวอย่างนี้สาธิตการใช้งานโมดูล 16x2 LCD (แสดงผลข้อความแบบ Alphanumeric มี 2 แถว ๆ ละ 16 ตัวอักษร) แบบ I2C ที่ใช้ไอซี PCF8574 เป็นตัวควบคุมการทำงาน และอ่านค่าจากโมดูลเซ็นเซอร์ SHT31 ผ่านทาง I2C เช่นเดียวกันแล้วนำค่าที่ได้มาแสดงเป็นข้อความบนจอโมดูล LCD
โมดูล SHT31 มีการกำหนดแอดเดรสไว้เท่ากับ 0x44
ในขณะที่โมดูล 16x2 LCD-PCF8574 มีแอดเดรสเท่ากับ 0x3F
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 มีดังนี้
# 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 )


โค้ดตัวอย่างที่ 14: การสร้างรูปกราฟิกบน LED Matrix
โค้ดตัวอย่างต่อไปนี้ สาธิตการสร้างรูปกราฟิกตามรูปแบบที่กำหนดโดยเงื่อนไข โดยใช้ค่าของพิกัด (x,y) บนแผง 5x5 LED Matrix (N=5)
ฟังก์ชัน create_pattern(..)
ใช้สำหรับสร้างข้อมูลอาร์เรย์ data
ที่มีข้อมูลสมาชิกเป็น 0 หรือ 1 และมีจำนวนเท่ากับ 5x5 (25) ค่าของอาร์กิวเมนต์ i
สำหรับฟังก์ชันนี้ เป็นเลขจำนวนเต็ม i=0,1,...,6
จะเป็นตัวเลือกว่า ต้องการสร้างข้อมูลในอาร์เรย์เป็นแบบใด
ฟังก์ชัน conditions(..)
ทำหน้าที่ระบุสถานะที่พิกัด (x,y) ของ LED Matrix ตามเงื่อนไขที่กำหนดไว้ และ i
เป็นอาร์กิวเมนต์ เพื่อกำหนดรูปแบบ หรือ กรณีในการสร้างรูปกราฟิก ฟังก์ชันนี้จะถูกเรียกใช้ในฟังก์ชัน create_pattern(..)
ฟังก์ชัน show_pattern(..)
ใช้สำหรับนำค่าที่ได้จากอาร์เรย์ data
ไปใช้เพื่อแสดงสถานะของ LED Matrix บนบอร์ดไมโครบิต ถ้ามีสถานะเป็น 0 หมายถึง OFF แต่ถ้ามีค่าเท่ากับ 1 หมายถึง ON (มีค่าของความสว่าง 9)
เมื่อทดสอบการทำงานของโค้ด ให้กดปุ่ม A เพื่อเปลี่ยนรูปแบบกราฟิกในลำดับถัดไป
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 )




โค้ดตัวอย่างที่ 15: Conway's Game of Life
โค้ดตัวอย่างถัดไป สาธิตการจำลองสถานการณ์ที่เรียกว่า Conway's Game of Life ซึ่งกล่าวถึง ระบบที่ประกอบด้วยเซลล์ (Cells) ที่ถูกจัดเรียงแบบเมตริกซ์ (อาร์เรย์สองมิติ) แต่ละเซลล์ที่พิกัด (x,y) มีสถานะเป็น 0 หรือ 1 ซึ่ง 0 หมายถึง ไม่มีชีวิต (dead) และ 1 หมายถึง เซลล์กำลังมีชีวิตอยู่ (alive)
สถานะเริ่มต้นของเซลล์ อาจได้จากการสุ่ม (Randomization of Cell States) และสถานะของเซลล์ที่เปลี่ยนแปลงไปตามเวลา (เวลาเป็นแบบ discrete-time) ขึ้นอยู่กับสถานะของเซลล์โดยรอบ (Neighbouring Cells) โดยทั่วไปแต่ละเซลล์ในอาร์เรย์สองมิติ จะมีเซลล์ที่อยู่รอบ ๆ ติดกัน ไม่เกิน 8 เซลล์ (มีอยู่รอบทิศ)
การกำหนดสถานะของเซลล์ในลำดับเวลาถัดไป เป็นไปตามกฎ (Rules) ได้ดังนี้
ถ้าเซลล์นั้นมีชีวิต และมีจำนวนเซลล์รอบ ๆ ที่มีชีวิตอยู่ เท่ากับ 2 หรือ 3 ให้เซลล์นั้นยังคงมีชีวิตต่อไป (Healthy Population)
ถ้าเซลล์นั้นมีชีวิต แต่มีจำนวนเซลล์รอบ ๆ ที่มีชีวิตอยู่ น้อยกว่า 2 หรือ มากกว่า 3 ให้เซลล์นั้นตายไป (กรณีนี้เรียกว่า Underpopulation และ Overpopulation ตามลำดับ)
ถ้าเซลล์นั้นไม่มีชีวิตหรือตายไปแล้ว แต่มีจำนวนเซลล์รอบ ๆ ที่มีชีวิตอยู่ เท่ากับ 3 ให้เซลล์นั้นเริ่มต้นหรือกลับมามีชีวิตใหม่อีกครั้ง (กรณีนี้เรียกว่า Reproduction)
ในโค้ดตัวอย่างนี้ ขนาดของเมตริกซ์เท่ากับ 5x5 (N=5) แต่การเก็บสถานะของเซลล์ จะใช้อาร์เรย์มิติเดียว (One-dimensional Array หรือ List) โดยใช้ชื่อตัวแปรว่า cells
ดังนั้นสถานะของเซลล์ที่พิกัด (x,y) ในอาร์เรย์ดังกล่าวคือ cells[x + N*y]
# 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 ของสัญญาณดังกล่าว)
ถ้ากำหนดให้ Duty Cycle = 25% 50% และ 75% โดยทางทฤษฏี จะได้ความกว้างเท่ากับ 5000, 10000 และ 15000 ไมโครวินาที ตามลำดับ
สัญญาณเอาต์พุตที่ขา pin8
จะถูกป้อนกลับเข้าที่ขา pin11
ซึ่งถูกใช้เป็นขาอินพุต-ดิจิทัล (ในการทดลอง สามารถใช้ลวดสายไฟ Jumper Wire เชื่อมต่อระหว่างขาทั้งสอง)
การวัดความกว้างของพัลส์ช่วงที่เป็น 1 สามารถทำได้ง่ายโดยใช้คำสั่ง time_pulse_us()
ของกลุ่มคำสั่ง machine
การเรียกใช้คำสั่งนี้ จะต้องระบุขา (Pin) ที่ต้องการใช้ เลือกประเภทของพัลส์เป็น High (1) หรือ Low (0) และกำหนดค่าตัวเลขสำหรับ Timeout (หน่วยเป็นไมโครวินาที)
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)


โค้ดตัวอย่างที่ 17: การวัดระยะห่างจากสิ่งกีดขวางโดยใช้โมดูล Ultrasonic Sensor HC-SR04P
HC-SR04P เป็นโมดูลเซ็นเซอร์ที่ใช้สัญญาณคลื่นเสียงอัลตราโซนิค (ความถี่สูง ประมาณ 40kHz) ในการตรวจสอบและวัดระยะห่างจากวัดถุกีดขวาง
หลักการทำงานของเซ็นเซอร์ประเภทนี้คือ การใช้ตัวส่ง (Transmitter) สร้างสัญญาณที่เป็นคลื่นเสียงออกไป เมื่อไปกระทบวัตถุ จะเกิดคลื่นสะท้อนกลับมายังตัวรับ (Receiver) เมื่อจับเวลาการเดินทางของคลื่นเสียงในทิศทางไปและกลับ และกำหนดอัตราเร็วของคลื่นเสียงในอากาศ (เช่น 340 เมตร/วินาที โดยประมาณ) ก็จะสามารถคำนวณระยะห่างจากวัตถุได้
โมดูล HC-SR04P สามารถวัดระยะห่างจากวัตถุได้สูงสุด ประมาณ 4 เมตร และใช้แรงดันไฟเลี้ยง 3.3V หรือ 5V ได้ โมดูลนี้มีขาอินพุต TRIG (Trigger) และขาเอาต์พุต ECHO เมื่อได้รับสัญญาณพัลส์ (ความกว้างอย่างน้อย 10 ไมโครวินาที) ที่ขา TRIG จะมีการสร้างสัญญาณคลื่นเสียงออกไป หลังจากนั้นจะเกิดสัญญาณพัลส์ที่ขา ECHO ความกว้างของสัญญาณพัลส์ที่ขา ECHO จะเป็นตัวระบุระยะเวลาการเดินทางของคลื่นเสียงและสะท้อนกลับมาถึงตัวรับ
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
จากนั้นจะต้องแปลงค่าที่ได้วัดได้สำหรับระยะเวลา (มีหน่วยเป็นไมโครวินาที) ให้เป็นระยะทาง (มีหน่วยเป็นเซนติเมตร)
โค้ดตัวอย่างนี้เลือกใช้ขา pin12
สำหรับสัญญาณ ECHO และ pin13
สำหรับ TRIG ตามลำดับ โดยจะทำการวัดค่าที่เป็นระยะห่างจากวัตถุซ้ำไปเรื่อย ๆ จนกว่าจะหยุดเมื่อมีการกดปุ่ม Button A

โค้ดตัวอย่างที่ 18: การใช้งานไอซี MCP4921 SPI DAC
MCP4921 ของบริษัท Microchip เป็นไอซีประเภท DAC (Digital-to-Analog Converter) มีเพียงเอาต์พุต 1 ช่องสัญญาณ (แต่ถ้าเป็น MCP4922 จะมี 2 ช่อง) มีความละเอียดของข้อมูล เท่ากับ 12 บิต (4096 ระดับ) และรับข้อมูลโดยใช้บัส SPI
ตัวอย่างนี้ สาธิตการสร้างสัญญาณรูปไซน์ (Sinusoidal Waveform) หนึ่งคาบ โดยใช้ไอซี MCP4921 DAC สร้างสัญญาณเอาต์พุตแบบแอนะล็อก ใช้แรงดันไฟเลี้ยงและแรงดันอ้างอิง (Reference Voltage) เท่ากับ +3.3V

ถ้าจะลองต่อวงจรบนเบรดบอร์ด ก็ให้ใช้ตัวถังของไอซีแบบ PDIP-8 มี 8 ขา ดังนี้
VDD เป็นขาแรงดันไฟเลี้ยง
/CS เป็นขาสัญญาณอินพุต Chip-Select ทำงานแบบ Active-Low
SCK เป็นขาสัญญาณอินพุต Serial Clock Input (ความถี่สูงสุดที่ใช้ได้คือ 20MHz)
SDI เป็นขาอินพุตสำหรับข้อมูลที่เลื่อนเข้าทีละบิต Serial Data Input
/LDAC เป็นขาอินพุตสำหรับสัญญาณควบคุม DAC Ouput Latch ทำงานแบบ Active-Low โดยทั่วไป ให้ต่อกับ GND ของวงจร เพื่อให้เอาต์พุตที่การเปลี่ยนแปลงโดยอัตโนมัติหลังจากเขียนข้อมูล 16 บิต (เมื่อ /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
ข้อมูล 16 บิต มีการแบ่งออกเป็น 2 ส่วน คือ ส่วนแรกมี 4 บิต เรียกว่า Config Bits และส่วนที่สองมี 12 บิต เรียกว่า Data Bits ซึ่งเป็นตัวกำหนดระดับแรงดันเอาต์พุตที่ขา VOUTA

กลุ่มคำสั่งที่เกี่ยวข้องกับการใช้งานบัส SPI คือ machine.spi
และเริ่มต้นโดยการใช้คำสั่ง spi.init()
ซึ่งมีอาร์กิวเมนต์ดังนี้
ความเร็วในการส่งข้อมูล (baudrate)
ขนาดข้อมูลหนึ่งเฟรม (bits = จำนวนบิตในการเลื่อนข้อมูล) เช่น 8 บิต
โหมดการทำงานของ SPI ที่ต้องการเลือกใช้งาน เลือกโหมด (0,0) หรือ (1,1)
ขา GPIO ที่ใช้งานสำหรับสัญญาณ SCK (Serial Clock), MOSI (Master-Out / Slave-In) และ MISO (Master-In / Slave-Out) ของบัส SPI
ถ้าต้องการส่งข้อมูลออกไป ก็ใช้คำสั่ง spi.write()
ซึ่งจะต้องระบุอาร์กิวเมนต์ที่เป็นบัฟเฟอร์ข้อมูล เช่น มีชนิดข้อมูลเป็น bytearray
และมีขนาดหรือความยาวตามจำนวนไบต์ที่ต้องการส่ง
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 )



อ้างอิงจากเอกสาร 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 ใช้วิธีสื่อสารข้อมูลกับไมโครบิตได้ด้วยบัส I2C และในตัวอย่างนี้ได้กำหนดให้โมดูลนี้ มีแอดเดรสเท่ากับ 0x20
และในการเชื่อมต่อสายกับไมโครบิต ก็ได้ใช้ขา pin19
และ pin20
สำหรับสัญญาณ SCL และ SDA ของบัส I2C ตามลำดับ
ไมโครบิตทำหน้าที่เป็น I2C Master และโมดูล PCF8574 ทำหน้าที่เป็น I2C Slave คอยรับข้อมูลหนึ่งไบต์ เพื่อนำมากำหนดสถาะลอจิกสำหรับเอาต์พุต 8 บิต ที่นำไปต่อกับโมดูล 8-Bit LED Bar
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 ที่สว่าง จะถูกเลื่อนไปเรื่อย ๆ แล้ววนซ้ำใหม่

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

โค้ดตัวอย่างที่ 20: การกำหนดสถานะลอจิกของโมดูล LED ที่มี 8 ดวง โดยใช้ค่าอินพุตแอนะล็อก
ตัวอย่างนี้สาธิตการอ่านค่าจากโมดูลที่มีตัวต้านทานปรับค่าและเลื่อนตำแหน่งได้แบบเชิงเส้น (Linear Potentiometer) โดยนำมาใช้เป็นวงจรแบ่งแรงดัน (Voltage Divider) ใช้แรงดันไฟเลี้ยง 3.3V และเลือกใช้ขา pin0
สำหรับอ่านค่าแรงดันอินพุต (อยู่ในช่วง 0V ถึง 3.3V) ที่ได้จากวงจรแบ่งแรงดันดังกล่าว
ค่าที่อ่านได้จากขาอินพุต pin0
จะอยู่ในช่วง 0..1023 (เนื่องจาก ADC มีความละเอียด 10 บิต) แต่จะถูกนำมาแปลงให้เป็นเลขจำนวนเต็ม ให้มีค่าอยู่ในช่วง 0..9 และเก็บไว้ในตัวแปร value
จากนั้นจะนำไปใช้กำหนดสถานะการทำงานของโมดูล LED Bar ที่มีจำนวน 8 ดวง
ค่าของตัวแปร value
จะถูกใช้ในการกำหนดจำนวนหรือระดับของ LED ที่อยู่ในสถานะ ON เช่น ถ้า value
มีค่าเท่ากับ 0 ซึ่งเป็นค่าต่ำสุด ก็จะไม่มีดวงใด LED สว่าง หรือถ้าใช้ค่าสูงสุดคือ 9 จะทำให้ LED ทุกดวงสว่าง เป็นต้น
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)


โค้ดตัวอย่างที่ 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 จะมีเฟสต่างกัน (ขอบขาขึ้นหรือขาลง เกิดขึ้นไม่พร้อมกันทั้งสองสัญญาณ)



โค้ดตัวอย่างนี้ สาธิตการตรวจสอบดูว่ามีการหมุนเกิดขึ้นหรือไม่ โดยดูจากสัญญาณพัลส์ที่ขา A เกิดพัลส์แบบ High และมีความกว้าง (Pulse Width) ตามเงื่อนไขที่กำหนดไว้หรือไม่ จำนวนพัลส์ที่เกิดขึ้นขึ้นอยู่กับจำนวนสเต็ปที่หมุนไป
เมื่อเกิดพัลส์ที่สัญญาณ A ก็ตรวจสอบดูด้วยว่า สถานะลอจิกของสัญญาณ B เป็นอย่างไร เพื่อจำแนกว่า เป็นการหมุนในทิศทางใด เช่น ถ้าให้ทิศทางการหมุนเป็นตัวกำหนดว่า จะเพิ่มหรือลดค่าของตัวแปร level
ที่เป็นเลขจำนวนเต็ม และให้ตัวแปร level
ถูกจำกัดให้มีค่าอยู่ในช่วง 0 ถึง 9 เท่านั้น
ในตัวอย่างนี้ ได้ใช้โมดูล WS2812 Neopixel เพื่อใช้ RGB LED จำนวน 8 ดวง แสดงค่าของตัวแปร level
ในขณะนั้น ค่าของตัวแปร level
เป็นตัวกำหนดว่า จะมี LED ทั้งหมดกี่ดวงที่อยู่ในสถานะ ON (สว่าง)
ขา pin16
ของไมโครบิต ได้ถูกเลือกใช้เป็นขาเอาต์พุตสำหรับขา DIN ของโมดูล Neopixel และใช้แรงดันไฟเลี้ยง +3.3V จากโมดูลเสริมที่นำมาต่อกับ Edge Connector ของบอร์ดไมโครบิต
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)


โค้ดตัวอย่างที่ 22: การรับส่งข้อมูลแบบ UART Loopback
บอร์ดไมโครบิตสามารถสื่อสารแบบ Serial กับอุปกรณ์อื่นได้ เนื่องจากมีวงจร UART อยู่ภายใน nRF51822 โดยจะต้องเลือกใช้ขา Pin จำนวน 2 ขา สำหรับใข้งานเป็นขา TXD (ส่งข้อมูลออก) และ RXD (รับข้อมูลเข้ามา)
โค้ดตัวอย่างนี้ สาธิตการใช้อุปกรณ์หรือโมดูล 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
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 ได้อีกครั้ง ก็ให้ทำคำสั่งดังนี้
uart.init(baudrate=115200)

โค้ดตัวอย่างที่ 23: การอ่านค่าจาก Analog Input แล้วส่งออกทาง UART
โค้ดตัวอย่างถัดไป สาธิตการอ่านค่าจากขาอินพุตแบบแอนะล็อก (Analog Input) ของบอร์ดไมโครบิต โดยเลือกใช้ขา pin2
และได้รับสัญญาณอินพุตจากโมดูลเซ็นเซอร์วัดความชื้นในดินแบบคาปาซิทีฟ (Capacitive Soil Moisture Sensor) แล้วนำค่าที่อ่านได้ (อยู่ในช่วง 0 ถึง1023) ส่งออกเป็นข้อความผ่านทาง UART โดยเลือกใช้ขาภายนอก pin0
และ pin1
สำหรับ TXD และ RXD ตามลำดับ
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...')
โมดูลเซ็นเซอร์วัดความชื้นในดินที่ได้เลือกมาลองใช้งาน จะตอบสนองต่อระดับความชื้นในดินดังนี้ ถ้ามีความชื้นมาก หรือมีปริมาณน้ำมาก หรือนำไปจุ่มน้ำในระดับหนึ่ง จะได้แรงดันไฟฟ้าที่ลดลง หรืออ่านค่าตัวเลขได้น้อยลง
ข้อความที่ถูกส่งออกไปนั้น จะมีหนึ่งค่าตัวเลขต่อหนึ่งบรรทัด และถ้าใช้โปรแกร Arduino IDE - Serial Plotter ก็จะเห็นกราฟเชิงเส้นจากข้อมูลที่ได้รับมาตามลำดับ



จากรูปกราฟตัวอย่างที่ได้จากการทดลอง สังเกตได้ว่า มีช่วงเวลาหนึ่งที่ค่าตัวเลขลดต่ำลงอย่างรวดเร็วแล้วเพิ่มขึ้น ซึ่งเกิดขึ้นเนื่องจากได้มีการเทน้ำในช่วงเวลาสั้น ๆ ลงในกระถางบริเวณที่มีแท่งวัดของโมดูลเซ็นเซอร์เสียบอยู่ในดิน
โค้ดตัวอย่างที่ 24: การตรวจสอบปริมาณการใช้หน่วยความจำแบบ Heap
เมื่อมีการทำคำสั่งต่าง ๆ สร้างตัวแปรและอ็อปเจกต์สำหรับไมโครไพธอน จะมีการใช้หน่วยความจำ SRAM ของระบบที่ถูกจัดสรรไว้และเรียกว่า Heap ('ฮีป') ถ้าอ็อปเจกต์ใด ไม่ถูกอ้างอิงโดยตัวแปรหรือใช้งานอีกต่อไป เช่น โดยการใช้คำสั่ง del
จะเป็นหน้าที่ของส่วนที่เรียกว่า Garbage Collector เพื่อจัดการและคืนหน่วยความจำ
โค้ดตัวอย่างนี้ สาธิตการใช้คำสั่งเพื่อตรวจสอบสถานะการใช้หน่วยความจำสำหรับ Heap และเรียกใช้ Garbage Collector (gc
) ของไมโครไพธอน
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 ตามลำดับ
เฟิร์มแวร์สำหรับไมโครไพธอนที่ใช้คือ
MicroPython v1.9.2-34-gd64154c73 on 2017-09-01; micro:bit v1.0.1 with nRF51822
ถ้าใช้ Mu-Editor v1.0.2 จะได้ข้อความเอาต์พุตดังนี้
Used: 1104, Free: 8944 bytes
Used: 1056, Free: 8992 bytes
แต่ถ้าใช้ Thonny IDE v3.2.6 จะเป็นดังนี้
Used: 6192, Free: 3856 bytes
Used: 1424, Free: 8624 bytes
เราจะสังเกตเห็นความแตกต่างของปริมาณหน่วยความจำที่ใช้
โค้ดตัวอย่างที่ 25: การอ่านและแสดงค่าจากโมดูล SDS011 Dust Sensor
SDS011 เป็นโมดูลเซ็นเซอร์ราคาถูก ประเภท 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
โดยปรกติ โมดูลเซ็นเซอร์จะส่งข้อมูลออกมา 10 ไบต์ ทุก ๆ 1 วินาที โดยอัตโนมัติ มีลำดับข้อมูลไบต์ดังนี้
ไบต์ที่ 0:
0xaa
ไบต์ที่ 1:
0xc0
ไบต์ที่ 2 และ 3: ค่า PM2.5 (low byte and high byte) นำไปหารด้วย 10
ไบต์ที่ 4 และ 5: ค่า PM10 (low byte and high byte) นำไปหารด้วย 10
ไบต์ที่ 6 และ 7: Reserved
ไบต์ที่ 7: Checksum
ไบต์ที่ 8:
0xab
สูตรการคำนวณค่า PM2.5 หรือ PM10 จากข้อมูล 2 ไบต์ในแต่ละกรณี
การเชื่อมต่อกับโมดูล 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)
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()
ถ้าต้องการจบการทำงานของโปรแกรม ให้กดปุ่ม Button A ค้างไว้



โค้ดตัวอย่างที่ 26: การอ่านค่าจากเซ็นเซอร์วัดอุณหภูมิแบบอินฟราเรด MLX90614
MLX90614 ของบริษัท 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)
การอ่านข้อมูลสำหรับอุณหภูมิ จะต้องระบุแอดเดรสของรีจิสเตอร์ที่เก็บข้อมูลแต่ละตัวขนาด 16 บิตภายใน RAM ขนาด 32x16 เช่น
0x06
= TA (Ambient Temperature)0x07
= TOBJ1 (Object Temperature, Zone 1)
ข้อมูลที่อ่านได้ขนาด 2 ไบต์ จะต้องนำมาแปลงให้เป็น 16-bit signed แล้วคูณด้วย 0.02 และแปลงจากหน่วย Kelvin ให้เป็น Celsius ตามลำดับ
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 ที่จำเป็นต้องใช้ร่วมกับโค้ดสาธิตการทำงาน มีดังนี้
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.

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

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