---
# System prepended metadata

title: 2026陽明交大電機營Arduino

---

# 2026 陽明交大電機營 Arduino組
### 底下Exercise與Lab的程式碼都會被挖空，請根據題目要求將程式碼完成
---
# Exercise 0 前置作業

## CH340與Arduino IDE
https://drive.google.com/drive/folders/1xobgSl3XplSYGP4RiAqiI1rOKVg4xDGa?usp=drive_link

## 工具箱
1. Arduino UNO版(含傳輸線)*1
2. 麵包版*1
3. 公對公杜邦線*18
4. 公對母杜邦線*5
6. 色環電阻*4
7. LED*4
8. 可變電阻*1
9. 按鈕*1
10. 超音波感測器*1
11. 伺服馬達*1
12. 無源蜂鳴器*1
13. 搖桿*1
---

# Exercise 1 利用可變電阻調整LED亮度

---

![螢幕擷取畫面 2026-01-08 154921](https://hackmd.io/_uploads/BJ5uVx8aWe.png)


# Exercise 2 上數計數器

---

![螢幕擷取畫面 2026-01-14 194002](https://hackmd.io/_uploads/Sky04gLp-x.png)





## 程式碼

```arduino=
int pushbutton = 7;   //設定按鈕的接腳
int buttonState = 1;  //代表按鈕現在的狀態
int beforeState = 1; //代表按鈕的前一個狀態
int times = 0;        //設定一個用於計次的整數
void setup() {
    Serial.begin(9600); //開始與電腦連接
    ___1___ //設定按鈕並且給予上拉電阻
}
void loop() {
    buttonState = digitalRead(pushbutton); //讀入此時的按鈕狀態
    if(buttonState == 0 && beforeState == 1) { //當按鈕被按下時執行
        ___2___  //讓程式暫停100毫秒後再繼續讀取
        times++; //計次數加1
        ___3___ //在視窗列印出計次數並換行
    }
    beforeState = buttonState; //將此時的按鈕狀態存到前一刻的按鈕狀態
}
```

## Problem 1 設定按鈕給予上拉電阻

```arduino=5
void setup() {
  Serial.begin(9600); //開始與電腦連接
  ___1___ //設定按鈕並且給予上拉電阻
}
```

<aside>

:::success
Problem 1 :
選項 >> 只有一個是對的喔!

</aside>

> A )  `pinmode(pushbutton,INPUT);`
> 
> 
> B )  `pinmode(pushbutton,INPUT_PULLUP);` 
> 
> C )  `pinMode(pushbutton,Input_Pullup);` 
> 
> D )  `pinMode(pushbutton,INPUT_PULLUP);` 
> 
## Problem 2 Debounce

```arduino=11
if(buttonState == 0 && beforeState == 1) { //當按鈕被按下時執行
    ___2___  //讓程式暫停100毫秒後再繼續讀取
    times++; //計次數加1
    ___3___  //在視窗列印出計次數並換行
}
```

<aside>

:::success
Problem 2 :
選項 >> 只有一個是對的喔!

</aside>

> A )  `delay(100);`
> 
> 
> B )  `Delay(100);` 
> 
> C )  `Debounce(times);`
> 
> D )  `Debounce(buttonState);` 
> 
## Problem 3 在序列埠監控視窗列印出計次數並換行

```arduino=11
if(buttonState == 0 && beforeState == 1){ //當按鈕被按下時執行
    ___2___  //讓程式暫停100毫秒後再繼續讀取
    times++; //計次數加1
    ___3___  //在視窗列印出計次數並換行
}
```

<aside>
    
:::success
Problem 3 :
選項 >> 只有一個是對的喔!

</aside>

> A )  `Serial.print(times);`
> 
> 
> B )  `serial.print(times);` 
> 
> C )  `Serial.println("times");`
> 
> D )  `Serial.println(times);` 
> 
# Exercise 3 超音波感測器
![image](https://hackmd.io/_uploads/rk0x9DmJlg.png)

---
### 超音波感測器請兩個圈圈朝外方便感測(腳位順序如示意圖)。
![image](https://hackmd.io/_uploads/rysTGb86Ze.png)






## 程式碼

```arduino=
int trig_pin=13;//觸發接口接13腳位
int echo_pin=12;//回聲接口接12腳位

void setup() 
{
  Serial.begin(9600);//初始化鮑率
  __1.1__
  __1.2__
}

void loop() 
{
  digitalWrite(trig_pin,__2.1__);//輸入10微秒的啟動脈衝
  delayMicroseconds(10);
  digitalWrite(trig_pin,__2.2__);

  long duration=pulseIn(echo_pin,HIGH);//引用函式計算時間
  long distant=__3__;//利用速度、時間計算距離

  Serial.print("Distant : ");//輸出結果
  Serial.print(distant);
  Serial.println(" cm");

  delay(2000);//延遲2000毫秒
}
```

## Problem 1 設定腳位的模式

```arduino=4
void setup() 
{
  Serial.begin(9600);//初始化鮑率
  __1.1__
  __1.2__
}
```

<aside>
    
:::success
Problem 1 :
選項 >> 只有一個是對的喔!
</aside>

```
__1.1__ / __1.2__
```

> A )  `pinMode(trig_pin,OUTPUT);` / `pinMode(echo_pin,OUTPUT);`
> 
> B )  `pinMode(trig_pin,INPUT);`  / `pinMode(echo_pin,INPUT);`
> 
> C )  `pinMode(trig_pin,OUTPUT);` / `pinMode(echo_pin,INPUT);`
> 
> D )  `pinMode(trig_pin,INPUT);`  / `pinMode(echo_pin,OUTPUT);`
> 

