[Toc]
# 第11章 micro:bit
今天,墨博士向小墨介紹了一種開發板:micro:bit,這是一種可以使用Python直接操作的硬件。在本章中,墨博士就向小墨展示了Python操作micro:bit的方法,并做出了幾個小應用,一起來看看吧。
## 11.1 micro:bit是什么
墨博士:小墨,你來看這是什么東西?

圖11.1 micro:bit
小墨:這個……,沒見過。
墨博士:這個東西叫micro:bit,是一款由英國廣播電視公司(BBC)推出的專為青少年編程教育設計的微型電腦開發板。我看很不錯,就入手了一個,正好咱們可以一塊玩玩。
小墨:這個micro:bit能做什么呀?
墨博士:你可別小看它。它雖然很小,但集成了種類豐富的電子模塊:5x5的LED顯示屏,兩顆可編程按鍵,加速度計,電子羅盤,溫度光線傳感器等。

圖11.2 micro:bit電子模塊介紹
小墨:這么強大呢!
墨博士:除了自身集成的電子模塊外,它還能外接很多電子元件,讓你輕松完成如電子游戲、遙控小車、音樂播放、可穿戴設備等場景下的開發。
小墨:厲害厲害!那這個東西到底怎么玩呢?
## 11.2 micro:bit在線開發平臺
墨博士:micro:bit的常用開發方式有兩種。一種是使用MakeCode,這是微軟為micro:bit提供的在線編程平臺,如下圖11.3所示。

圖11.3 MakeCode在線編程平臺
它最大的特點是提供了圖形化的編程方式,讓新手也能很快上手制作出一些好玩的程序來。
另一種方式是使用PythonEditor,這也是一個在線編程平臺,如圖11.4所示。

圖11.4 PythonEditor在線編程平臺
從PythonEditor的名字就可以看出,這個編程平臺其實就相當于一個Python的編輯器,也就是說它使用Python作為開發語言。這是它與MakeCode最大的區別。
**注意**:嚴格來說PythonEditor使用MicroPython而不是Python作為開發語言,而MicroPython可以看做是Python語言應用在硬件領域的特殊版。
> 墨博士提醒:你可能需要使用最新版的谷歌瀏覽器chrome來訪問PythonEditor。
小墨:我們今天用哪種方式呢?
墨博士:你已經學習了很多Python的知識,可以直接選擇第二種方式了,代碼的方式也比圖形化的方式更靈活、更強大。
小墨:好,正好練練手。不過我們編寫的程序如何運行到micro:bit上去呢?
墨博士:首先通過usb線將micro:bit連接到你的電腦上,此時電腦中多了個MICROBIT的盤符。如圖11.5所示。

圖11.5 micro:bit連接上電腦后,就跟插了一個U盤一樣
然后你看PythonEditor的左上角有個download按鈕,它的作用是將你編寫好的代碼轉成micro:bit能識別的格式并下載下來。下載好后把下載的文件直接拷貝到MICROBIT磁盤中,這樣你寫的程序就自動運行起來了。
小墨:哦,那如果我寫了好多個程序呢?
墨博士:當新的程序被拷貝到MICROBIT磁盤后,micro:bit就開始運行新的程序了,這個過程就相當于新程序把老程序給覆蓋了,也即同一時刻只能運行一個程序。
好了,我們實際來操作一下,看看這個平臺的使用。
## 11.3 micro:bit的使用
老規矩,我們還是從micro:bit的Hello World說起。
### 11.3.1 Hello World
在PythonEditor中編寫如下代碼:
```
from microbit import *
display.scroll("Hello, World!")
```
第1句表示將microbit中的所有內容全部導入。這個microbit就是MicroPython中自帶的操作micro:bit的庫。
第2句的表示在顯示器上滾動顯示字符串“Hello,World!”。
編寫好代碼后可以在右側輸入框中輸入一個名稱,比如我這里寫成hello,如圖11.6所示。

圖11.6 在PythonEditor中編寫代碼并保存為hello
這個名稱將作為你下載或保存的文件的名稱。
點擊download按鈕,就會下載一個hello.hex文件,將此文件拷貝到MICROBIT磁盤中,就可以在micro:bit上看到程序的運行結果了。
除了scroll外,另外一個常用來顯示字符的方法是show():
```
display.show("Hello, World!")
```
它和scroll的區別是scroll是滾動顯示“Hello, World!”的每個字符,而show是逐個顯示“Hello, World!”中的每個字符。
> 動動手:動手實際操作比較下scroll和show的區別。
### 11.3.2 內置圖案
除了字符外,還可以顯示一些小圖案在屏幕上。MicroPython內置了很多小圖案,比如:
```
from microbit import *
display.show(Image.HAPPY)
```
就用于在屏幕上顯示一個笑臉。運行結果如圖11.7所示。

