在這個(gè)有趣的項(xiàng)目中,我們將學(xué)習(xí)如何使用Arduino制作嗡嗡聲有線游戲。您可以在入門工具包(入門工具包中有什么?)和房屋周圍找到許多所需的零件。該項(xiàng)目使用Arduino,盡管您幾乎可以使用周圍的任何微控制器(以下是5美元的微控制器之間的比較,以獲取一些啟發(fā))。
查看最終結(jié)果-它甚至可以播放音樂:
您需要什么
以下是完成此項(xiàng)目所需的核心部分:
1 x Arduino UNO或類似產(chǎn)品。
1 x金屬衣架。
2 x 220歐姆電阻。
1 x面包板。
1 x壓電蜂鳴器。
2 x鱷魚夾。
各種熱縮管。
公對公連接線。
以下是一些可選部件增強(qiáng)構(gòu)造:
1 x附加壓電蜂鳴器。
1 x定位桿。
1 x四個(gè)七段式顯示器。
1 x 220歐姆電阻。
1 x瞬時(shí)按鈕。
公對母連接線。
木板(用于保護(hù)箱)。
各種木螺釘。
只要有足夠的引腳,幾乎所有的Arduino都可以使用。如果不確定您的需求,請查看此購買指南。
構(gòu)建計(jì)劃
雖然這可能看起來很復(fù)雜,這實(shí)際上是一個(gè)非常簡單的項(xiàng)目。我將從基本游戲開始,然后添加其他組件以增加復(fù)雜性。您可以根據(jù)自己的需要選擇“選擇”。
核心機(jī)制由線形和手柄上的環(huán)組成。玩家必須引導(dǎo)整個(gè)路線的循環(huán),而不能使兩個(gè)方向接觸。如果兩者接觸,則電路完成,并且蜂鳴器響起。當(dāng)然也可以不使用微控制器來構(gòu)建該電路,但是這樣做的樂趣何在(以及還能聽到Monty Python的“ Flying Circus”主題曲)?
課程 strong》
這是玩家指導(dǎo)自己的回合的形狀。這是整個(gè)游戲的基礎(chǔ),所以做得好!我選擇先降一小點(diǎn),然后再進(jìn)行一次大的爬升。將金屬衣架彎曲成所需的形狀。黃銅線或銅管也可以正常工作,盡管衣架可能是最便宜的。
您可能需要戴手套,用鉗子或錘子才能使事情變得完美。用斷線鉗剪掉多余的部分。留下兩個(gè)垂直的立柱以推入基座。為了安全起見,您想歸檔切割端。最后,切開兩根熱縮管,并如下放置在末端:
這將使回路與過程絕緣,從而提供一個(gè)起點(diǎn)/末端或安全區(qū)域。另外,如果您沒有熱縮管,也可以用膠帶甚至是吸管。
現(xiàn)在將電纜連接到課程的一端。您在這里有兩個(gè)選擇:可以焊接,也可以使用鱷魚夾。鱷魚夾是較容易的選擇,但焊接是更可靠和長期的選擇。確保首先用砂紙“弄粗”衣架表面,并使用大量助焊劑。 (以前從來沒有焊接過?請學(xué)習(xí)這里的方法。)
根據(jù)下一步在底座上鉆的孔的大小,您可能需要先將電纜穿過安裝孔。使用兩根絞在一起的電線會(huì)提高耐用性:
使用電鉆來做這有很大幫助:
基礎(chǔ)
是時(shí)候創(chuàng)建基礎(chǔ)了。這用于將航線保持在直立位置,并提供將電子設(shè)備固定到的位置。我使用了一些松木碎片,盡管您可以使用房子周圍的任何物品,甚至可以用紙板箱。
將三塊切成“ n”形。只需將這三部分?jǐn)Q(或膠)在一起。切記先在側(cè)板上鉆一個(gè)導(dǎo)向孔,以防止側(cè)板裂開。您可能需要沉頭螺釘(特別是如果要填充然后上漆),因此我強(qiáng)烈建議使用沉頭鉆頭。如果您沒有沉頭工具或鉆孔,則可以使用較大直徑的鉆頭。
鉆兩個(gè)孔,其距離應(yīng)足夠遠(yuǎn),以便安裝過程的末端。 ink孔下面的東西,準(zhǔn)備粘貼。
手柄
現(xiàn)在是時(shí)候制作回路/控制器了。在一端扭曲一小部分衣架,以形成帶有小金屬手柄的環(huán)。確保銼平切割邊緣,然后在必要時(shí)用膠帶/泡沫覆蓋。
這將形成電路的另一半-當(dāng)此循環(huán)接觸到當(dāng)然它將完成電路(就像一個(gè)開關(guān))。將另一根線焊接(或使用鱷魚夾)到其底部,與之前在該過程中所做的完全相同。
為實(shí)際手柄切去一小段銷釘。該金屬環(huán)將插入該手柄中。如果沒有銷釘,則可以使用皮帶或圓盤砂光機(jī)將一塊方形的軟木修圓(也可以使用砂紙,但這會(huì)花費(fèi)很長時(shí)間)。
鉆一個(gè)洞這個(gè)手柄。它必須足夠大以適合金屬環(huán)和導(dǎo)線穿過:
盡管很棘手,但這可以在立柱鉆機(jī)上完成。 Alathe可以完美地完成這項(xiàng)工作:
是的,我很清楚這是金屬車床(對于感興趣的人,這是Boley制表車床,來自1930年代。我認(rèn)為這是3C,但是如果您對此有所了解,我希望能收到您的來信。)
您還可以使用去除中心的圓珠筆。
最后,使用熱膠將電纜和回路固定到手柄中。熱膠會(huì)提供牢固的(但不是永久性的)固定裝置,因此非常適合。
完成
將導(dǎo)線束插入底座的孔中。不要忘了先添加回路/控制器。再次使用熱熔膠,通過如下方式填充底座底部的沉孔,將路線固定到底座上:
電路 》
這是完整的電路。您不必如此復(fù)雜—在分解每個(gè)部分時(shí)請繼續(xù)閱讀。
首先,將兩個(gè)壓電元件連接到數(shù)字引腳10和11極性無關(guān)緊要:
您不必使用兩個(gè)壓電體-我這樣做的唯一原因是在出現(xiàn)以下情況時(shí)會(huì)發(fā)出更大的嗡嗡聲電線接觸。將一側(cè)連接到數(shù)字引腳,另一側(cè)接地。
現(xiàn)在插入金屬層并處理:
同樣,它不會(huì)無論這兩個(gè)連接方式如何。電路的這一部分就像按鈕或開關(guān)一樣,當(dāng)循環(huán)觸碰到路線時(shí),玩家將完成電路。確保同時(shí)包括兩個(gè)電阻。
一個(gè)電阻將電路接地(稱為下拉電阻),以確保其不會(huì)“浮空”(這使Arduino能夠檢測到電路的變化)。另一個(gè)電阻器保護(hù)Arduino。當(dāng)兩個(gè)部分接觸時(shí),+ 5V進(jìn)入數(shù)字引腳。如果不存在此電阻,則可能會(huì)出現(xiàn)短路故障–如果幸運(yùn)的話,您的計(jì)算機(jī)將斷開USB插座的連接,以吸收過多的電流。
連接信號(hào)線(紫色,如圖所示)連接到數(shù)字引腳9。
下一步,將按鈕連接到數(shù)字引腳2:
最后,連接七段式LED顯示屏:
該特殊型號(hào)來自Seeed。它使用TM1637驅(qū)動(dòng)四個(gè)顯示器-這意味著僅需要兩個(gè)數(shù)字引腳。將 GND 連接到Arduino地面,將 VCC 連接到Arduino + 5V。將 D10 連接到Arduino數(shù)字引腳13,將 CLK 連接到數(shù)字引腳12。
代碼
要使該項(xiàng)目正常工作將需要兩個(gè)附加文件。第一個(gè)稱為pitches.h。該文件僅將音符名稱映射到其壓電值。例如,您可以簡單地說“ NOTE_C3”而不是“ 31”,這使編寫樂曲變得容易得多。這是在公共領(lǐng)域,可以在Arduino網(wǎng)站上找到。按照說明創(chuàng)建一個(gè)名為pitches.h的新文件(或者,將代碼粘貼到現(xiàn)有腳本中。)
接下來,您需要一種方法來實(shí)際在壓電板上彈奏音符/旋律。 Anthony DiGirolamo在Github上撰寫的要點(diǎn)包含您需要的代碼。復(fù)制“ void buzz”和“}}”之間的所有內(nèi)容并將其粘貼到您的主文件中。作為參考,這里是:
void buzz(int targetPin, long frequency, long length) {
/* Buzzer example function by Rob Faludi
http://www.faludi.com
https://gist.github.com/AnthonyDiGirolamo/1405180
*/
long delayValue = 1000000/frequency/2; // calculate the delay value between transitions
//// 1 second‘s worth of microseconds, divided by the frequency, then split in half since
//// there are two phases to each cycle
long numCycles = frequency * length/ 1000; // calculate the number of cycles for proper timing
//// multiply frequency, which is really cycles per second, by the number of seconds to
//// get the total number of cycles to produce
for (long i=0; i 《 numCycles; i++){ // for the calculated length of time.。.
digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphragm
delayMicroseconds(delayValue); // wait for the calculated delay value
digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphragm
delayMicroseconds(delayValue); // wait again for the calculated delay value
}
}
您需要的最后一個(gè)庫是控制七段顯示-如果您不使用它,則可以跳過此步驟。該庫名為 TM1637 ,由創(chuàng)建驅(qū)動(dòng)板的同一家公司Seeed創(chuàng)建。
在Arduino IDE中,轉(zhuǎn)到“管理庫”( Sketch 》 包含庫》 管理庫)。這將調(diào)出庫管理器。等待幾秒鐘進(jìn)行更新,然后在右上方的搜索框“ TM1637”中搜索。將找到兩個(gè)庫-您需要“ TM1637”而不是“ TM1637Display”。選擇,然后單擊“安裝”。
此庫的最后一項(xiàng)任務(wù)-它尚未完成!就目前而言,該庫只能顯示數(shù)字0–9和字母A–F。如果這涵蓋了您要顯示的所有內(nèi)容,則可以跳過此步驟。如果不是,則需要修改代碼。放松!這聽起來并不那么困難,并且如果您可以使用Arduino IDE編寫代碼,則可以執(zhí)行此操作。
首先,打開您的庫文件夾。這將在您的Arduino文件夾中。在Mac OS X上,該文件位于/Users/Joe/Documents/Arduino/Libraries中。打開名為 TM1637 的文件夾。您將需要編輯名為TM1637.cpp的文件-您可以放心地忽略擴(kuò)展名為.h的其他文件。在您喜歡的文本編輯器(對我來說,這是Sublime Text 3),記事本或Arduino IDE中打開此文件。
從此修改第三行代碼:
static int8_t TubeTab[] = {0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};//0~9,A,b,C,d,E,F(xiàn)
為此:
static int8_t TubeTab[] = {
/* defaults */
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
0x66, // 4
0x6d, // 5
0x7d, // 6
0x07, // 7
0x7f, // 8
0x6f, // 9
0x77, // A -- 10
0x7c, // b -- 11
0x39, // C -- 12
0x5e, // d -- 13
0x79, // E -- 14
0x71, // F -- 15
/* additional */
0x174, // h -- 16
0x176, // H -- 17
0x138, // L -- 18
0x15, // M -- 19
0x137, // n -- 20
0x73, // P -- 21
0x67, // q -- 22
0x131, // r -- 23
0x78, // t -- 24
0x240 // - 25
};
您現(xiàn)在可以保存并關(guān)閉此文件。在每個(gè)元素之后,注釋描述了這是什么字符。注釋的下一部分是元素的索引。
是時(shí)候編寫實(shí)際的代碼了。首先,包括前面提到的兩個(gè)庫:
#include
#include
現(xiàn)在創(chuàng)建顯示對象:
TM1637 *_display = new TM1637(12, 13);
不要擔(dān)心,如果您不了解語法-此行告訴Arduino,將針腳12和13連接到七段顯示器,并進(jìn)行適當(dāng)配置。
這首歌存儲(chǔ)在melody和tempo。這些包含所有音符和音樂的音符持續(xù)時(shí)間。如果您想更改音樂,請修改這些數(shù)組(盡管它不如粘貼音符值那么簡單,定時(shí)是音樂中非常重要的一部分)。 songState變量僅存儲(chǔ)最后播放的音符的位置。這樣可以確保從頭到尾播放旋律,而不是前后不停跳動(dòng):
int songState = 0;
int melody[] = {
NOTE_F4,。..}
int tempo[] = {
8,。..}
請注意,我已經(jīng)刪除了數(shù)組的內(nèi)容,有關(guān)完整代碼,請參見下文
此代碼是非阻塞的-這意味著Arduino可以同時(shí)執(zhí)行多個(gè)任務(wù)。請查看此說明以獲取更多信息。計(jì)時(shí)器的設(shè)置方法如下:
unsigned long previousMillis1 = 0;
const long interval1 = 1500;
變量previousMillis1將在以后的階段進(jìn)行更新以存儲(chǔ)當(dāng)前時(shí)間。 interval1變量存儲(chǔ)兩次代碼執(zhí)行之間的等待時(shí)間-在這種情況下為1.5秒。它定義為const,這意味著它是恒定的并且永遠(yuǎn)不會(huì)改變-這允許Arduino進(jìn)一步優(yōu)化代碼。
在setup()函數(shù)中,有一些事情繼續(xù)。首先,設(shè)置輸入和輸出。這是必須要做的,因此Arduino知道每個(gè)引腳都連接了什么:
pinMode(9, INPUT); // setup circuit
pinMode(10, OUTPUT); // setup buzzer 1
pinMode(11, OUTPUT); // setup buzzer 2
pinMode(2, INPUT); // setup button
現(xiàn)在顯示器需要配置:
_display-》set(5); // set brightness
_display-》point(false); // remove colon
_display-》init(); // start display
方法set,point和init全部包含在_display對象中。代替點(diǎn),使用指針(“-》”)訪問它們。再次,不必?fù)?dān)心語法(盡管,如果您想了解更多,請查閱C ++指針)。
主循環(huán)有兩種游戲模式:挑戰(zhàn)和免費(fèi)游戲。免費(fèi)游戲允許玩家無限次地玩游戲。挑戰(zhàn)模式使用showCountdown方法將計(jì)時(shí)器設(shè)置為20秒。它使用按鈕來啟動(dòng)和停止計(jì)時(shí)器。當(dāng)前,更改游戲模式的唯一方法是手動(dòng)編輯名為mode的變量。看看是否可以添加另一個(gè)按鈕來執(zhí)行此操作并適當(dāng)?shù)匦薷拇a。
buzz方法將播放為其提供的注釋。與sing結(jié)合使用。唱歌方法會(huì)遍歷每個(gè)音符并進(jìn)行演奏。定期調(diào)用此方法,盡管僅在自上次演奏以來經(jīng)過了足夠的時(shí)間后才會(huì)演奏下一個(gè)音符。一旦歌曲到達(dá)結(jié)尾,它將把歌曲重置為1節(jié)(songState = 14)。您可以將其設(shè)置為零以從頭開始播放歌曲,但是這樣做的原因是跳過介紹。簡介會(huì)在Arduino通電后播放一次,然后不再播放。
showFree和showPlay方法只需將單詞“ FrEE”和“ PLAY”寫入”顯示。注意,當(dāng)所有其他字符均為大寫時(shí),free中的“ r”如何變?yōu)樾憽_@是七段顯示器的局限之一。它們不能顯示字母表中的每個(gè)字母,并且它們可以顯示的某些字符必須是大小寫混合的。
toggleFreePlay方法在“ FREE”和“ PLAY”之間閃爍顯示。再次,它以非阻塞方式進(jìn)行此操作。
另一個(gè)有用的方法是showNumber。這樣會(huì)在顯示屏的中間兩個(gè)字符中寫入一個(gè)數(shù)字,如下所示:
顯示屏不夠智能,無法知道如何顯示大數(shù)字,它必須明確告知該怎么做。此方法使用一些簡單的邏輯在每個(gè)字符上顯示適當(dāng)?shù)臄?shù)字。
最后使用的方法稱為showCountdown。這將在20處開始計(jì)數(shù),并每秒減少1。如果該值達(dá)到零,它會(huì)嗡嗡響三聲,表明時(shí)間已用完。
所有代碼都放在這里:
#include // include display library
#include // include pitches
TM1637 *_display = new TM1637(12, 13); // create display object, 12 = CLK (clock), 13 = D10 (data)
// music
int songState = 0;
int melody[] = {
NOTE_F4, NOTE_E4, NOTE_D4, NOTE_CS4,
NOTE_C4, NOTE_B3, NOTE_AS3, NOTE_A3,
NOTE_G3, NOTE_A3, NOTE_AS3, NOTE_A3,
NOTE_G3, NOTE_C4, 0,
NOTE_C4, NOTE_A3, NOTE_A3, NOTE_A3,
NOTE_GS3, NOTE_A3, NOTE_F4, NOTE_C4,
NOTE_C4, NOTE_A3, NOTE_AS3, NOTE_AS3,
NOTE_AS3, NOTE_C4, NOTE_D4, 0,
NOTE_AS3, NOTE_G3, NOTE_G3, NOTE_G3,
NOTE_FS3, NOTE_G3, NOTE_E4, NOTE_D4,
NOTE_D4, NOTE_AS3, NOTE_A3, NOTE_A3,
NOTE_A3, NOTE_AS3, NOTE_C4, 0,
NOTE_C4, NOTE_A3, NOTE_A3, NOTE_A3,
NOTE_GS3, NOTE_A3, NOTE_A4, NOTE_F4,
NOTE_F4, NOTE_C4, NOTE_B3, NOTE_G4,
NOTE_G4, NOTE_G4, NOTE_G4, 0,
NOTE_G4, NOTE_E4, NOTE_G4, NOTE_G4,
NOTE_FS4, NOTE_G4, NOTE_D4, NOTE_G4,
NOTE_G4, NOTE_FS4, NOTE_G4, NOTE_C4,
NOTE_B3, NOTE_C4, NOTE_B3, NOTE_C4, 0
};
int tempo[] = {
8, 16, 8, 16,
8, 16, 8, 16,
16, 16, 16, 8,
16, 8, 3,
12, 16, 16, 16,
8, 16, 8, 16,
8, 16, 8, 16,
8, 16, 4, 12,
12, 16, 16, 16,
8, 16, 8, 16,
8, 16, 8, 16,
8, 16, 4, 12,
12, 16, 16, 16,
8, 16, 8, 16,
8, 16, 8, 16,
8, 16, 4, 16,
12, 17, 17, 17,
8, 12, 17, 17,
17, 8, 16, 8,
16, 8, 16, 8, 1
};
// non blocking setup
// free play
unsigned long previousMillis1 = 0; // time words last changed
const long interval1 = 1500; // interval between changing
// music
unsigned long previousMillis2 = 0; // time last changed
const long interval2 = 100; // interval between notes
int displayStatus = 0; // keep track of what’s displayed
int mode = 0; // keep track of game mode -- change to 0 or 1 for different modes
bool countdown = false;
unsigned long previousMillis3 = 0; // time last changed
const long interval3 = 1000; // interval between countdown
int count = 20; // challenge mode timer
void setup() {
// put your setup code here, to run once:
pinMode(9, INPUT); // setup circuit
pinMode(10, OUTPUT); // setup buzzer 1
pinMode(11, OUTPUT); // setup buzzer 2
pinMode(2, INPUT); // setup button
_display-》set(5); // set brightness
_display-》point(false); // remove colon
_display-》init(); // start display
}
void loop() {
// put your main code here, to run repeatedly:
if(mode == 0) {
// challenge mode
if(digitalRead(2) == HIGH) {
delay(25);
if(digitalRead(2) == HIGH) {
countdown = true; // stop the countdown
}
else {
countdown = false; // stop the countdown
}
}
if(countdown) {
showCountdown(); // advance countdown
}
}
else {
// free play
toggleFreePlay();
}
if(digitalRead(10) == HIGH) {
delay(25);
if(digitalRead(10) == HIGH) {
while(digitalRead(10) == HIGH) {
buzz(11, NOTE_B0, 1000/24);
}
}
}
else
sing();
}
void showCountdown() {
// countdown the time remaining
unsigned long currentMillis = millis(); // current time
if (currentMillis - previousMillis3 》= interval3) {
previousMillis3 = currentMillis;
--count;
showNumber(count);
if(count == 0) {
// game over
countdown = false;
count = 20;
// reset countdown
// buzz 3 times
buzz(11, NOTE_B0, 1000/24);
delay(100);
buzz(11, NOTE_B0, 1000/24);
delay(100);
buzz(11, NOTE_B0, 1000/24);
}
}
}
void showNumber(int number) {
// show numbers (maximum 99) on display
_display-》display(0, 25); // write - to segment 1
_display-》display(3, 25); // write - to segment 4
// write number to middle of display
if(number == 10)
{
_display-》display(1,1);
_display-》display(2,0);
}
else if(number 》 9)
{
_display-》display(1,1);
int newVal = number - 10;
_display-》display(2, newVal);
}
else
{
_display-》display(1,0);
_display-》display(2,number);
}
}
void toggleFreePlay() {
// scroll between words without blocking
unsigned long currentMillis = millis(); // current time
if (currentMillis - previousMillis1 》= interval1) {
previousMillis1 = currentMillis;
if(displayStatus == 1)
showPlay();
else
showFree();
}
}
void showPlay() {
// write “PLAY” to the display
_display-》display(0, 21); // write P to segment 1
_display-》display(1, 18); // write L to segment 2
_display-》display(2, 10); // write A to segment 3
_display-》display(3, 4); // write Y to segment 4
displayStatus = 2;
}
void showFree() {
// write “Free” to the display
_display-》display(0, 15); // write F to segment 1
_display-》display(1, 23); // write r to segment 2
_display-》display(2, 14); // write E to segment 3
_display-》display(3, 14); // write E to segment 4
displayStatus = 1;
}
void buzz(int targetPin, long frequency, long length) {
/* Buzzer example function by Rob Faludi
http://www.faludi.com
https://gist.github.com/AnthonyDiGirolamo/1405180
*/
long delayValue = 1000000/frequency/2; // calculate the delay value between transitions
//// 1 second‘s worth of microseconds, divided by the frequency, then split in half since
//// there are two phases to each cycle
long numCycles = frequency * length/ 1000; // calculate the number of cycles for proper timing
//// multiply frequency, which is really cycles per second, by the number of seconds to
//// get the total number of cycles to produce
for (long i=0; i 《 numCycles; i++){ // for the calculated length of time.。.
digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphragm
delayMicroseconds(delayValue); // wait for the calculated delay value
digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphragm
delayMicroseconds(delayValue); // wait again for the calculated delay value
}
}
void sing() {
// play the song in a non blocking way
unsigned long currentMillis = millis();
if (currentMillis - previousMillis2 》= interval2) {
previousMillis2 = currentMillis;
int noteDuration = 1000 / tempo[songState];
buzz(10, melody[songState], noteDuration);
int pauseBetweenNotes = noteDuration;
delay(pauseBetweenNotes);
// stop the tone playing:
buzz(10, 0, noteDuration);
++songState;
// start song again if finished
if(songState 》 79) {
songState = 14; // skip intro
}
}
}
保存將該文件作為“ buzzwire”(文件》另存為),然后將其上傳到您的開發(fā)板上(文件》上傳)。如果您不確定如何上傳Arduino,或者該代碼看起來有些嚇人,請查看我們的《 Arduino入門指南》。一切都很好,您現(xiàn)在應(yīng)該擁有自己的嗡嗡聲有線游戲-很棒!
責(zé)任編輯:wv
-
Arduino
+關(guān)注
關(guān)注
188文章
6477瀏覽量
187641
發(fā)布評論請先 登錄
相關(guān)推薦
評論