## Problem 2 設定10微秒啟動脈衝波

```arduino=13
  digitalWrite(trig_pin,__2.1__);//調成高電位
  delayMicroseconds(10);         //延遲10微秒
  digitalWrite(trig_pin,__2.2__);//調成低電位
```

<aside>

:::success
Problem 2 :
選項 >> 只有一個是對的喔!

</aside>

```
__2.1__ / __2.2__
```


> A )  `HIGH` / `LOW`
> 
> 
> B )  `LOW`  / `HIGH`
> 
> C )  `INPUT` / `OUTPUT`
> 
> D )  `OUTPUT` / `INPUT`
> 

## Problem 3 距離計算公式

```arduino=17
long duration=pulseIn(echo_pin,HIGH);//引用函式計算時間
long distant=__3__;//利用速度、時間計算距離
```

<aside>

:::success
Problem 3 :
選項 >> 只有一個是對的喔!

</aside>



> A )  `0.0346*duration/2`
> 
> 
> B )  `0.0346*duration`
> 
> C )  `346*duration/2`
> 
> D )  `duration * 346`
> 

# Exercise 4 伺服馬達

---
![image](https://hackmd.io/_uploads/SkxLubU6-x.png)






## 程式碼

```arduino=
#include <Servo.h>
Servo my_servo;//宣告物件
int spin=10;//一次轉多少角度(請不要自己調整參數)
int angle=0;//初始角度

void setup()
{
  __1__//signal接9腳位
}

void loop()
{
  my_servo.write(angle);
  angle=angle + spin;

  if(__2__)//如果超過轉動範圍就換方向
  {
    spin=spin*(-1);//換方向
  }
  delay(500);//請不要自己調整參數
}
```

## Problem 1 設定伺服馬達的腳位

```arduino=6
void setup()
{
  __1__//signal接9腳位
}
```

<aside>

:::success
Problem 1 :
選項 >> 只有一個是對的喔!

</aside>

> A )  `attach(9);`
> 
> 
> B )  `my_servo.write(9);`
> 
> C )  `my_servo.attach(9);`
> 
> D )  `pinMode(9,INPUT);` 
> 

## Problem 2 判斷旋轉角度是否到邊界

```arduino=16
if(__2__)//如果超過轉動範圍就換方向
{
  spin=spin*(-1);//換方向
}
```

<aside>

:::success
Problem 2 :
選項 >> 只有一個是對的喔!

</aside>

> A )  `angle<=0`
> 
> 
> B )  `angle>=180`
> 
> C )  `angle<=0 && angle>=180`
> 
> D )  `angle<=0 || angle>=180`
> 

# Exercise 5 無源蜂鳴器

---




