MicroPython無線電通訊

覺得一個micro:bit的顯示LED不夠用?想要把結果傳送到另外一個micro:bit上面的LED顯示器上?想要兩個micro:bit連線對戰?想要在不同的micro:bit之間傳遞訊息?這些都難不倒micro:bit喔,它內建了無線電通訊模組,想要在不同的micro:bit之間交換訊息,只要簡單的幾個指令就可以完成了。

MicroPython有幾個函數負責在不同的micro:bit之間的無線電通訊,詳細的內容可以在以下這個網址中找到相關的說明:

其中radio.config()用來設定一些初始化的參數,最重要的部份是設定在板子之間要互相溝通用的頻道,如此才不會不小心接受到不相關的板子所傳來的無線電訊號,它使用channel參數來進行設定,預設是7,可以用的頻道是0~83。

第2個重要的函數是radio.on()和radion.off(),它們分別負責開啟和關閉板子的無線電訊號功能,在使用之前一定要利用radio.on(),才能進行後續的傳送和接收訊息的工作。

傳送訊息使用radio.send(),接收訊息使用radio.receive(),在接收端要特別留意,因為無線電訊號傳送的時候只會傳送一次,時間過了並不會保留該訊號,為了確保一定能夠接收到訊息,radio.receive()通常必需編寫在無窮迴圈中。

以下的練習要使用兩塊以上的micro:bit板子,而且兩塊板子上都要寫入具有無線電接收作業的程式才行。

先來看看以下的程式,透過無線電訊號傳送一個數字到另外一片板子上:

from microbit import *
import radio
radio.on()
while True:
if button_a.was_pressed():
radio.send('Hi')
msg = radio.receive()
if msg is not None:
display.scroll(msg)
sleep(500)

這段程式碼在第2行的地方要匯入radio模組才可順利執行無線電通訊。接著,在無窮迴圈中會檢查這片板子上的A按鈕是否曾被按下,如果有的話,就要送出Hi這個字串。接著,利用radio.receive()函數接收目前在同一個頻道中是否有人送出任何的訊息,在第10行的地方檢查訊息內容是否不為空(not None),如果是的話,就把該字串顯示在這片板子上。

執行上面這個程式,當按鈕A按下去之後,除了自己以外,其它位於同一頻道的板子都會出現Hi這個字樣喔。以下這個程式貼不顯示文字,而是以動畫的形式來取代,大家可以同時使用多片板子,看看會有什麼樣有趣的畫面出現!

from microbit import *
import random
import radio
images = [Image().invert()*(i/9) for i in range(10)]
radio.on()
display.show(Image.HAPPY)
while True:
if button_a.was_pressed():
radio.send('go')
incoming = radio.receive()
if incoming == 'go':
sleep(random.randint(100, 500))
display.show(images, delay=50, loop=False)
display.show(reversed(images), delay=50, loop=False)
if random.randint(0, 3) == 0:
sleep(200)
radio.send('go')

為了展示更多有趣的應用,以下的程式利用5片micro:bit,在每一片板子上寫入2種功能,且每一片板子各自有自己的編號,在收到無線電來的訊息的時候,每一個板子自己都要先確定是否自己是被點名到的對象,如果是的話,就執行動作,並在做完動作之後再送出無線電訊息,讓下一片板子開始工作。在這個例子中micro:bit的排列方式如下:

在一開始執行的時候,每一片板子都會顯示自己的編號,方便我們佈置它們的位置。程式設計如下:

from microbit import *
import random
import radio
images = [Image().invert()*(i/9) for i in range(10)]
airplane = list()
airplane.append(Image(
'00000:'
'00000:'
'00000:'
'00000:'
'00000'))
airplane.append(Image(
'00000:'
'00000:'
'00009:'
'00000:'
'00000'))
airplane.append(Image(
'00000:'
'00000:'
'00099:'
'00000:'
'00000'))
airplane.append(Image(
'00000:'
'00009:'
'00999:'
'00009:'
'00000'))
airplane.append(Image(
'00009:'
'00090:'
'09999:'
'00090:'
'00009'))
airplane.append(Image(
'00090:'
'00900:'
'99999:'
'00900:'
'00090'))
airplane.append(Image(
'00900:'
'09009:'
'99999:'
'09009:'
'00900'))
airplane.append(Image(
'09000:'
'90090:'
'99990:'
'90090:'
'09000'))
airplane.append(Image(
'90000:'
'00900:'
'99900:'
'00900:'
'90000'))
airplane.append(Image(
'00000:'
'09000:'
'99000:'
'09000:'
'00000'))
airplane.append(Image(
'00000:'
'90000:'
'90000:'
'90000:'
'00000'))
airplane.append(Image(
'00000:'
'00000:'
'00000:'
'00000:'
'00000'))
my_channel = 1
next = 2
radio.on()
display.show(my_channel)
while True:
if button_a.was_pressed():
radio.send('go 1 1')
elif button_b.was_pressed():
radio.send('go 1 2')
incoming = radio.receive()
if incoming is not None:
commands = incoming.split()
else:
commands = list()
if len(commands) > 2:
if commands[0] == 'go' and my_channel == int(commands[1]):
if int(commands[2]) == 1:
sleep(random.randint(100, 500))
display.show(images, delay=50, loop=False)
display.show(reversed(images), delay=50, loop=False)
radio.send("go {} 1".format(next))
elif int(commands[2]) == 2:
display.show(airplane, loop=False, delay=200)
radio.send("go {} 2".format(next))

在程式開始的地方除了記錄一些要製作動畫的串列變數之外,第78行設定的是自己的編號,第79行設定的next變數則是下一個要動作的號碼,利用next的指定,即可讓一些micro:bit的運用變成無窮的循環動畫。

在第84行送出第一個命令,它決定了開始動作的板子(第1個參數,在這個例子是1)以及要顯示的動畫(第2個參數),如果按下的是A按鈕,則執行第1個動畫,如果按下的是B按鈕,則執行第2個動畫。

在 89行拆解收到的訊息,在第93行的地方檢查是否是自己需要進行動畫,在第94行判斷是否為第1種動畫,第99行則是檢查是否為第2種動畫效果。以下是執行的結果影片: