#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Servo.h>
/* --- 硬體設定區 --- */
// LCD 設定:位址通常是 0x27,大小 16x2
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Servo 設定
Servo myservo;
const int servoPin = 10;
// Keypad 設定
const byte ROWS = 4;
const byte COLS = 4;
char hexaKeys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
// 配合 Wokwi 的接線習慣,這裡定義接腳
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
/* --- 變數區 --- */
String inputString = ""; // 儲存使用者輸入的數字字串
bool isConfirming = false; // 狀態標記:現在是不是在「確認中」?
void setup() {
Serial.begin(9600);
// 初始化 Servo
myservo.attach(servoPin);
myservo.write(0); // 歸零
// 初始化 LCD
lcd.init();
lcd.backlight();
// 開機畫面
lcd.setCursor(0, 0);
lcd.print("Servo Controller");
delay(1000);
resetDisplay();
}
void loop() {
char customKey = customKeypad.getKey();
if (customKey) {
// 根據目前的狀態(輸入中 vs 確認中)決定按鍵的功能
if (isConfirming) {
handleConfirmation(customKey);
} else {
handleInput(customKey);
}
}
}
// 處理一般輸入模式
void handleInput(char key) {
// 如果按的是數字 0-9
if (key >= '0' && key <= '9') {
if(inputString.length() < 3) { // 限制最多輸入3位數
inputString += key;
lcd.setCursor(0, 1);
lcd.print("Angle: " + inputString);
}
}
// 如果按下 '#' 代表輸入完畢 (Enter)
else if (key == '#') {
if (inputString.length() > 0) {
int angle = inputString.toInt();
// 簡單的防呆:檢查角度是否在 0~180 之間
if(angle >= 0 && angle <= 180) {
askConfirmation(); // 進入確認模式
} else {
lcd.clear();
lcd.print("Error: 0-180 only");
delay(1500);
resetDisplay();
}
}
}
// 如果按下 'B' 代表 Backspace (刪除)
else if (key == 'B') {
inputString = "";
resetDisplay();
}
}
// 處理確認模式
void handleConfirmation(char key) {
if (key == 'C') {
// 按下 C:執行動作!
int angle = inputString.toInt();
lcd.clear();
lcd.print("Moving to " + String(angle));
myservo.write(angle); // 轉動馬達
delay(1500);
resetDisplay();
}
else if (key == 'B') {
// 按下 B:反悔了,取消
lcd.clear();
lcd.print("Cancelled");
delay(1000);
resetDisplay();
}
}
// 輔助函式:切換到確認畫面
void askConfirmation() {
isConfirming = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Angle: " + inputString);
lcd.setCursor(0, 1);
lcd.print("Confirm? (C/B)");
}
// 輔助函式:重置回輸入畫面
void resetDisplay() {
isConfirming = false;
inputString = "";
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter Angle:");
lcd.setCursor(0, 1);
lcd.print("Angle: ");
}