![螢幕擷取畫面 2026-04-26 015742](https://hackmd.io/_uploads/HJ-MgFq6-e.png)

---
:::warning
注意超音波感測器方向，要面向外側
:::
![螢幕擷取畫面 2026-04-23 144733](https://hackmd.io/_uploads/rJ1kHHPTWe.png =45%x)![螢幕擷取畫面 2026-04-23 150608](https://hackmd.io/_uploads/rkmeSBvpZl.png =55%x)





## 程式碼

```arduino=
//宣告腳位
int TRIG = 12;  // 超音波模組的Trig腳位 
int ECHO = 11;  // 超音波模組的Echo腳位 
int BUZZER = 9; // 無源蜂鳴器腳位 
double distance();

void setup() {
  pinMode(TRIG, __1.1__);
  pinMode(ECHO, __1.2__);
  pinMode(BUZZER, __1.3__);
  Serial.begin(9600); // 開啟序列埠監視器(檢查程式用)
}

void loop() {
  double d = distance();
  
  if (d > 20) { // 大於 20cm，不發出聲音
    noTone(BUZZER);
  } else if (d > 10) { // 10-20 cm，1 秒 1000Hz，1 秒靜音
    tone(BUZZER, __2.1__);
    delay(1000);
    __2.2__(BUZZER);
    delay(1000);
  } else { // 0-10 cm，0.5 秒 2000Hz，0.5 秒靜音
    tone(BUZZER, __2.3__);
    delay(500);
    __2.2__(BUZZER);
    delay(500);
  }
}

double distance() {
  digitalWrite(TRIG, HIGH); //產生高電位脈衝
  delayMicroseconds(10);    //持續 10 μs
  digitalWrite(TRIG, LOW);
  long duration = pulseIn(ECHO, HIGH); //時間Δt
  return duration * 0.0346 / 2;        //距離公式
}
```

## Problem 1 設定蜂鳴器和超音波腳位

```arduino=6
void setup() { 
    pinMode(TRIG,__1.1__); //將Trig腳位訂為輸出
    pinMode(ECHO,__1.2__); //將Echo腳位訂為輸入
    pinMode(BUZZER,__1.3__); // 把蜂鳴器設為輸出
    Serial.begin(9600); // 開啟序列埠監視器 
}
```
:::success
Problem １ :
選項 >> 只有一個是對的喔!
:::
```
__1.1__ / __1.2__ / ___1.3___
```

> A )  `INPUT` / `OUTPUT` / `INPUT`
> 
> 
> B )  `OUTPUT` / `INPUT` / `OUTPUT`
> 
> C )  `INPUT` / `OUTPUT` / `OUTPUT`
> 
> D )  `OUTPUT` / `INPUT` / `INPUT` 
> 

## Problem 2 讓蜂鳴器發出特定聲音

```arduino=13
void loop() { 
    double d = distance();
    if (d > 20) { // 大於 20cm，不發出聲音
        noTone(BUZZER); 
    } else if (d > 10) { // 10-20 cm，1 秒 1000Hz，1 秒靜音
        tone(BUZZER, __2.1__); 
        delay(1000); 
        __2.2__(BUZZER); 
        delay(1000); 
    } else { // 0-10 cm，0.5 秒 2000Hz，0.5 秒靜音
        tone(BUZZER, __2.3__); 
        delay(500); 
        __2.2__(BUZZER); 
        delay(500); 
    }
}
```
:::success
Problem 2 :
選項 >> 只有一個是對的喔!
:::
```
__2.1__ / __2.2__ / __2.3__
```


> A )  `1000` / `tone` / `2000`
> 
> 
> B )  `2000` / `noTone` / `1000`
> 
> C )  `1000` / `noTone` / `2000`
> 
> D )  `2000` / `tone` / `500`
> 

# Exercise 6 搖桿

---



![螢幕擷取畫面 2026-04-23 060831](https://hackmd.io/_uploads/SJStFtD6Wx.png)
![螢幕擷取畫面 2026-04-23 200315](https://hackmd.io/_uploads/Bky0KFvpbe.png)
![螢幕擷取畫面 2026-04-23 144544](https://hackmd.io/_uploads/B1RCtYwTbe.png =70%x)






## 程式碼

```arduino=
#include <Servo.h>

// 定義腳位
int Xpin = A1;
int Ypin = A2;     // 目前沒用到，但保留
int swPin = A3;    // 搖桿按鈕
int buzzer = 11;
int servoPin = 3;

int servoPos = 0;
int Xvalue;

Servo myServo;

void setup() {
  Serial.begin(9600);
  myServo.attach(servoPin);
  
  pinMode(Xpin, INPUT);
  pinMode(Ypin, INPUT);
  pinMode(swPin, INPUT_PULLUP); // 使用內建上拉電阻
  
  pinMode(buzzer, OUTPUT);
}

void loop() {
  // 1. 讀取搖桿x方向數值（0~1023）
  Xvalue = __1.1__;
  // 2. 使用 map 函式將 X 軸 (0-1023) 轉換為馬達角度 (0-180)
  servoPos = __2.1__;
  // 3. 控制馬達轉動
  myServo.write(servoPos);
  // 4. 判斷邊界值發出高頻聲音 (大於 1000 或 小於 20)
  if (Xvalue > 1000 || Xvalue < 20) {
    tone(buzzer, 2000); // 發出 2000Hz 的高音
  } else {
    noTone(buzzer);     // 正常範圍內停止發聲
  }

  /*5. 序列埠輸出（方便除錯）
  Serial.print("X-axis: ");
  Serial.print(Xvalue);
  Serial.print(" | Servo Angle: ");
  Serial.println(servoPos);*/

  delay(15); // 給予馬達反應時間
}
```

## Problem 1 讀取搖桿數值

```arduino=27
  // 1. 讀取搖桿x方向數值（0~1023）
  Xvalue = __1.1__;
```

:::success
Problem １ :
選項 >> 只有一個是對的喔!
:::
```
__1.1__
```


> A )  `analogRead(swPin)`
> 
> B )  `analogRead(Xvalue)`
> 
> C )  `analogRead(Ypin)` 
> 
> D )  `analogRead(Xpin)` 
> 

## Problem 2 搖桿數值轉換

```arduino=29
 // 2. 使用 map 函式將 X 軸 (0-1023) 轉換為馬達角度 (0-180)
  servoPos = __2.1__;
```

:::success
Problem 2 :
選項 >> 只有一個是對的喔!
:::
```
__2.1__
```

> A )  `map(XValue, 0, 180, 0, 1023)` 
> 
> 
> B )  `Map(XValue, 0, 180, 0, 1023)` 
> 
> C )  `map(XValue, 0, 1023, 0, 180)` 
> 
> D )  `Map(XValue, 0, 1023, 0, 180)` 
> 

---

# Lab1 自動門

---

底下 Lab 的程式碼都會被挖空，請根據題目要求與註解提示，將正確的程式碼填入 `___` 中完成任務！

## Lab: 智慧自動門
### 範例接線圖
![螢幕擷取畫面 2026-04-22 175941](https://hackmd.io/_uploads/rJl42fUTbe.png)


### 📝 任務說明
1. 利用超音波感測器讀取距離。
2. 距離小於 15 公分時，伺服馬達轉動至 90 度開門。
3. 開門狀態下，**紅燈閃爍**且**蜂鳴器發出警報**，同時系統不能卡住（需使用 millis）。
4. 人離開後，馬達歸零關門，綠燈恆亮。

### 💻 程式碼填空
```cpp=
#include <Servo.h>

// 硬體腳位定義
const int trigPin = 12;
const int echoPin = 11;
const int buzzerPin = 8;
const int redPin = 7;
const int greenPin = 6;
const int servoPin = 3;

// 變數與物件
/* 填空 1: 宣告一個伺服馬達的物件，命名為 myGate */
__1__ myGate;
float pingTime;
float distance;
int servoPos = 0;
bool isGateOpen = false;

// Millis 計時變數
unsigned long currentMillis = 0;
unsigned long previousBlinkTime = 0;
unsigned long previousBeepTime = 0;
int ledState = LOW;
int buzzerState = LOW;

void setup() {
  Serial.begin(9600);
  
  /* 填空 2: 設定 trigPin 為輸出模式 */
  pinMode(trigPin, __2__);
  pinMode(echoPin, INPUT);
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(buzzerPin, OUTPUT);
  
  /* 填空 3: 將馬達物件連接到我們定義的 servoPin 腳位 */
  myGate.__3__(servoPin);
  myGate.write(0); // 初始狀態:門關閉
}

void loop() {
  /* 填空 4: 取得 Arduino 開機運作到現在的毫秒數 */
  currentMillis = __4__(); 
  
  // 1. 觸發超音波發射
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  // 接收回音並計算距離 (公分)
  /* 填空 5: 讀取 echoPin 高電位持續的時間 */
  pingTime = __5__(echoPin, HIGH);
  distance = (pingTime * 0.0343) / 2;

  // 2. 邏輯判斷
  if (distance < 15 && distance > 0) {
    
    // 【有人靠近：開門模式】
    /* 填空 6: 判斷「如果門還沒有開」 (利用 ! 符號做反相) */
    if (__6__) {
      /* 填空 7: 命令伺服馬達轉動到 90 度的位置 */
      myGate.__7__(90); 
      isGateOpen = true;
      digitalWrite(greenPin, LOW); // 綠燈滅
    }
    
    // 非阻塞 LED 閃爍 (間隔 200ms)
    /* 填空 8: 判斷目前時間與上次閃爍時間的差，是否達到 200 毫秒 */
    if (currentMillis - previousBlinkTime >= __8__) {
      previousBlinkTime = currentMillis;
      if (ledState == LOW) ledState = HIGH; else ledState = LOW;
      digitalWrite(redPin, ledState);
    }
    //設定聲音變化頻率
    int alarmPitch = map(distance, 1, 15, 2000, 500);
    // 使用 constrain 確保頻率不會因為稍微超出距離而亂叫
    alarmPitch = constrain(alarmPitch, 500, 2000);
      
    // 非阻塞 蜂鳴器警示 (間隔 100ms)
    if (currentMillis - previousBeepTime >= 100) {
      previousBeepTime = currentMillis;
      if (buzzerState == LOW) {
        buzzerState = HIGH;
        /* 填空 9: 讓蜂鳴器發出 1000Hz 的聲音 */
        __9__(buzzerPin, alarmPitch); 
      } else {
        buzzerState = LOW;
        /* 填空 10: 停止蜂鳴器的聲音 */
        __10__(buzzerPin);      
      }
    }
  } else {
    // 【無人：待機模式】
    if (isGateOpen) {
      myGate.write(0); // 關門
      isGateOpen = false;
      
      // 重置警示狀態
      digitalWrite(redPin, LOW);
      noTone(buzzerPin);
      digitalWrite(greenPin, HIGH); // 綠燈恆亮
    }
  }
  
  delay(50);
}
```

## Problem 1 宣告物件

```arduino=13
__1__ myGate;
float pingTime;
float distance;
int servoPos = 0;
bool isGateOpen = false;
```

:::success
Problem １ :
在宣告伺服馬達物件時，應該使用哪一個關鍵字？（注意大小寫）
:::


> ( A )  `motor`
> 
> ( B )  `Servo` 
> 
> ( C )  `servo`  
> 
> ( D )  `Motor`

## Problem 2 腳位模式設定

```arduino=30
pinMode(trigPin, __2__);
pinMode(echoPin, INPUT);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
```

:::success
Problem 2 :
超音波感測器的 `trigPin` 負責發射超音波，請問在 `setup()` 中應將其腳位模式設為什麼？
:::
> ( A )  `INPUT`
> 
> ( B )  `OUTPUT` 
> 
> ( C )  `INPUT_PULLUP`  
> 
> ( D )  `HIGH`

## Problem 3 連接伺服馬達

```arduino=37
myGate.__3__(servoPin);
 myGate.write(0); //
```

:::success
Problem 3 :
要將伺服馬達物件綁定到特定的 Arduino 腳位上（例如 `servoPin`），應該呼叫哪一個函式？
:::


> ( A )  `pinMode()`
> 
> ( B )  `bind()` 
> 
> ( C )  `connect()`  
> 
> ( D )  `attach()`

## Problem 4 緊急按鈕觸發條件

```arduino=43
currentMillis = __4__();
```

:::success
Problem 4 :
為了解決 `delay()` 會讓程式卡住的問題（非阻塞多工），我們使用哪一個函式來取得 Arduino 開機後經過的毫秒數？
:::
> ( A )  `time()`
> 
> ( B )  `delay()` 
> 
> ( C )  `millis()`  
> 
> ( D )  `micros()`

## Problem 5 超音波測距

```arduino=54
pingTime = __5__(echoPin, HIGH);
distance = (pingTime * 0.0343) / 2;
```


:::success
Problem 5 :
在計算超音波回音時間時，我們使用哪一個函式來測量 `echoPin` 維持高電位（HIGH）的時間長度？
:::

> ( A )  `digitalRead()`
> 
> ( B )  `analogRead()` 
> 
> ( C )  `pulseIn()`  
> 
> ( D )  `delayMicroseconds()`

## Problem 6 偵測開門

```arduino=62
 if (__6__) {
      myGate.__7__(90); 
      isGateOpen = true;
      digitalWrite(greenPin, LOW); // 綠燈滅
    }
```

:::success
Problem 6 :
程式中要判斷「如果門還沒有開」，下列哪一種條件判斷式的寫法是正確的？（已知 `isGateOpen` 為記錄門狀態的布林值變數）
:::


> ( A )  `if (!isGateOpen)`
> 
> ( B )  `if (isGateOpen == true)` 
> 
> ( C )  `if (isGateOpen != false)`  
> 
> ( D )  `if (~isGateOpen)`

## Problem 7 馬達轉動

```arduino=62
 if (__6__) {
      myGate.__7__(90); 
      isGateOpen = true;
      digitalWrite(greenPin, LOW); // 綠燈滅
    }
```

:::success
Problem 7 :
當條件滿足要開啟閘門時，要命令伺服馬達轉動到 90 度的位置，應該呼叫伺服馬達的哪一個函式？
:::
> ( A )  `turn(90)`
> 
> ( B )  `rotate(90)` 
> 
> ( C )  `angle(90)`  
> 
> ( D )  `write(90)`

## Problem 8 判斷時間差

```arduino=78
if (currentMillis - previousBlinkTime >= __8__) {
      previousBlinkTime = currentMillis;
      if (ledState == LOW) ledState = HIGH; else ledState = LOW;
      digitalWrite(redPin, ledState);
    }
```

:::success
Problem 8 :
根據任務要求，紅燈需要以 200 毫秒的間隔閃爍。在判斷時間差的條件式 `if (currentMillis - previousBlinkTime >= __8__)` 中應填入什麼數值？
:::
> ( A )  `100`
> 
> ( B )  `200` 
> 
> ( C )  `1000`  
> 
> ( D )  `50`

## Problem 9 蜂鳴器警報

```arduino=84
if (buzzerState == LOW) {
  buzzerState = HIGH;
  __9__(buzzerPin, alarmPitch); 
```

:::success
Problem 9 :
要讓蜂鳴器發出特定頻率（例如 alarmPitch 變數儲存的頻率）的警報聲，應該使用哪一個 Arduino 內建函式？
:::


> ( A )  `play()`
> 
> ( B )  `sound()` 
> 
> ( C )  `beep()`  
> 
> ( D )  `tone()`


## Problem 10 警報結束

```arduino=91
__10__(buzzerPin);
```
---
:::success
Problem 10 :
當警示結束，或者在閃爍間隔內要讓蜂鳴器停止發聲（靜音），應該使用哪一個函式？
:::


> ( A )  `noTone()`
> 
> ( B )  `stopTone()` 
> 
> ( C )  `mute()`  
> 
> ( D )  `noSound()`

# Lab2 憤怒鳥彈射模擬器

![image](https://hackmd.io/_uploads/ByfxnWU6Zx.png)
### 注意:四顆LED(豬豬)的位置
![image](https://hackmd.io/_uploads/rk5Uh-8pbg.png)




## 程式碼

```arduino=
#include <Servo.h> // 引入伺服馬達函式庫

Servo catapult; // 建立投石機馬達物件

const int joyX = A0;      // 搖桿 X 軸
const int joyY = A1;      // 搖桿 Y 軸
const int extButton = 7;  // 外接按鈕接腳 (補彈/重置)

const int ledPig1 = 3;    // 第 1 隻豬的 LED
const int ledPig2 = 4;    // 第 2 隻豬的 LED
const int ledPig3 = 5;    // 第 3 隻豬的 LED
const int ledPig4 = 6;    // 第 4 隻豬的 LED
const int buzzer = 8;     // 蜂鳴器
const int servoPin = 9;   // 伺服馬達接腳

int ammo = 5; // 初始子彈數量
const int HIT_RADIUS = 100; // 炸彈命中判定範圍
const unsigned long TIME_LIMIT = 120000; // 遊戲限時 120 秒

// --- 使用「平行陣列」來記錄 4 隻豬的資訊 ---
float pigX[4]      = { 150.0, 210.0, 210.0, 270.0 }; // X 座標
float pigY[4]      = {   0.0, 200.0,-200.0,   0.0 }; // Y 座標
bool pigAlive[4]   = {  true,  true,  true,  true }; // 存活狀態
int pigLedPin[4]   = { ledPig1, ledPig2, ledPig3, ledPig4 }; // 對應 LED

bool isAiming = false;   // 是否正在拉搖桿瞄準
int pullSide = 0;        // 搖桿左右拉力
int pullForce = 0;       // 搖桿前後拉力
unsigned long gameStartTime = 0; // 遊戲開始時間
bool gameOver = false;   // 遊戲是否結束

int currentScore = 0;    // 本局分數
int highScore = 0;       // 歷史最高分

void setup() {
  Serial.begin(9600); // 開啟序列埠看文字訊息
  catapult.attach(servoPin); // 設定馬達
  catapult.write(90); // 馬達歸位 (置中)
  
  pinMode(extButton, INPUT_PULLUP); // 外接按鈕啟用內建上拉電阻

  pinMode(ledPig1, OUTPUT); // 設定 4 顆 LED 為輸出
  pinMode(ledPig2, OUTPUT);
  pinMode(ledPig3, OUTPUT);
  pinMode(ledPig4, OUTPUT);
  pinMode(buzzer, OUTPUT);  // 設定蜂鳴器為輸出
  
  resetGame(); // 呼叫重置函式，準備開局
}

void loop() {
  if (digitalRead(extButton) == LOW) { // 按鈕被按下
    if (gameOver) {
      resetGame(); // 遊戲結束就重新開始
    } else {
      ammo = 5; // 遊戲中則補充子彈
      tone(buzzer, 1500, 100); delay(150); tone(buzzer, 2000, 150);
      Serial.println(">>> 彈藥已補充！");
    }
    delay(400); // 防彈跳
  }

  if (gameOver) return; // 遊戲結束就暫停下方動作

  unsigned long elapsedTime = millis() - gameStartTime;
  if (elapsedTime >= TIME_LIMIT || checkAllPigsDead()) { // 時間到或豬全滅
    endGame(elapsedTime);
    return;
  }
  
  int currentSide = analogRead(joyX) - 512; // 讀取搖桿左右並置中
  int currentForce = analogRead(joyY) - 512; // 讀取搖桿前後並置中

  if (currentForce > 100 && ammo > 0) { // 往後拉且有子彈 (瞄準中)
    isAiming = true;
    pullSide = currentSide;
    pullForce = currentForce;
    
    float angleRad = atan2((float)pullForce, (float)pullSide); // 計算角度
    int angleDeg = angleRad * 180.0 / PI;
    catapult.write(constrain(180 - angleDeg, 0, 180)); // 馬達轉向
    
    delay(50);
  } 
  else if (isAiming && currentForce <= 50) { // 放開搖桿 (發射)
    isAiming = false;
    fireProjectile(pullForce, pullSide); // 執行發射
  }
}

void fireProjectile(int pf, int ps) { 
  ammo--; // 扣子彈
  catapult.write(90); // 馬達歸位
  tone(buzzer, 1800, 150); // 發射音效
  
  float landX = (pf * 0.75); // 計算落點 X
  float landY = (ps * 0.75); // 計算落點 Y
  
  bool hitThisTurn = false; 
  for (int i = 0; i < 4; i++) { // 巡視 4 隻豬
    if (pigAlive[i]) { // 如果豬還活著
      // 計算落點與豬的距離
      float dist = sqrt(sq(landX - pigX[i]) + sq(landY - pigY[i])); 
      if (dist <= HIT_RADIUS) { // 如果在爆炸範圍內
        pigAlive[i] = false; // 豬死掉
        digitalWrite(pigLedPin[i], LOW); // 關燈
        currentScore += 25; // 加分
      }
    }
  }
}

bool checkAllPigsDead() { 
  for (int i = 0; i < 4; i++) if (pigAlive[i]) return false; // 還有活著的
  return true; // 全滅
}

void endGame(unsigned long elapsed) { 
  gameOver = true; 
  int finalScore = currentScore; 
  
  if (checkAllPigsDead()) { // 全滅獲勝
    int timeBonus = (120 - (elapsed / 1000)) * 10; // 計算時間獎勵
    finalScore += timeBonus; 
    tone(buzzer, 2000); // 勝利音效
    delay(500);
    noTone(buzzer);
  } else { // 時間到失敗
    tone(buzzer, 300, 1000); // 失敗音效
  }

  if (finalScore > highScore) highScore = finalScore; // 更新最高分
}

void resetGame() { 
  ammo = 5; 
  currentScore = 0; 
  gameOver = false; 
  isAiming = false; 
  
  for (int i = 0; i < 4; i++) { // 把 4 隻豬復活
    pigAlive[i] = true; 
    digitalWrite(pigLedPin[i], HIGH); // 重新開燈
  }
  
  gameStartTime = millis(); // 重新計時
}
```
### Problem 1 設定序列埠，用於輸出訊息

```arduino=34
void setup() {
  __1__(9600); // 設定序列埠，用於輸出訊息
}
```

:::success
Problem 1 :
選項 >> 只有一個是對的喔!
:::


> A )  `Serial.write`
> 
> 
> B )  `Serial.print`
> 
> C )  `Serial.begin`
> 
> D )  `Serial.end`
> 

---
### Problem 2 設定搖桿按鈕為輸入

```arduino=41
pinMode(joySw, __2__ );  // 請根據你拿到的搖桿選擇正確答案。
```

### Hint :
**黑色搖桿需要**給予上拉電阻（否則電位會浮動）。
**紅色搖桿不需**給予上拉電阻，只須設定該腳位為輸入。
:::success
Problem 2 :
請根據你拿到的搖桿選擇正確答案。
:::
**黑色搖桿：**`INPUT_PULLUP` 
**紅色搖桿：**`INPUT`
### Problem 3 當按鈕按下時

```arduino=49
if ( ( __3__ ) && ( __4__ ) ) { //當(按鈕按下時)且(Debounce)
//code
}
```


### Hint :
**黑色搖桿**有被給予上拉電阻，因此當按鈕按下時，使用digitalRead函式會讀取到低電位。
**紅色搖桿**預設連接到GND，因此當按鈕按下時，使用digitalRead函式會讀取到高電位。
:::success
Problem 3 :
選項 >> 請根據你拿到的搖桿選擇正確答案。
:::
**黑色搖桿：**`digitalRead(joySw) == LOW` 
**紅色搖桿：**`digitalRead(joySw) == HIGH`

---
### Problem 4 Debounce

```arduino=49
if ( ( __3__ ) && ( __4__ ) ) { //當(按鈕按下時)且(Debounce)
//code
}
```