圖11.7 笑臉顯示在micro:bit的顯示器上
其他常用內置小圖案有:
```
# 內置圖案
Image.HEART
Image.HEART_SMALL
Image.HAPPY
Image.SMILE
Image.SAD
Image.CONFUSED
Image.ANGRY
Image.ASLEEP
Image.SURPRISED
Image.SILLY
Image.FABULOUS
Image.MEH
Image.YES
Image.NO
……
```
### 11.3.3 自定義圖案
小墨:博士,除了能顯示這些內置的圖案外,我們自己能控制屏幕上的這25個LED燈嗎?如果可以的話我們就能自己畫一些圖案出來了。
墨博士:你想畫什么呢?
小墨:我想畫一輛坦克,或者一挺沖鋒槍。
墨博士:自定義圖案,使用類似如下代碼:
```
from microbit import *
my_img = Image("00900:00900:09990:09990:09990")
display.show(my_img)
```
my_img就是我們自定義的圖案了。這里的“00900:00900:09990:09990:09990”就對應25個LED燈,每五個一組,一共分五組。每個LED對應的數字可以取0~9中的任意一個,表示LED的亮度。數字越大表示越亮,數字為0表示燈是暗的。
上述的“00900:00900:09990:09990:09990”其實就是一個坦克的圖案,如圖11.8所示。

圖11.8 自定義坦克圖案
> 動動手:請幫助小墨顯示一挺機槍的圖案出來。
### 11.3.4 動畫
墨博士:小墨,你聽說過逐幀動畫嗎?
小墨:聽墨哥哥說過。據他所說,有些動畫片就是一幀一幀畫出來的。
墨博士:如果我們在micro:bit的顯示屏上交替顯示兩種圖片,也就變成了逐幀動畫了:
```
from microbit import *
my_img_0 = Image("00900:09990:00900:09090:90009")
my_img_1 = Image("00900:09990:00900:00900:00900")
img_list = [my_img_0, my_img_1]
display.show(img_list, delay=500, loop=True)
```
你來運行一下,看看效果。
小墨:一個小人兒走路的動畫!這個跟紅綠燈路口人行道交通燈中的小人兒差不多。如圖11.9和11.10所示

圖11.9 站立的小人兒

