專題--燈籠牆實作

在前面的章節中我們介紹了許多的micro:bit的練習,如何運用在練習中建立的實例運用到實際的場域中呢?這次博雅教育中心的成果展裡面所展示的燈籠牆是其中的一個例子。先來看看照片:

從照片中同學們應該可以看得出來,在這面牆上共有7串燈籠,每一燈籠串共有10個燈籠,在這個例子中每一串我們只使用了中間的8個燈籠,也就是每一串都有8個燈籠是可以由我們來分別控制它們的明滅動作的。

在組裝之前是這個樣子的:

以及這個樣子:

由於每一串均需使用一個控制器,因此7串燈籠共使用了7個控制器,而由於每一個燈籠中使用的是12V的高亮度LED,這顆LED所供應的電流並不是micro:bit所能提供的,因此要控制這些燈的開關,就是繼電器控制模組派上用場的時候了。

每一個單獨的控制器如下所示:

在上面的控制器裡面使用了一個八路繼電器模組,每一個繼電器可以控制1個燈的明滅運用。這支影片示範的是讓八路繼電器模組可以使用2進位的方式從00000000計算到11111111,同學們有看出來嗎?

在燈籠牆的例子中使用的就是這樣控制模組,但是,如何才能控制這7個模組的運作呢?答案是透過micro:bit的無線電功能。

在每一個模組中,它都會一直不斷地偵測無線電訊號,如果一收到從無線電來的訊息之後,立刻剖析收到的字串,依照字串的內容來呈現出對應的燈號,其程式碼如下所示:

from microbit import *
import radio
UNIT_NO = 3
relay = [pin0, pin1, pin2, pin3, pin4, pin6, pin7, pin8]
display.show(UNIT_NO)
sleep(2000)
display.off()
for r in relay:
    if UNIT_NO == 3:
        r.write_digital(0)
    else:
        r.write_digital(1)
radio.on()

while True:
    incoming = radio.receive()
    while incoming is None:
        incoming = radio.receive()
    if len(incoming) > 4:
        command, unit_no, digit = incoming.split(",")
        if int(unit_no) == UNIT_NO:
            if command == "go":
                for r in relay:
                    r.write_digital(0)
                bits = [0, 0, 0, 0, 0, 0, 0, 0]
                count = 0
                digit = int(digit)
                while digit > 1:
                    bits[count] = digit % 2
                    count += 1
                    digit = digit // 2
                bits[count] = digit
            for i, r in enumerate(reversed(bits)):
                if UNIT_NO == 3:
                    relay[i].write_digital(1-r)
                else:
                    relay[i].write_digital(r)

上述的程式中,UNIT_NO代表的就是第幾串燈籠串,當收到屬於自己這串命令的時候,會把收到的命令剖析出來,每一個數字(放在digit變數中)先把它轉換成整數,之後把它從10進位轉換成2進位,再依序依照該位元是0或是1把訊號送給繼電器模組,以決定開關是打開還是關閉,也就是可以直接以數字的方式控制燈的明滅。以下是原始設計文字元的紙張:

在7串燈籠的控制器都燒錄了之後,接下來就可以透過控制器去傳送要呈現的資訊給每一個燈籠串,其中一個範例如下:

from microbit import *
import radio
bitmaps = {'?': [0, 64, 128, 138, 144, 96, 0],
'!': [0, 0, 0, 251, 0, 0, 0],
'l': [56, 124, 252, 63, 252, 124, 56],
'A': [7, 56, 72, 136, 72, 56, 7],
'B': [255, 255, 145, 145, 145, 105, 14],
'C': [60,66,129,129,129,129,70],
'D': [255,129,129,129,129,66,60],
'E': [255,137,137,137,137,137,137],
'F': [255,136,136,136,136,128,128],
'G': [60,66,129,137,137,138,79],
'H': [255,255,24,24,24,255,255],
'I': [129,129,129,255,129,129,129],
'J': [132,130,129,255,128,128,128],
'K': [0,255,8,24,36,66,129],
'L': [255,1,1,1,1,7,0],
'M': [255,192,112,53,112,192,255],
'N': [255,192,32,16,8,7,255],
'O': [60,66,129,129,129,66,60],
'P': [0,0,255,136,136,112,0],
'Q': [62,66,129,137,70,62,1],
'R': [127,200,136,136,140,114,1],
'S':[0,2,98,146,146,140,128],
'T':[128,128,128,255,128,128,128],
'U':[252,2,1,1,1,2,252],
'V':[224,56,14,3,14,56,224],
'W':[124,2,3,124,3,2,124],
'X':[193,66,36,24,24,36,199],
'Y':[192,32,16,15,16,32,192],
'Z':[195,131,133,137,145,161,199],
'9':[0,114,137,137,137,126,0],
'8':[0,110,145,145,145,110,0],
'7':[0,224,128,143,144,224,0],
'6':[0,126,137,145,145,78,0],
'5':[0,0,243,145,145,142,0],
'4':[0,24,40,72,255,8,0],
'3':[0,66,129,145,145,110,0],
'2':[0,97,131,133,137,113,0],
'1':[0,33,65,255,1,0,0],
'0':[0,126,129,129,129,126,0],
' ':[0, 0, 0, 0, 0, 0, 0],
'a':[255, 255, 255, 255, 255, 255, 255]}

radio.on()
RADIO_DELAY = 100

def ani_banner(banner, d):
    for char in banner:
        lines = bitmaps[char]
        for i, line in enumerate(lines, 1):
            radio.send("go,{},{}".format(i, line))
            sleep(RADIO_DELAY)
        sleep(d)
def ani_banner_shift(banner, d):
    all_chars = [0, 0, 0, 0, 0, 0]
    for char in banner:
        all_chars += bitmaps[char]
    all_chars += [0, 0, 0, 0, 0, 0, 0]
    for i in range(len(all_chars)-7):
        for j in range(i, i+7):
            radio.send("go,{},{}".format(j-i+1, all_chars[j]))
            sleep(RADIO_DELAY)
        sleep(d)

banners = ['lNKUST']
while True:
    if button_a.was_pressed():
        while True:
            display.show(Image.HAPPY)
#           ani_banner_shift("N K U S T ", 50)
#            sleep(2000)
            for banner in banners:
                ani_banner(banner, 1000)
            sleep(2000)
    else:
        display.show(Image.HEART)

Last updated