製作動機:因為有人去年在南濱公園被拍了一張追焦照,然後被扣牌六個月,於是我們決定製作出測速機器監督大家有沒有超速。
#include <LiquidCrystal.h>
// LCD 腳位 (Shield 固定接法)
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// 腳位定義
#define IR1_PIN 2 // 中斷腳 (左感測器)
#define IR2_PIN 3 // 中斷腳 (右感測器)
#define BUZZER_PIN 11
#define LED_PIN 12
// 按鍵定義 (使用 Shield 的鍵盤)
#define BTN_RIGHT 0
#define BTN_UP 1
#define BTN_DOWN 2
#define BTN_LEFT 3
#define BTN_SELECT 4
#define BTN_NONE 5
// 測速變數
volatile unsigned long time1 = 0, time2 = 0;
volatile float speed = 0;
volatile bool direction_R = true; // true=右 (IR1→IR2), false=左 (IR2→IR1)
float speedLimit = 30.0; // 預設速限 (cm/s)
const float SENSOR_DISTANCE = 8.5; // 感測器間距 (cm)
void setup() {
lcd.begin(16, 2);
lcd.print("Speed: -- cm/s");
lcd.setCursor(0, 1);
lcd.print("Limit: ");
lcd.print(speedLimit);
lcd.print(" cm/s");
pinMode(IR1_PIN, INPUT);
pinMode(IR2_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
// 設定中斷(雙邊緣觸發)
attachInterrupt(digitalPinToInterrupt(IR1_PIN), irTrigger, CHANGE);
attachInterrupt(digitalPinToInterrupt(IR2_PIN), irTrigger, CHANGE);
}
// 統一的中斷處理函數
void irTrigger() {
static unsigned long lastTime = 0;
static int lastSensor = 0; // 0=無, 1=IR1(左), 2=IR2(右)
int ir1State = digitalRead(IR1_PIN);
int ir2State = digitalRead(IR2_PIN);
unsigned long currentTime = millis();
// 判斷觸發順序
if (ir1State == LOW && lastSensor != 1) { // 左感測器 (IR1) 觸發
if (lastSensor == 2) { // 前一次是右感測器 → 方向 R→L (右到左)
direction_R = false;
speed = (SENSOR_DISTANCE * 1000.0) / (currentTime - lastTime);
updateDisplay();
}
lastSensor = 1;
lastTime = currentTime;
}
else if (ir2State == LOW && lastSensor != 2) { // 右感測器 (IR2) 觸發
if (lastSensor == 1) { // 前一次是左感測器 → 方向 L→R (左到右)
direction_R = true;
speed = (SENSOR_DISTANCE * 1000.0) / (currentTime - lastTime);
updateDisplay();
}
lastSensor = 2;
lastTime = currentTime;
}
}
// 更新 LCD 顯示
void updateDisplay() {
lcd.setCursor(7, 0);
lcd.print(" "); // 清除舊數據
lcd.setCursor(7, 0);
lcd.print(speed, 1); // 顯示速度 (1位小數)
lcd.print(direction_R ? " R " : " L "); // R=右, L=左
}
// 讀取按鍵
int readKeypad() {
int val = analogRead(A0); // Shield 按鍵接 A0
if (val < 50) return BTN_RIGHT;
if (val < 200) return BTN_UP;
if (val < 400) return BTN_DOWN;
if (val < 600) return BTN_LEFT;
if (val < 800) return BTN_SELECT;
return BTN_NONE;
}
// 檢查超速
void checkSpeedLimit() {
if (speed > speedLimit && speed < 1000) {
digitalWrite(LED_PIN, HIGH);
tone(BUZZER_PIN, 1000, 500);
} else {
digitalWrite(LED_PIN, LOW);
noTone(BUZZER_PIN);
}
}
// 按鍵調整速限
void adjustSpeedLimit() {
int key = readKeypad();
if (key == BTN_UP) { // 按 UP 鍵增加速限
speedLimit += 5.0;
if (speedLimit > 100) speedLimit = 10.0;
lcd.setCursor(7, 1);
lcd.print(" "); // 清除舊速限
lcd.setCursor(7, 1);
lcd.print(speedLimit, 1); // 顯示新速限
delay(300); // 防抖延遲
} else if (key == BTN_DOWN) { // 按 DOWN 鍵減少速限
speedLimit -= 5.0;
if (speedLimit < 10) speedLimit = 100.0;
lcd.setCursor(7, 1);
lcd.print(" ");
lcd.setCursor(7, 1);
lcd.print(speedLimit, 1);
delay(300);
}
}
void loop() {
adjustSpeedLimit(); // 檢查按鍵調整速限
checkSpeedLimit(); // 檢查是否超速
delay(100);
}