圖11.10 行走的小人
墨博士:來看看實現的過程:show()的第一個參數是兩個圖案組成的list;第二個參數delay=500表示兩張圖的替換間隔,單位是毫秒,一秒等于一千毫秒;第三個參數loop=True表示兩張圖一直交替顯示,就相當于死循環。事實上,我們確實也可以使用死循環來制作這種動畫效果。
比如我們來模擬一個螢火蟲的發光過程:把這25個LED燈整體看做是一個螢火蟲,然后讓它逐漸從暗到亮,到最亮后再逐漸從亮到暗,如此反復。
分析一下:螢火蟲從暗到亮,其實是LED燈對應的數字由“00000:00000:00000:00000:00000”變成“11111:11111:11111:11111:11111”、“22222:22222:22222:22222:22222”,然后再逐漸變成“99999:99999:99999:99999:99999”的過程。螢火蟲從亮變暗的過程反之。我們可以先定義一個函數,用于產生這些數字:
```
def get_image(n):
result = ''
for i in range(5):
for j in range(5):
result += str(n)
result += ':'
return result[0:len(result)-1]
```
這里的參數n就是LED燈的亮度,可以打印測試一下:
```
print(get_image(1))
```
輸出結果為:
```
11111:11111:11111:11111:11111
```
來分析一下這段代碼,它使用了雙重for循環來生成數據,程序的運行流程為:
```
# 外層循環開始第一輪循環
# 內層循環開始第一輪循環,產生了5個相同的數字并且拼接在一起
# 此時內層循環第一輪循環結束
# 將產生的5個數字后面拼接上一個冒號
# 此時結束外層循環的第一輪
# 開始外層循環的第二輪循環
# …… 后面過程省略
# 外層循環的第5輪結束。
```
雙重for循環結束,我們會得到類似“11111:11111:11111:11111:11111:”的字符串,也就是最后面多了個冒號。
把這個冒號去掉方法很多,但最簡單的是使用切片操作符。切片操作符用于取list或tuple中的部分元素,比如:
```
list_name = ['墨小小', '墨小妹', '墨大元', '墨當歸', '墨魚兒']
print(list_name[0:2])
```
輸出結果為:
```
['墨小小', '墨小妹']
```
這里的list_name[0:2]就是對list_name的切片操作。[0:2]表示從下標為0的元素開始切,切到下標為2的元素為止,但是**不包括**下標為2的元素。也即切出來了第1個和第2個元素,所以輸出結果是墨小小和墨小妹。
小墨:這個就像墨媽媽切黃瓜,我只吃頭不吃把兒,她就把黃瓜頭切給我。
墨博士:跟切黃瓜差不多,指定開始和結束位置就行了。如果是想從頭開始切,第一個數可以不寫。比如list_name[:2]一樣可以得到墨小小和墨妹妹。而如果我想從某個下標開始一直切到最后,則后面一個數可以不寫。比如list_name[3:]就表示從下標為3的元素開始切到最后,得到的結果是墨當歸和墨魚兒。
小墨:切片的規則還真是靈活呀,指定頭和尾的下標,如果從頭開始就頭部坐標不指定,如果切到最后就最后下標不指定。
墨博士:還有更強大的:Python中的list支持負下標。負下標是從-1開始的,倒數第一個元素的負下標是-1,倒數第二個元素的負下標是-2,以此類推。
小墨:這個前面曾說過,list_name[-1]會得到墨魚兒、list_name[-2]就得到墨當歸。
墨博士:是的。同樣的,切片也可以使用負坐標。比如list_name[-3:-1]就表示從下標為-3的元素取到下標為-1的元素,不包括下標為-1的元素本身,得到墨大元和墨當歸。list_name[-3:]則表示從下標為-3的元素開始取到最后,會得到墨大元、墨當歸和墨魚兒。
小墨:我知道了。list_name[:-3]就表示從開頭取到下標為-3的墨大元,會得到墨小小和墨小妹,沒錯吧博士?
墨博士:沒錯。回到我們模擬螢火蟲的程序,現在得到的字符串是“11111:11111:11111:11111:11111:”,如何把最后一個冒號去掉呢?
小墨:這個字符串長度是30,所以使用result[0:29]從0切到29就可以了。哦,原來這就是你寫的result[0:len(result)-1]。我再想想……,還可以使用負下標切,從開始切到最后一位,用result[:-1]就可以了。
等等,博士,關于產生25個LED對應的數字的方式,我突然有個新思路:
```
def get_image(n):
result = ''
for i in range(1, 26):
result += str(n)
if i % 5 == 0:
result += ':'
return result[:-1]
```
你看,就不需要寫兩個for循環了。或者我可以直接拼接29次,省去切片的麻煩:
```
def get_image(n):
result = ''
for i in range(1, 30):
if i % 6 == 0:
result += ':'
else:
result += str(n)
return result
```
怎么樣博士?我厲害吧?
墨博士:厲害!青出于藍而勝于藍,維護世界和平,哦不,人工智能的未來,就靠你們了。
回到我們的螢火蟲程序,現在get_image()函數已經能生成LED對應的數字了。想讓螢火蟲能從暗到亮,可以使用循環生成0~9,然后調用get_image()方法得到對應的LED的數字,顯示在顯示屏上。
```
for i in range(10):
img = get_image(i)
display.show(Image(img))
sleep(200)
```
這里的sleep(200)和小人兒走路程序中的delay=500作用一樣,表示暫停200毫秒,這樣配合著循環我們就能看到螢火蟲逐漸變亮的過程了。
然后再來寫一個循環,產生9~0對應的數字并顯示在顯示屏上,就能讓螢火蟲由亮變暗了。
最后,在兩個for循環外套一層死循環,就能讓螢火蟲一直不停的由暗變亮,由亮變暗了。完整代碼如下:
```
from microbit import *
import random
def get_image(n):
result = ''
for i in range(1, 30):
if i % 6 == 0:
result += ':'
else:
result += str(n)
return result
while True:
for i in range(10):
img = get_image(i)
display.show(Image(img))
sleep(200)
for i in range(10):
img = get_image(9 - i)
display.show(Image(img))
sleep(200)
```
> 動動手:策劃一個下雨的動畫場景,并使用micro:bit模擬。
### 11.3.5 按鈕
除了25個LED組成的顯示屏外,micro:bit還提供了兩個按鈕可供我們編程控制。左邊按鈕稱為A,右邊按鈕稱為B。見圖11.2所示。
現在我們來寫個程序,完成下面功能:
當點擊A鍵時,屏幕上一直快速的在動態變化顯示0~9;
當點擊B鍵時,動態變化停止,屏幕上固定顯示一個0~9間的數字。
小墨:這個可以和墨妹妹一起玩,我們倆每人一次,最后看誰停住的數字大誰就贏了。
墨博士:不錯的主意!另外,我們還可以讓游戲能一直玩下去:
```
from microbit import *
import random
def get_image():
return str(random.randint(0,9))
while True:
if button_a.is_pressed():
while True:
my_img = get_image()
display.show(my_img)
if button_b.is_pressed():
break
```
這里的“get_image”用于隨機獲取字符串類型的0~9。“if button_a.is_pressed():”就表示如果按下了A鍵。在“if button_a.is_pressed():”后又跟了一個死循環,用于屏幕一直動態刷新,在此過程中如果按了B鍵,則內部死循環結束,屏幕上顯示最終定格的數字。
## 11.4 本章小結
墨博士總結:關于micro:bit介紹到這里就結束了。你可以訪問micro:bit的官網繼續深入的了解和學習。另一方面,你看本章操作micro:bit的幾個案例,用的都是很簡單的Python基礎知識,從這方面來說,你現在的Python水平已經能做出各種各樣有趣的東西了。