:::success
Problem 4 :
選項 >> 只有一個是對的喔!
:::


> A ) `millis() - lastButtonPress < debounceDelay`
> 
> 
> B ) `millis() - lastButtonPress > debounceDelay`
> 
> C ) `lastButtonPress < debounceDelay`
> 
> D ) `lastButtonPress > debounceDelay`
> 

### Problem 5 檢查遊戲是否已達時間限制

```arduino=62
// 檢查遊戲是否已達時間限制
if ( __5__ ) {
//code
}
```

:::success
Problem 5 :
選項 >> 只有一個是對的喔!
:::



> 
> A ) `startTime <= gameDuration`
> 
> B ) `startTime > gameDuration`
> 
> C ) `millis() - startTime < gameDuration`
> 
> D ) `millis() - startTime >= gameDuration`
> 
---

### Problem 6 確保 LED 點亮，且符合冷卻時間

```arduino=91
// 確保 LED 點亮，且符合冷卻時間
if ( __6__ ) {
  //code
}
```
:::success
Problem 6 :
選項 >> 只有一個是對的喔!
:::

> A ) `activeLED == -1 && (millis() - lastLedActionTime) > ledCooldown`
> 
> 
> B ) `activeLED != -1 && (millis() - lastLedActionTime) > ledCooldown`
> 
> C ) `activeLED == -1 && lastLedActionTime > ledCooldown`
> 
> D ) `activeLED != -1 && lastLedActionTime > ledCooldown`
> 

