# Arduino 教學 5:利用光敏電阻控制蜂鳴器
> 作者:王一哲
> 日期:2016/9/13
## 所需元件
1. 光敏電阻1個
2. 10kΩ電阻1個
3. 蜂鳴器或喇叭1個
4. 麵包板1塊
5. Arduino Uno開發板1塊
6. 麵包板連接線數條
<img height="100%" width="100%" src="https://i.imgur.com/VB0I1UV.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">線路圖</div>
</br>
<img height="100%" width="100%" src="https://i.imgur.com/f0cjb3E.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">實際的裝置照片(連接蜂鳴器)</div>
</br>
<img height="100%" width="100%" src="https://i.imgur.com/8ORTUQV.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">實際的裝置照片(連接喇叭)</div>
</br>
## 控制蜂鳴器或喇叭發出指定頻率的聲音
### 方法1:由頻率算出蜂鳴器發聲、不發聲的持續時間
```c=
/* 實驗3-1:指定無源蜂鳴器發聲頻率 *
* 不使用tone *
* 日期:Sep. 8, 2016 *
* 作者:王一哲 */
int buzzerPin = 3;
int frequency = 262; // 31 Hz ~ 4978 Hz
float dt = 1./(float(frequency)*2.)*1000000.; //由頻率換算延遲時間,單位為microsecond
void setup() {
pinMode(buzzerPin, OUTPUT);
Serial.begin(9600);
Serial.println(dt);
}
void loop() {
digitalWrite(buzzerPin, HIGH);
delayMicroseconds(int(dt));
digitalWrite(buzzerPin, LOW);
delayMicroseconds(int(dt));
}
```
</br>
這是最直接但不方便的方法,由於聲音頻率較高,延遲時間極短,必須以μs(百萬分之1秒)為單位,算式如下
```c
float dt = 1./(float(frequency)*2.)*1000000.;
```
延遲時間則寫為
```c
delayMicroseconds(int(dt));
```
</br>
### 方法2:使用內建函數tone
```c=
/* 實驗3-2:指定無源蜂鳴器發聲頻率 *
* 使用tone *
* 日期:Sep. 8, 2016 *
* 作者:王一哲 */
int buzzerPin = 3;
void setup() {
pinMode(buzzerPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
tone(buzzerPin, 262); //31Hz ~ 4978Hz
}
```
</br>
Arduino有內建的聲音函數tone,但要將蜂鳴器的正極接腳接在支援PWM的腳位3或11,詳細的說明請參考Arduino的說明文件。寫法如下
```c
tone(pin, 頻率) 或 tone(pin, 頻率, 持續時間)
```
</br>
## 利用光敏電阻控制蜂鳴器
這裡所使用的光敏電阻是最常見、最便宜的規格,當光敏電阻照到光時電阻值減少,沒照到光時則電阻值增加,範圍約為2kΩ ~ 980kΩ。若將光敏電阻串聯1個10kΩ的固定電阻,兩端接上+5V的電壓,就是一個簡單的分壓電路。由於我將+5V先接到光敏電阻再接到固定電阻,當光敏電阻照到光時電阻值減少,固定電阻分到的電壓增加;反之則降低。我們可以先寫一個最簡單的程式來測試一下。
```c=
/* 3-5:利用光敏電阻的分壓電路 *
* 日期:Sep. 8, 2016 *
* 作者:王一哲 */
int potPin = 0; //定義讀取電壓值的接腳
int val = 0 ; //定義讀取數值暫存用的變數
//光敏電阻範圍約2kOhm ~ 980kOhm
//串聯10kOhm固定電阻
void setup() {
pinMode(potPin, INPUT); //定義讀取電壓值的接腳為輸入
Serial.begin(9600);
}
void loop() {
val = analogRead(potPin); //val = 0 ~ 1023
Serial.println(val);
}
```
</br>
當我們確定光敏電阻及蜂鳴器都能正常運作後,就能將兩個部分結合起來,利用光敏電阻照光的程度控制蜂鳴器的發聲頻率。
```c=
/* 實驗3-6:光敏電阻控制無源蜂鳴器發聲頻率 *
* 日期:Sep. 8, 2016 *
* 作者:王一哲 */
int buzzerPin = 3; //定義連接蜂鳴器的接腳
int potPin = 0; //定義讀取電壓值的接腳
int val = 0 ; //定義讀取數值暫存用的變數
int frequency; //發聲頻率,31Hz ~ 4978Hz
//光敏電阻範圍約2kOhm ~ 980kOhm
//串聯10kOhm固定電阻
void setup() {
pinMode(buzzerPin, OUTPUT);
pinMode(potPin, INPUT); //定義讀取電壓值的接腳為輸入
Serial.begin(9600);
}
void loop() {
val = analogRead(potPin);
frequency = map(val, 200, 1000, 31, 4978);
tone(buzzerPin, frequency, 200);
Serial.print("val = ");
Serial.print(val);
Serial.print(" frequency = ");
Serial.println(frequency);
}
```
</br>
## pitches.h及演奏曲子
由於每個音高會對應到特定的頻率,但這些頻率並不好記,因此就有熱心人士將兩者對應的關係寫成 **pitches.h** ,我們可以從Arduino官網上找到(https://www.arduino.cc/en/Tutorial/ToneMelody)。開啟文字編輯器,將該頁面的內容複製、貼上,存成 pitches.h ,再建立一個名為 pitches 的新資料夾,將 pitches.h 移到資料夾內,最後將資料夾移到Arduino開發程式的資料夾內即可。可能的路徑為
1. Windows環境 **C:\\Program Files (x86)\\Arduino\\libraries\\**
2. Linux環境 **home/<user name>/Arduino/libraries 或 home/<user name>/sketchbook/libraries**
接下來在程式碼的開頭只要加上 ```#include "pitches.h"``` 即可引用。之前我為了某次研習寫了一個小程式,當盒蓋打開時讓蜂鳴器或喇叭演奏一小段曲子,當時就是利用了 pitches.h,有興趣的同學可以參考。
```c=
/* 實驗3-4(示範用):用無源蜂鳴器演奏曲子 *
* 使用pitches.h *
* 日期:Sep. 8, 2016 *
* 作者:王一哲 */
#include "pitches.h"
#define NOTE_ 0
int buzzerPin = 3;
//設定曲子
int tempo = 30; //每分鐘幾拍
float dt = 1000.0*60.0/float(tempo); //換算1拍的時間長度單位為ms
//The prelude of Final Fantasy 3
int melody[]={NOTE_AS3, NOTE_C3, NOTE_D3, NOTE_F3, NOTE_AS3, NOTE_C4, NOTE_D4, NOTE_F4,
NOTE_AS4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_AS5, NOTE_C6, NOTE_D6, NOTE_F6,
NOTE_AS6, NOTE_F6, NOTE_D6, NOTE_C6, NOTE_AS5, NOTE_F5, NOTE_D5, NOTE_C5,
NOTE_AS4, NOTE_F4, NOTE_D4, NOTE_C4, NOTE_AS3, NOTE_F3, NOTE_D3, NOTE_C3,
NOTE_F3, NOTE_A3, NOTE_AS3, NOTE_D3, NOTE_F3, NOTE_A3, NOTE_AS3, NOTE_D4,
NOTE_G4, NOTE_A4, NOTE_AS4, NOTE_D5, NOTE_G5, NOTE_A5, NOTE_AS5, NOTE_D6};
int noteDurations[] = {16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16};
// 計算陣列長度,sizeof 是回傳陣列所佔的 bytes 而非長度,要算出真正的長度要再除以 sizeof(DataType)
int arrayLen = sizeof(melody)/sizeof(int);
// 設定函數,讀陣列 melody 及 noteDrations 並輸入到 tone
void play(int *melody, int *noteDurations) {
for(int i=0; i < arrayLen; i++) {
int noteDuration = int(dt/float(noteDurations[i]));
Serial.print(melody[i]);
Serial.println(noteDurations[i]);
tone(buzzerPin, melody[i], noteDuration);
int pauseBetweenNotes = int(noteDuration * 1.30);
delay(pauseBetweenNotes);
noTone(buzzerPin);
}
}
void setup() {
pinMode(buzzerPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
play(melody, noteDurations);
noTone(buzzerPin);
delay(2000);
}
```
</br>
---
###### tags:`Arduino`