學習如何用Arduino按鈕來作發出聲音或亮燈
這是一個在網路上的實例教學:有四個按鈕對應四個LED燈,程式會亂數點亮LED,要記住順序依序按下那四個按鈕,若對了就會發出成功的音效,錯了則發出失敗的音效,然後在繼續下一次。
接線如下:
1.從Arduino板的GND接到麵包板
2.Arduino腳位5接開關右上腳,開關佐下腳接地
3.Arduino腳為13接LED長腳,LED短腳接GND
完整電路圖如下
從Arduino板的GND接到麵包板。
先差上四個瞬時型開關,左下腳接地。右上腳,從左到右,接到5、4、3、2。
然後開關的左上腳接LED的短腳,LED的長腳接到100 ohm的電阻。
再然後,從左到右,電阻的另一腳,接到Arduino板的腳位11、10、9、8。
聲音的部分:從Arduino板的腳位Ao接到1k ohm可變電阻的左腳,中腳接蜂鳴器的紅線,蜂鳴器的黑線接地,將以這個可變電組調整音量。
程式碼以功能區塊劃分說明。
#define TONE_PIN A0 // 定義蜂鳴器的腳位
#define NUMBER 4 // 共有四個LED、四個開關
// 定義開關與LED的腳位
const int switches[NUMBER] = {5, 4, 3, 2};
const int leds[NUMBER] = {11, 10, 9, 8};
// 定義各LED對應的音符頻率
const int notes[NUMBER] = {
NOTE_C4, NOTE_D4, NOTE_E4,NOTE_F4,
};
// 使用狀態機的概念,要定義各種狀態
typedef enum{
STATE_START, // 重新開始遊戲
STATE_QUESTION, // 閃爍LED,給出問題
STATE_ANSWER, // 等待使用者按下開關,回答問題
STATE_CORRECT, // 正確,播放恭喜的音效,重置
STATE_WRONG, // 錯誤,播放可惜的音效,重置
STATE_MAX,
} State;
// 定義播放音效的資料
Melody melodys[MELODY_MAX] = {
{noteStart, noteDurationsStart, 13},
{noteCorrect, noteDurationsCorrect, 6},
{noteWrong, noteDurationsWrong, 8},
};
// 以tone()播放音效
void playtone(int *note, int *noteDurations, int num){
for(int thisNote = 0; thisNote < num; thisNote++){
int noteDuration = 3000 / noteDurations[thisNote];
tone(TONE_PIN, note[thisNote], noteDuration);
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
}
}
// 程式裡有三種音效,開始遊戲、回答正確、回答錯誤
typedef enum{
MELODY_START,
MELODY_CORRECT,
MELODY_WRONG,
MELODY_MAX,
} Melody_Enum;
// 把playtone()包一層,用起來比較簡單
void playMelody(Melody_Enum me){
playtone(melodys[me].note, melodys[me].duration, melodys[me].number);
}
// 初始化,開關的腳位,開啟上拉電阻,所以預設狀態會是HIGH,
// 之後讀取開關狀態時,若讀到LOW,代表按下按鍵
void setup(){
for(int i = 0; i < NUMBER; i++){
pinMode(switches[i], INPUT);
digitalWrite(switches[i], HIGH);
pinMode(leds[i], OUTPUT);
digitalWrite(leds[i], LOW);
}
// 亂數,將以亂數製作問題
randomSeed(analogRead(A1));
}
// 只播放一個音符的函式,秀出問題時可用,
// 使用者回答時也可用。
void playOneTone(int note, float delayScale){
int noteDuration = 3000 / 8;
tone(TONE_PIN, note, noteDuration);
int pauseBetweenNotes = noteDuration * delayScale;
delay(pauseBetweenNotes);
}
// 準備好問題後,放出音效、閃爍LED
void playQuestionsTone(){
for(int i = 0; i < q_num; i++){
digitalWrite(leds[questions[i]], HIGH);
playOneTone(notes[questions[i]], 1.3);
digitalWrite(leds[questions[i]], LOW);
}
}
// 檢查問題跟使用者的回答是不是一樣
boolean check(){
for(int i = 0; i < q_num; i++){
if(questions[i] != answers[i]){
return false;
}
}
return true;
}
// 重點
void loop(){
switch(state){ // 以state記錄目前狀態,根據狀態作事
case STATE_START:{ // 開始遊戲,播放開始音效,進入問題狀態
reset();
playMelody(MELODY_START);
state = STATE_QUESTION;
break;
}
case STATE_QUESTION:{ // 問題狀態,以亂數製作問題
questions = (int *)(malloc(sizeof(int) * q_num));
answers = (int *)(malloc(sizeof(int) * q_num));
for(int i = 0; i < q_num; i++){
questions[i] = random(0, NUMBER);
}
answer_num = 0;
playQuestionsTone(); // 顯示問題
lastClickTime = millis(); // 記錄目前時間
state = STATE_ANSWER;
break;
}
case STATE_ANSWER:{
// 看看使用者多久沒按開關了,超過10就宣布失敗
const unsigned long nowTime = millis();
if(nowTime >= lastClickTime + 10000UL){
state = STATE_WRONG;
break;
}
// 讀取每個開關的狀態
for(int i = 0; i < NUMBER; i++){
int ss = digitalRead(switches[i]);
if(ss == LOW){
digitalWrite(leds[i], HIGH);
lastClickTime = nowTime;
answers[answer_num] = i; // 把使用者的輸入答案存起來
answer_num++;
playOneTone(notes[i], 1);
digitalWrite(leds[i], LOW);
delay(200);
break;
}
}
if(answer_num >= q_num){ // 判斷是不是已經回答完了
// 檢查回答正不正確
state = check() ? STATE_CORRECT : STATE_WRONG;
}
break;
}
case STATE_CORRECT:{ // 回答正確,恭喜
q_num++;
playMelody(MELODY_CORRECT);
delay(2000);
state = STATE_START;
break;
}
case STATE_WRONG:{ // 回答錯誤,可惜
playMelody(MELODY_WRONG);
delay(2000);
state = STATE_START;
break;
}
default:{
state = STATE_START;
break;
}
}
}
實際操作影片網址:https://www.youtube.com/watch?v=ZDvK9AbC1eY#action=share
資料來源 網址:http://yehnan.blogspot.tw/2012/02/arduinosimon-says.html