# Arduino 教學 10:紅外線發射接收器
> 作者:王一哲
> Ver. 1:2016/11/20
> Ver. 2:2018/3/29
## 所需元件
1. 紅外線發射 LED 2個
2. 紅外線接收 LED 2個
3. 3mm或5mm LED 1個
4. 220Ω電阻1個(串聯普通LED)
5. 560Ω電阻2個(串聯紅外線發射LED)
6. 1MΩ電阻2個(串聯紅外線接收LED)
7. 麵包板1塊
8. Arduino Uno開發板1塊
9. 麵包板連接線數條
10. 自製的LED支架2組
</br>
<img height="40%" width="40%" src="https://i.imgur.com/QRSY6ok.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">圖中藍色的是紅外線發射 LED、透明的是紅外線接收 LED。</div>
<div style="text-align:center;color:red;font-weight:bold">請注意,各廠牌的發射器及接收器顏色不一定相同,購買前最好先向店家確認。</div>
</br>
<img height="60%" width="60%" src="https://i.imgur.com/s0rylwr.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">自製的LED支架</div>
</br>
## 使用類比輸入測試
我們所用的紅外線 LED 發射及接收器需要對準才能讀到較強的訊號,因此下圖只是示意圖,請參考以下的接法但另外用較長的線將發射、接收器拉出來。我自己測試的結果,當發射、接收器對準時且距離5cm,讀數可達1000,若中間有障礙物時約為300。
<img height="100%" width="100%" src="https://i.imgur.com/OY19GP3.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/Fiq5W1B.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">實際的裝置照片</div>
</br>
```c=
/* 實驗7-1: 紅外線發射、接收器 *
* 用類比輸入讀取,檢測讀數範圍 *
* 對準時距離5cm讀數可達1000,隔絕時約300 *
* Ver. 1: Sep. 16, 2016 *
* Ver. 2: Mar. 20, 2018 *
* 作者: 王一哲 */
#define SENSOR A0
#define BAUDRATE 9600
void setup() {
pinMode(SENSOR, INPUT);
Serial.begin(BAUDRATE);
}
void loop(){
int state = analogRead(SENSOR);
Serial.println(state);
delay(1000);
}
```
</br>
## 使用數位輸入測試
由於我想要把紅外線發射、接收器當成光電閘使用,只要能讀出兩者間是否有東西經過即可,因此需要用數位輸入測試看看。除了紅外線發射、接收器,另外再接上一顆普通的 LED ,當光電閘之間沒有障礙物時點亮 LED,有障礙物時熄滅 LED。
<img height="100%" width="100%" src="https://i.imgur.com/beFhCzi.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/FtgDUGm.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<img height="100%" width="100%" src="https://i.imgur.com/3UWSSVr.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">實際的裝置照片</div>
</br>
```c=
/* 實驗7-2: 紅外線發射、接收器 *
* 用數位輸入讀取,檢測讀數是否正常 *
* Ver. 1: Sep. 16, 2016 *
* Ver. 2:Mar. 20, 2018 *
* 作者: 王一哲 */
#define LED 13
#define SENSOR 2
#define BAUDRATE 9600
void setup() {
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT);
Serial.begin(BAUDRATE);
}
void loop(){
int state = digitalRead(SENSOR);
Serial.println(state);
if(state == HIGH) {
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
delay(100);
}
```
</br>
## 迴圈
迴圈(loop)是用來重複執行某段程式碼的工具,在C、C++、Arduino中的迴圈有while、do while、for等三種,Arduino網站對於這三種迴圈的說明如下:
1. while: https://www.arduino.cc/en/Reference/While
2. do while: https://www.arduino.cc/en/Reference/DoWhile
3. for: https://www.arduino.cc/en/Reference/For
### while 的語法
```c
while (條件) {
迴圈內容......;
通常會有遞增或遞減條件;
}
```
當條件成立時會執行迴圈內容,當條件不成立時結束迴圈。另一個結束迴圈的狀況則是在迴圈中遇到 break,一旦遇到 break 會立即跳出迴圈,位在迴圈內但在 break 之後的程式碼不會被執行。
</br>
### do while 的語法
```c
do {
迴圈內容......;
通常會有遞增或遞減條件;
} while (條件);
```
用法與 while 迴圈幾乎一樣,差別在於 do while 迴圈會先執行一次迴圈內容,執行完後再檢查條件是否成立,當條件成立時會再次執行迴圈內容,當條件不成立時結束迴圈。一樣可以用 break 跳出迴圈。
</br>
### for 的語法
```c
for (起始狀態; 條件; 遞增或遞減) {
迴圈內容......;
}
```
for 迴圈通常會用在已知執行次數的狀況,例如 1+2+3+...+10 = ? 這類的計算,這會寫成
```c
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
```
一開始設定了變數 i 並將其值指定為1,接著檢查 i 是否小於、等於10,若條件成立則執行迴圈內容,執行完後將 i 的值加1;若條件不成立則結束迴圈。在這個例子當中有一些不太符合數學運算規則的寫法
```c
i++ i += 1 i = i + 1
```
這三種寫法的功能相同,將 i 的值加1後再存到 i 當中,若 i 原為1,運算後變為2。
```c
sum += i sum = sum + i
```
這兩種寫法的功能相同,將 sum 的值加上 i 的值後再存到 sum 當中,若 sum 原為45、i 原為 10,運算後 sum 變為55。for 迴圈一樣可以用 break 跳出迴圈。
</br>
<img height="100%" width="100%" src="https://i.imgur.com/xO8AfyC.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">三種迴圈的流程圖</div>
</br>
## 使用1組光電閘設次計時
線路圖與裝置請參考**使用數位輸入測試**,接法完全相同。程式碼如下:
```c=
/* 實驗7-3: 紅外線發射、接收器 *
* 用數位輸入讀取,設次計時 *
* Ver. 1: Sep. 16, 2016 *
* Ver. 2: Mar. 20, 2018 *
* 作者: 王一哲 */
#define LED 13 //設定LED接腳
#define SENSOR 2 //設定IR LED接收器接腳
#define BAUDRATE 9600 //設定鮑率
const int times = 21; //設定計時次數
int lastState, currentState;
bool blocked = false;
//更新接收器狀態函式
bool updateSensor(int pin){
currentState = digitalRead(pin);
if (currentState == LOW && lastState == HIGH) {
lastState = currentState;
blocked = true;
} else if (currentState == HIGH && lastState == LOW) {
lastState = currentState;
blocked = false;
} else {
blocked = false;
}
return blocked;
}
//使LED閃爍1下
void blinkLed(int pin) {
digitalWrite(pin, HIGH);
delay(50);
digitalWrite(pin, LOW);
delay(50);
digitalWrite(pin, HIGH);
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT);
digitalWrite(LED, HIGH);
Serial.begin(BAUDRATE);
}
void loop(){
int counts = 0;
unsigned long t1, t2;
lastState = digitalRead(SENSOR);
//直到接收器第1次被擋住時記錄時間t1、次數+1、使LED閃爍1次
while(1){
if(updateSensor(SENSOR) == true) {
t1 = millis();
counts += 1;
blinkLed(LED);
Serial.print("counts: ");
Serial.println(counts);
Serial.print("t1: ");
Serial.println(t1);
break;
}
}
//當接收器被擋住時記錄時間t2、次數+1、使LED閃爍1次,到達設定的次數時停止
while(counts < times){
if(updateSensor(SENSOR) == true) {
t2 = millis();
counts += 1;
blinkLed(LED);
Serial.print("counts: ");
Serial.println(counts);
Serial.print("t2: ");
Serial.println(t2);
}
}
//印出測量結果
unsigned long dt = t2 - t1;
Serial.print("t1 = ");
Serial.println(t1);
Serial.print("t2 = ");
Serial.println(t2);
Serial.print("dt = ");
Serial.println(dt);
}
```
</br>
程式的流程大致可分為
1. 設定接腳編號、鮑率、計時次數、全域變數
2. 設定自定函式updateSensor、blinkLed
3. 在 void setup() 中設定接腳模式、開啟連接埠
4. 在 void loop()
a. 設定區域變數counts、t1、t2,讀取sensorPin的狀態
b. 在第1個 while 迴圈中,直到接收器第1次被擋住時記錄時間t1、次數+1、使LED閃爍1次
c. 在第2個 while 迴圈中,當接收器被擋住時記錄時間t2、次數+1、使LED閃爍1次,到達設定的次數時停止
d. 印出測量結果
</br>
在這個程式當中應用了一個技巧,利用 while 迴圈及 if 使程式停在某個部分,直到指定的條件發生為止。
```c
while(1) {
if(updateSensor(SENSOR) == true) {
t1 = millis();
counts += 1;
blinkLed(LED);
Serial.print("counts: ");
Serial.println(counts);
Serial.print("t1: ");
Serial.println(t1);
break;
}
}
```
</br>
while(1) 其實是個無窮迴圈,因為條件設定為常數1,條件一定成立,迴圈內的程式碼會不斷地被重複執行,直到 **if(updateSensor(SENSOR) == true) 成立為止**,因為在 if 當中最後一行會碰到 break 跳出迴圈。
第2個 while 迴圈只是單純地計算次數,直到光電閘被擋住的次數到達設定次數為止,其中 Serial.println 的部分會將次數及時間印出來,方便除錯。當第2個 while 迴圈跑完後會印出 t1、t2 及時間差 dt ,如果光電閘沒有再被擋住,程式就會又停在第1個 while 迴圈。
</br>
## 使用2組光電閘測量物體通過所用的時間
線路圖與裝置如下,只是發射、接收器增加為2組。
<img height="100%" width="100%" src="https://i.imgur.com/drINdj2.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/dzibVv2.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<img height="100%" width="100%" src="https://i.imgur.com/XCgdpcC.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">實際的裝置照片</div>
</br>
程式碼如下:
```c=
/* 實驗7-4: 紅外線發射、接收器作為光電閘 *
* 用數位輸入讀取,測量通量2組光電閘的時差 *
* Ver. 1: Sep. 16, 2016 *
* Ver. 2: Mar. 20, 2018 *
* 作者: 王一哲 */
#define LED 13 //設定LED接腳
#define START 2 //設定IR LED接收器1接腳
#define STOP 3 //設定IR LED接收器2接腳
#define BAUDRATE 9600 //設定鮑率
int lastState, currentState;
bool blocked = false;
//更新接收器狀態函式
bool updateSensor(int pin) {
currentState = digitalRead(pin);
if (currentState == LOW && lastState == HIGH) {
lastState = currentState;
blocked = true;
} else if (currentState == HIGH && lastState == LOW) {
lastState = currentState;
blocked = false;
} else {
blocked = false;
}
return blocked;
}
//使LED閃爍1下
void blinkLed(int pin) {
digitalWrite(pin, HIGH);
delay(50);
digitalWrite(pin, LOW);
delay(50);
digitalWrite(pin, HIGH);
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(START, INPUT);
pinMode(STOP, INPUT);
digitalWrite(LED, HIGH);
Serial.begin(BAUDRATE);
}
void loop(){
unsigned long t1, t2;
lastState = digitalRead(START);
while(1) {
if(updateSensor(START) == true) {
t1 = millis();
blinkLed(LED);
break;
}
}
lastState = digitalRead(STOP);
while(1) {
if(updateSensor(STOP) == true) {
t2 = millis();
blinkLed(LED);
break;
}
}
unsigned long dt = t2 - t1;
Serial.print("t1 = ");
Serial.println(t1);
Serial.print("t2 = ");
Serial.println(t2);
Serial.print("dt = ");
Serial.println(dt);
}
```
</br>
程式的流程大致可分為
1. 設定接腳編號、鮑率、全域變數
2. 設定自定函式updateSensor、blinkLed
3. 在 void setup() 中設定接腳模式、開啟連接埠
4. 在 void loop()
a. 設定區域變數t1、t2
b. 讀取啟動組接腳狀態
c. 在第1個 while 迴圈中,直到啟動組第1次被擋住時記錄時間t1、使LED閃爍1次
d. 讀取停止組接腳的狀態
e. 在第2個 while 迴圈中,直到停止組第1次被擋住時記錄時間t2、使LED閃爍1次
f. 印出測量結果
</br>
---
###### tags:`Arduino`