Python可以這樣玩(14):廣告流水燈
先準備下面的實驗器件,如果還沒有這些東西,務必跑一趟光華商場,在新大樓以及八德路對面的地下室都買的到。
l Arduino UNO 開發板一個
l 麵包板一個
l Led 燈:6 個,最好是紅、黃、綠各兩個。
l 220Ω的電阻:6 個,我買的一組220Ω電阻是五個,少一個可以用
1KΩ 電阻代替,亮度會低一點,不會燒掉。
l 多彩麵包板實驗跳線:若干
單獨看下面的概要圖,其實接法很簡單,數位PIN 1-6 (下圖有小錯誤,請不要接 PIN 0,從 PIN 1開始接,我在連接 PIN
0 的時候上傳發生問題,應該是因為 PIN 0 同時有 RX的作用,會與上傳的READ動作衝突,因為上傳的動作對板子而言就是READ) 接出來六個電阻與LED,最後全部接地。
不過看到下面這張圖,可能就會亂了:
這就是我們要練習的地方,凡是如果沒有親自動手做,只是在平板上面滑手指,永遠不會進步。現在試著從PIN 1的正極開始,一條一條接到 PIN 6 (也要跳過PIN 0)。接線的時候,請將連接電腦的 USB 街頭拔開。
實驗原理
在生活中我們經常會看到一些由各種顏色的 LED 燈組成的廣告燈(例如台灣的檳榔攤),廣告燈上各個位置上的 LED
燈不斷的亮滅變化,就形成各種不同的效果。本節實驗就是利用
LED 燈程式設計模擬廣告燈的效果。
在程式中我們設置 LED 燈亮滅的次序和時間,這樣就可以組成不同的效果。我們提供幾個不同的樣式。
樣式一副程式:LED 首先從左邊的綠燈開始間隔
200ms 依次點亮六個
LED 燈,如上圖,接著從右邊的綠燈開始間隔 200ms 依次熄滅六個 LED 燈(左右先別太在意,如果按照前面的接法,由於是反過來,會是右邊開始)。
燈閃爍副程式:六個 LED 燈首先全部點亮,接著延時 200ms,最後六個 LED 燈全部熄滅,這個過程迴圈兩次就實現了閃爍的效果。
樣式二副程式:設置 k 和 j 的值讓中間的兩個黃燈亮先亮,接著讓挨著兩個黃燈兩邊的紅燈亮,最後讓兩邊的綠燈亮;執行一遍後改發 k 和 j 的值讓讓兩邊的綠燈先熄滅,接著兩邊的紅燈熄滅,最後中間的兩個黃燈熄滅。
樣式三副程式:設置 k 和 j 的值,讓兩邊的綠燈亮 400ms 後再熄滅,
接著讓兩邊的紅燈亮 400ms 後再熄滅,最後讓中間的兩個黃燈亮 400ms 後再熄滅;執行一遍後改發 k 和 j 的值讓兩個紅燈亮 400ms 後熄滅,接著讓兩邊的綠燈亮 400ms 後熄滅。
程式碼
程式碼如下:
int Led1 = 1;
int Led2 = 2;
int Led3 = 3;
int Led4 = 4;
int Led5 = 5;
int Led6 = 6;
void style_1(void)
{
unsigned char
j;
for(j=1;j<=6;j++)
{
digitalWrite(j,HIGH);
delay(200);
}
for(j=6;j>=1;j--)
{
digitalWrite(j,LOW);
delay(200);
}
}
void flash(void)
{
unsigned char
j,k;
for(k=0;k<=1;k++)
{
for(j=1;j<=6;j++)
digitalWrite(j,HIGH);
delay(200);
for(j=1;j<=6;j++)
digitalWrite(j,LOW);
delay(200);
}
}
void style_2(void)
{
unsigned char
j,k;
k=1;
for(j=3;j>=1;j--)
{
digitalWrite(j,HIGH);
digitalWrite(j+k,HIGH);
delay(400);
k +=2;
}
k=5;
for(j=1;j<=3;j++)
{
digitalWrite(j,LOW);
digitalWrite(j+k,LOW);
delay(400);
k -=2;
}
}
void style_3(void)
{
unsigned char
j,k;
k=5;
for(j=1;j<=3;j++)
{
digitalWrite(j,HIGH);
digitalWrite(j+k,HIGH);
delay(400);
digitalWrite(j,LOW);
digitalWrite(j+k,LOW);
k -=2;
}
k=3;
for(j=2;j>=1;j--)
{
digitalWrite(j,HIGH);
digitalWrite(j+k,HIGH);
delay(400);
digitalWrite(j,LOW);
digitalWrite(j+k,LOW);
k +=2;
}
}
void setup()
{
unsigned char
i;
for(i=1;i<=6;i++)
pinMode(i,OUTPUT);
}
void loop()
{
style_1();
flash();
style_2();
flash();
style_3();
flash();
}
用Python 控制
使用 Python 控制之前,我們要先把
Standard Firmata上傳到UNO上,前面說明過步驟,上傳完畢之後,燈就不亮了。
開啟IDLE,先行測試下面的程式片段:
import pyfirmata
import time
port = 'COM3'
board = pyfirmata.Arduino(port)
def style1():
for i in
range(1, 7):
board.digital[i].write(1)
time.sleep(0.2)
for i in
range(6, 0, -1):
board.digital[i].write(0)
time.sleep(0.2)
style1()
馬上就發生下面的錯誤,這回是 PIN 1 的問題:
RESTART:
C:/Users/huskywang/Documents/GitHub/python/ArduinoPrograms/Play6LED.py
Traceback
(most recent call last):
File
"C:/Users/huskywang/Documents/GitHub/python/ArduinoPrograms/Play6LED.py",
line 15, in <module>
style1()
File
"C:/Users/huskywang/Documents/GitHub/python/ArduinoPrograms/Play6LED.py",
line 9, in style1
board.digital[i].write(1)
File
"C:\Users\huskywang\AppData\Local\Programs\Python\Python36\lib\site-packages\pyfirmata\pyfirmata.py",
line 526, in write
raise IOError("{0} can not be used
through Firmata".format(self))
OSError:
Digital pin 1 can not be used through Firmata
>>>
為了忠實傳真,我保留了所有我在coding 時候發生的錯誤,因為這些錯誤也一定會發生在其他人身上。仔細看一下手上的 UNO板,會發現 PIN 0 是與 RX 共用,所以我們在上傳時如果佔用,就會發生問題。PIN 1 則是與 TX 共用,在 Firmata 通訊協定中,這些特殊用途的 PIN 我們都要避開。因此請大家把線接到 PIN 2 – PIN 7 上面。在將程式改寫如下:
import pyfirmata
import time
port = 'COM3'
board = pyfirmata.Arduino(port)
def style1():
for i in range(2, 8):
board.digital[i].write(1)
time.sleep(0.2)
for i in range(7, 1,
-1):
board.digital[i].write(0)
time.sleep(0.2)
while(True):
style1()
再加上while() 迴圈,這樣燈號就會一直循環了:
剩下的 style2,
style3 以及 flash,就是這部分的隨堂練習,請自行完成,您可以將答案在我的部落格中留言。
現在我突然有個想法,就是想把著個六燈電路板改成八燈,並且代表 Do Re Mi Fa So La Si Do 八度音的燈號,作為彈奏鋼琴的音符指示燈。一般的情況,我們修改完電路之後,要重新寫C語言程式,重新編譯,重新上傳,之後才能展示新的功能,中間有任何錯誤,又必須重新編譯,重新上傳,一直在做這種反覆的動作。如果使用 Python 透過 Firmata 通訊協定,這些過程就免了,因為 Firmata 是標準通訊協定,上傳一次就好,Python 是直譯語言,寫完直接看結果,非常有效率。
我們從上一小節的程式碼直接修改,可以看到我們剛才新增的兩個燈也亮了,這當成我們的一個開機燈號:
import pyfirmata
import time
led1, led2, led3, led4, led5, led6, led7, led8 = 2, 3,
4, 5, 6, 7, 8, 9
port = 'COM3'
board = pyfirmata.Arduino(port)
def style1():
for i in
range(led1, led8 + 1):
board.digital[i].write(1)
time.sleep(0.2)
for i in
range(led8, led1 - 1, -1):
board.digital[i].write(0)
time.sleep(0.2)
for i in range(1):
style1()
接著,我們寫一個
note() 函數,傳入
1 就亮Do燈號,2 就亮 Re燈號,以此類推,接著上面的 for 迴圈往下寫:
def note(x):
global last
for i in x:
if last
!= 0:
board.digital[last].write(0)
if i !=
p:
board.digital[i].write(1)
time.sleep(0.4)
else:
time.sleep(0.4)
board.digital[i].write(0)
time.sleep(0.1)
last = i
play = [led5, led3, led3, p, led4, led2, led2, p, led1,
led2, led3,
led4, led5,
led5, led5, p]
for i in range(2):
note(play)
我們利用 play 列表來存讓音符,note()函數來演奏每個音符,使用p來定義停頓一拍,這樣您可以看到一首小蜜蜂了。
如果我們利用音樂小節的重複特性,就可以用最簡潔的方式完成整首歌,完整程式碼如下:
import pyfirmata
import time
led1, led2, led3, led4, led5, led6, led7, led8, p = 2,
3, 4, 5, 6, 7, 8, 9, 10
port = 'COM3'
board = pyfirmata.Arduino(port)
def style1():
for i in
range(led1, led8 + 1):
board.digital[i].write(1)
time.sleep(0.2)
for i in
range(led8, led1 - 1, -1):
board.digital[i].write(0)
time.sleep(0.2)
for i in range(1):
style1()
last = 0
def note(x):
global last
for i in x:
if last
!= 0:
board.digital[last].write(0)
if i !=
p:
board.digital[i].write(1)
time.sleep(0.4)
else:
time.sleep(0.4)
board.digital[i].write(0)
time.sleep(0.1)
last = i
play1 = [led5, led3, led3, p, led4, led2, led2, p]
play2 = [led1, led2, led3, led4, led5, led5, led5, p]
play3 = [led1, led3, led5, led5, led3, led3, led3, p]
play4 = [led2, led2, led2, led2, led2, led3, led4, p]
play5 = [led1, led3, led5, led5, led1, led1, led1, p]
note(play1)
note(play2)
note(play1)
note(play3)
note(play4)
note(list(map(lambda
x:x+1,play4[:7:]))+[p])
note(play1)
note(play5)
請注意紅色的部分,我使用了
map, lambda, 切片等技巧,如果看不懂,請再回到列表介紹的部分重新看一次。
留言
張貼留言