### Problem 7 判斷是否有移動搖桿
```arduino=96
// 判斷是否有移動搖桿
if ( __7.1__ || __7.2__ || __7.3__ || __7.4__ ) {
  moved = true;
}
```
:::success
Problem 7 :
選項 >> 只有一個是對的喔!
:::
```
__7.1__ || __7.2__ || __7.3__ || __7.4__
```


> A ) `yVal < 200 || yVal > 800 || xVal < 200 || xVal > 800`
> 
> 
> B ) `yVal < 200 || yVal < 800 || xVal > 200 || xVal > 800`
> 
> C ) `yVal > 200 || yVal < 800 || xVal > 200 || xVal < 800`
> 
> D ) `yVal > 200 || yVal > 800 || xVal < 200 || xVal < 800`
> 

---
### Problem 8 檢查移動方向是否與 LED 位置匹配
```arduino=101
// 檢查移動方向是否與 LED 位置匹配
if( __8__ ) 
/*
如果點亮的是 UpLed，則應該向上推
如果點亮的是 RightLed，則應該向右推
如果點亮的是 LeftLed，則應該向左推
如果點亮的是 DownLed，則應該向下推
*/
    correctMove = true;
}
```
### Hint :
![image](https://hackmd.io/_uploads/HkRYFMWHgx.png)
**黑色搖桿：**
1. 往上推：`xVal > 800`
2. 往右推：`yVal > 800`
3. 往左推：`yVal < 200`
4. 往下推：`xVal < 200`

**紅色搖桿：**
1. 往上推：`yVal < 200`
2. 往右推：`xVal > 800`
3. 往左推：`xVal < 200`
4. 往下推：`yVal > 800`
:::success
Problem 8 : 
選項 >> 請根據你拿到的搖桿選擇正確答案。
:::
**黑色搖桿：** `( activeLED == 0 && xVal > 800 ) || ( activeLED == 1 && yVal > 800 ) || ( activeLED == 2 && yVal < 200 ) || ( activeLED == 3 && xVal < 200 )`
**紅色搖桿：** `( activeLED == 0 && yVal < 200 ) || ( activeLED == 1 && xVal > 800 ) || ( activeLED == 2 && xVal < 200 ) || ( activeLED == 3 && yVal > 800 )`

### Problem 9 隨機點亮一顆 LED

```arduino=129
// 隨機點亮一顆 LED
void lightRandomLED() {
  activeLED = random(0, 4); // 產生 0~3 的隨機數
  digitalWrite( __9.1__, __9.2__ );
}
```

:::success
Problem 9 :
選項 >> 只有一個是對的喔!
:::
```
__9.1__ / __9.2__
```

> A ) `ledPins[activeLED] / HIGH`
> 
> 
> B ) `ledPins[activeLED] / LOW`
> 
> C ) `ledPins[millis()] / HIGH`
> 
> D ) `ledPins[gameDuration] / LOW`
> 

