4
編者注:本文作者為Akiba,原發(fā)于FreakLabs。
很多人問過我關(guān)于燈光控制和排序的問題,其中涉及到安裝、顯示和可穿戴設(shè)計(jì)等方方面面,所以我想干脆做一個(gè)詳細(xì)的教程好了!
為此我制作了一個(gè)文本教程和一個(gè)視頻教程,其中視頻教程分為兩部分,如下所示:
視頻一:怎么使用Arduino和Vixen控制發(fā)光序列
時(shí)間線:
00:15 - 在面包板上搭建LED電路
01:46 - 配置Vixen軟件
03:40 - 設(shè)置新的發(fā)光順序
05:12 - 檢查音序的串行輸出
07:08 - 編寫Arduino代碼解碼音序數(shù)據(jù)
10:28 - 在Arduino 系統(tǒng)上測(cè)試發(fā)光序列
視頻二:怎么使用無線的方式控制發(fā)光序列
時(shí)間線:
00:32 - 將原來的系統(tǒng)改成收發(fā)器系統(tǒng)
01:05 - 寫出freakduino發(fā)送器代碼
03:00 - 寫入freakduino接收器代碼
04:52 - 使用Vixen測(cè)試無線系統(tǒng)
05:48 - 使用晶體管實(shí)現(xiàn)LED發(fā)光條和控制電路的交互
06:55 - 戶外測(cè)試
07:42 - 實(shí)例:花式調(diào)酒和Wrecking Crew Orchestra樂隊(duì)
而視頻上看不明白的,就讓我們用文字來解決。首先讓我們一起來研究一下怎么使用有線的方式實(shí)現(xiàn)對(duì)LED發(fā)光的控制,這樣操作起來更加簡(jiǎn)單,對(duì)于新手來說這一步也格外關(guān)鍵。
準(zhǔn)備Arduino、面包板和六顆LED燈,按下圖的方式進(jìn)行連接:
這里我們選擇的是D2~D7這六個(gè)輸出口連接LED,注意在LED和每個(gè)接口之間需要串接一個(gè)100Ω的限流電阻。因?yàn)長(zhǎng)ED只需要20mA的電流即可正常發(fā)光,串接100Ω的電阻差不多剛好能讓標(biāo)準(zhǔn)LED獲得良好的表現(xiàn)。按照電路圖連接好之后是這樣的:
首先你可以測(cè)試一下LED能不能閃,如果你能控制LED閃爍,你就能控制LED的一切。一旦你學(xué)會(huì)了控制LED,控制其它電子設(shè)備也就是說手到擒來的事情。
Vixen
接下來我們來了解一下Vixen軟件,這是一款免費(fèi)軟件。之前我曾經(jīng)用Vixen 2開發(fā)過一個(gè)項(xiàng)目,但Vixen 2用起來老是崩潰,還好現(xiàn)在我們有Vixen 3版本了。另外必須注意的一點(diǎn)是Vixen 3支持的系統(tǒng)至少要是Windows 7,并不支持Windows XP。點(diǎn)擊這里下載。
Vixen 3的設(shè)置比Vixen 2要稍微復(fù)雜一點(diǎn),你必須先摸索一下其將發(fā)光方式映射到輸出上的方式。(視頻上的解釋更容易懂一點(diǎn),但是因?yàn)檎f的是英語,我們還是看看文字版吧。)
首先需要配置我們將會(huì)用到的發(fā)光元素。因?yàn)槲覀冎挥?個(gè)LED,所以我們只需要配置6個(gè)單獨(dú)的元素,其中每個(gè)元素都是可調(diào)節(jié)的燈光或設(shè)備,發(fā)光等級(jí)從0-255可調(diào)。
另外,你還可以設(shè)置調(diào)光曲線和顯示顏色。調(diào)光曲線很容易上手,因?yàn)樵S多LED更多是使用對(duì)數(shù)調(diào)光,而不是線性調(diào)光。你可以很輕松地設(shè)置設(shè)備的線性變化,讓它們的發(fā)光亮度在0-255之間均勻變化。
接下來我們需要設(shè)置音序器的輸出,這里需要用到通用串行端口輸出將數(shù)據(jù)輸出到串口,此處需要設(shè)置輸出的信道的數(shù)量。其將會(huì)通過串口為每一個(gè)信道一次輸出一個(gè)值。在本項(xiàng)目中,我們需要設(shè)置發(fā)光單元和信道的一一對(duì)應(yīng),所以我們需要?jiǎng)?chuàng)建6個(gè)信道。
然后設(shè)置串口參數(shù)。Vixen 3在這方面比Vixen 2進(jìn)步不少,因?yàn)閂ixen 2只能使用計(jì)算機(jī)的COM1-4,而Vixen 2則能夠自動(dòng)檢測(cè)計(jì)算機(jī)的所有串口,并且還都可以使用。串口的設(shè)置相對(duì)標(biāo)準(zhǔn),這里采用的參數(shù)是57600 bps, 8位, 無奇偶校驗(yàn)和1位停止位。
接下來,配置音序器輸出發(fā)送頭文件,該文件是一個(gè)ASCII的“+>”,該頭文件能夠幫助軟件實(shí)現(xiàn)同步已避免出現(xiàn)掉幀的情況。最壞的情況下,在再次進(jìn)行同步之前會(huì)掉2幀。如果我們?cè)O(shè)置的時(shí)序解析率是10微秒,那么就將出現(xiàn)20微秒的差異,這點(diǎn)時(shí)間人類根本無法感知,所以不用擔(dān)心。
最后,我們需要將LED和我們的輸出對(duì)應(yīng)起來。因?yàn)槲覀儾捎玫氖且灰粚?duì)應(yīng)的方式,設(shè)置方式就很簡(jiǎn)單了,只需要高亮發(fā)光元素和信道,然后點(diǎn)擊Patch按鈕即可。完成之后,應(yīng)該就能看到圖像化的一一對(duì)應(yīng)示意圖了。
到這里,Vixen的設(shè)置就完成了。接下來就該設(shè)置新的序列了,點(diǎn)擊“Start”按鈕開始設(shè)置序列流。
然后點(diǎn)擊New Sequence按鈕,會(huì)彈出一個(gè)用于創(chuàng)建新序列的窗口,可以看到其中每一個(gè)LED對(duì)應(yīng)的序列。
接下來該在時(shí)間上加入音頻信號(hào)了。
下面我用到了一個(gè)內(nèi)置的節(jié)奏檢測(cè)器,可以自動(dòng)根據(jù)音頻的節(jié)奏在時(shí)間線上自動(dòng)進(jìn)行標(biāo)記,這讓我們的工作輕松不少。這一步并不是必需的,我們也可以進(jìn)行手動(dòng)編輯。
接下來,還需要為已經(jīng)標(biāo)記的重音設(shè)置表現(xiàn)效果。因?yàn)檫@些標(biāo)記都是自動(dòng)生成的,為他們?cè)O(shè)置好的表現(xiàn)效果看起來就像是精確控制的實(shí)時(shí)表現(xiàn)。你可以設(shè)置LED在重音點(diǎn)改變顏色、閃爍或者亮度突變。
Arduino
接下來是Arduino代碼部分。在編程之前,首先需要檢查串口出來的數(shù)據(jù)是什么格式的。不過這一步并不是必須的,只是說能夠加深我們對(duì)項(xiàng)目的理解,在書寫代碼時(shí)也更加方便。
我利用Arduino的代碼實(shí)現(xiàn)了一個(gè)狀態(tài)機(jī),通過這個(gè)狀態(tài)機(jī),我們可以很容易地實(shí)現(xiàn)串行協(xié)議的解碼。
#define MAX_CHANNELS 6
int ch;
int state;
int chVal[MAX_CHANNELS] = {0};
int pins[] = {2, 3, 4, 5, 6, 7};
enum states
{
IDLE,
DELIM,
READ,
DISP
};
void setup()
{
for (ch=0; ch<MAX_CHANNELS; ch++)
{
pinMode(pins[ch], OUTPUT);
digitalWrite(pins[ch], LOW);
}
state = IDLE;
ch = 0;
Serial.begin(57600);
}
void loop()
{
if (Serial.available())
{
switch (state)
{
case IDLE:
ch = 0;
if (Serial.read() == '+')
{
state = DELIM;
}
else
{
state = IDLE;
}
break;
case DELIM:
ch = 0;
if (Serial.read() == '>')
{
state = READ;
}
else
{
state = IDLE;
}
break;
case READ:
chVal[ch++] = Serial.read();
if (ch >= MAX_CHANNELS)
{
ch = 0;
state = DISP;
}
break;
case DISP:
state = IDLE;
for (ch=0; ch<MAX_CHANNELS; ch++)
{
if (chVal[ch] > 0)
{
digitalWrite(pins[ch], HIGH);
}
else
{
digitalWrite(pins[ch], LOW);
}
}
break;
}
}
}
這個(gè)狀態(tài)機(jī)擁有4個(gè)狀態(tài),第一個(gè)狀態(tài)為空閑(IDLE)狀態(tài),該狀態(tài)為默認(rèn)狀態(tài)。在該狀態(tài)時(shí),程序等待“+”符號(hào),這是幀標(biāo)記的起點(diǎn)?!?”進(jìn)入后,狀態(tài)機(jī)進(jìn)入第二個(gè)狀態(tài)DELIM。在此狀態(tài)下,程序等待第二個(gè)分隔符標(biāo)記“>”,此符號(hào)到來后則幀開始運(yùn)作。這樣做可以減少出現(xiàn)誤報(bào)幀的可能,畢竟按一定的序列出現(xiàn)兩個(gè)特定符號(hào)的可能性還是比較小的。
兩個(gè)符號(hào)齊備了之后,狀態(tài)機(jī)進(jìn)入第三個(gè)狀態(tài)READ,此時(shí)讀取剩余的幀,并將其存儲(chǔ)到數(shù)組中。完成之后,狀態(tài)機(jī)過渡到DISP狀態(tài)。此時(shí)我們就要展示我們的數(shù)據(jù)了。在該狀態(tài)下,我們會(huì)循環(huán)數(shù)組中的每個(gè)數(shù)值,如果該數(shù)組是非零的,那么就將LED打開,否則就關(guān)閉LED。如果我們?cè)谶@些引腳上再配置上PWM(脈沖寬度調(diào)制),就能實(shí)現(xiàn)對(duì)LED光亮度的控制。但為了教程的簡(jiǎn)單,這里暫時(shí)只使用了簡(jiǎn)單的開關(guān)功能。
上面也就基本上實(shí)現(xiàn)了Arduino對(duì)LED的控制。接下來我們將換用無線的方式來實(shí)現(xiàn)這樣的功能。
無線
在開發(fā)這個(gè)項(xiàng)目時(shí)我將Arduino換成了Freakduino,不過本質(zhì)上都是一樣的。
這里我使用的是900 MHz大覆蓋面積版的Freakduino,這個(gè)版本的無線穿墻效果不錯(cuò),范圍也比較大。另外,900MHz頻段的干擾也比較少,很少WiFi具有這個(gè)頻段,在人多的地方進(jìn)行表演時(shí),這一點(diǎn)格外重要。
#include <chibi.h>
#define MAX_CHANNELS 6
#define MY_ADDR 5
#define DEST_ADDR 3
int ch;
int state;
byte chVal[MAX_CHANNELS] = {0};
enum states
{
IDLE,
DELIM,
READ,
DISP
};
void setup()
{
state = IDLE;
ch = 0;
chibiInit();
chibiSetShortAddr(MY_ADDR);
Serial.begin(57600);
}
void loop()
{
if (Serial.available())
{
switch (state)
{
case IDLE:
ch = 0;
if (Serial.read() == '+')
{
state = DELIM;
}
else
{
state = IDLE;
}
break;
case DELIM:
ch = 0;
if (Serial.read() == '>')
{
state = READ;
}
else
{
state = IDLE;
}
break;
case READ:
chVal[ch++] = Serial.read();
if (ch >= MAX_CHANNELS)
{
ch = 0;
state = DISP;
}
break;
case DISP:
state = IDLE;
chibiTx(DEST_ADDR, chVal, MAX_CHANNELS);
break;
}
}
}
首先讓我們來看看發(fā)送器代碼,這個(gè)代碼和上面有線版本的代碼大致類似。其中最主要的不同點(diǎn)是我們需chibiArduino來通過無線的方式來發(fā)送代碼。chibiArduino是一個(gè)無線協(xié)議棧,是我根據(jù)開放的IEEE 802.15.4標(biāo)準(zhǔn)編寫的,我已經(jīng)在配置和特性上對(duì)其進(jìn)行了很大程度的簡(jiǎn)化,你只需要簡(jiǎn)單地啟動(dòng)它就可以實(shí)現(xiàn)數(shù)據(jù)的收發(fā)了。不過對(duì)一般用戶而言,使用什么樣的通信標(biāo)準(zhǔn)其實(shí)都沒有關(guān)系。
仔細(xì)研究一下代碼,你會(huì)發(fā)現(xiàn)其中還新定義了源地址和目標(biāo)地址。源地址是我們自己的地址,而目標(biāo)地址則是數(shù)據(jù)需要發(fā)送到的地址。
我也使用chibiInit()函數(shù)初始化了chibiArduino棧,這一步會(huì)將棧和寄存器設(shè)置成默認(rèn)值,并使其為數(shù)據(jù)接收做好準(zhǔn)備。chibiSetShortAddr()函數(shù)則可以根據(jù)我們?cè)O(shè)置的設(shè)備地址和其它設(shè)備建立通信。我們只需要設(shè)置短地址一次就可以了,然后改地址會(huì)被存儲(chǔ)到非易失性存儲(chǔ)器中。但在這個(gè)項(xiàng)目中,我們每一次開機(jī)都要對(duì)地址進(jìn)行設(shè)置。
我們?nèi)匀皇褂肰ixen的狀態(tài)機(jī)來實(shí)現(xiàn)對(duì)串行協(xié)議的解碼。在循環(huán)代碼中,在DISP狀態(tài)的主要不同在于我們不再循環(huán)數(shù)據(jù)數(shù)列和開關(guān)LED,我們將數(shù)列和通過無線的方式發(fā)送給接收器,這一步通過chibiTx()函數(shù)實(shí)現(xiàn)。chibiTx()含有三個(gè)參數(shù):目標(biāo)地址、以數(shù)列形式存儲(chǔ)的數(shù)據(jù)和數(shù)據(jù)的長(zhǎng)度。
#include <chibi.h>
#define MAX_CHANNELS 6
#define MY_ADDR 3
#define DEST_ADDR 5
int pins[] = {2, 3, 4, 5, 6, 7};
int i;
void setup()
{
for (i=0; i<MAX_CHANNELS; i++)
{
pinMode(pins[i], OUTPUT);
digitalWrite(pins[i], LOW);
}
chibiInit();
chibiSetShortAddr(MY_ADDR);
Serial.begin(57600);
}
void loop()
{
int i;
// Check if any data was received from the radio. If so, then handle it.
if (chibiDataRcvd() == true)
{
int len, rssi, src_addr;
byte buf[100]; // this is where we store the received data
// retrieve the data and the signal strength
len = chibiGetData(buf);
// discard the data if the length is 0. that means its a duplicate packet
if (len == 0) return;
rssi = chibiGetRSSI();
src_addr = chibiGetSrcAddr();
// Print out the message and the signal strength
Serial.print("Data from node 0x");
Serial.print(src_addr, HEX);
Serial.print(": ");
for (i=0; i<len; i++)
{
if (buf[i] > 0)
{
digitalWrite(pins[i], HIGH);
}
else
{
digitalWrite(pins[i], LOW);
}
Serial.print(buf[i]);
Serial.print(" ");
}
Serial.print(", RSSI = 0x"); Serial.println(rssi, HEX);
}
}
接下來是接收端的代碼。在接收端我們需要接受發(fā)送端傳送的數(shù)據(jù),并將這些數(shù)據(jù)通過LED表現(xiàn)出來。
同樣我們首先設(shè)置源地址和目標(biāo)地址,然后我們初始化引腳和chibi棧。
在循環(huán)函數(shù)中,我們加入了一些新代碼。在循環(huán)函數(shù)中,首先會(huì)對(duì)數(shù)據(jù)的接受情況進(jìn)行檢查,如果數(shù)據(jù)接受,則函數(shù)chibiDataRcvd()會(huì)返回“真”,然后就接受數(shù)據(jù)。為了接受數(shù)據(jù),我們聲明了一個(gè)len變量,其存儲(chǔ)的是要接收的數(shù)據(jù)的字節(jié)長(zhǎng)度以及一個(gè)100字節(jié)長(zhǎng)度的字節(jié)數(shù)列。為什么要選擇100字節(jié)呢?因?yàn)閏hibiArduino棧的最大負(fù)載為100字節(jié)。你可以設(shè)置小一點(diǎn)以節(jié)約RAM。
然后chibiGetData()函數(shù)被作為字節(jié)數(shù)列的一個(gè)參數(shù)調(diào)用。其將會(huì)將接收到的數(shù)據(jù)逐個(gè)寫入到這個(gè)字節(jié)數(shù)列中,并返回接收數(shù)據(jù)的長(zhǎng)度。另外,我們進(jìn)行了一下長(zhǎng)度檢查,以防止數(shù)據(jù)長(zhǎng)度為0的情況。如果長(zhǎng)度為0,則說明我們之前已經(jīng)接受到過這個(gè)數(shù)據(jù),這是一個(gè)重復(fù)的數(shù)據(jù)包。接收到的數(shù)據(jù)應(yīng)該包含6個(gè)字節(jié),接下來就是逐個(gè)字節(jié)檢查,并將對(duì)應(yīng)的LED的亮度進(jìn)行相應(yīng)的調(diào)整。
另外我還在chibiArduino棧中額外增加了一些其它的數(shù)據(jù),這些數(shù)據(jù)不是必需的,但是對(duì)調(diào)試等工作有很重要的幫助。比如記錄信號(hào)強(qiáng)度的變化,多個(gè)源地址可以從多個(gè)來源接收信號(hào),同時(shí)也可以對(duì)可能的設(shè)備故障進(jìn)行檢查,這在戶外應(yīng)用時(shí)還是大有裨益的。
至此,對(duì)Arduino和Vixen實(shí)現(xiàn)對(duì)LED燈光的控制的介紹就完成了,趕快自己做一個(gè)來裝飾你的家吧。
2015-2016賽季全球創(chuàng)客馬拉松深圳大學(xué)站已經(jīng)開始接受報(bào)名啦!關(guān)注“硬創(chuàng)邦”(微信號(hào):leiphone_bang),回復(fù)“深大”即可參與報(bào)名!此外還可加入全球創(chuàng)客馬拉松主群(群號(hào):259592983),參與我們的互動(dòng)討論~
雷峰網(wǎng)原創(chuàng)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。