---

### Problem 10 關閉所有 LED

```arduino=135
// 關閉所有 LED
void turnOffAllLEDs() {
  for (int i = 0; i < 4; i++) {
    __10__;
  }
```

:::success
Problem 10 :
選項 >> 只有一個是對的喔!
:::

> A ) `digitalWrite(ledPins[j], LOW)`
> 
> 
> B ) `digitalWrite(ledPins[j], HIGH)`
> 
> C ) `digitalWrite(ledPins[i], LOW)`
> 
> D ) `digitalWrite(ledPins[i], HIGH)`
> 

### Problem 11 發出 1000 Hz 音調 300ms / 發出 1500 Hz 音調 500ms

```arduino=142
// 🎵 遊戲開始音效
void playStartCountdown() {
    for (int i = 3; i > 0; i--) {
        __11.1__
        __11.2__ // 發出 1000 Hz 音調 300ms
        noTone(buzzer);
        delay(1000);  // 等待 1 秒
    }
    __11.3__
    __11.4__ // 發出 1500 Hz 音調 500ms
    noTone(buzzer); 
    delay(500);
}
```

:::success
Problem 11 :
選項 >> 只有一個是對的喔!
:::
```
__11.1__ / __11.2__ / __11.3__ / __11.4__
```

> A ) `delay(300); / tone(buzzer, 1000); / tone(buzzer, 1500); / delay(500);`
> 
> 
> B ) `delay(300); / tone(buzzer, 1000); / delay(500); / tone(buzzer, 1500);`
> 
> C ) `tone(buzzer, 1000); / delay(300); / tone(buzzer, 1500); / delay(500);`
>
> D ) `tone(buzzer, 1000); / delay(300); / delay(500); / tone(buzzer, 1500);`
>