🔘 EP1.1: Button Class & OOP Style
การห่อหุ้มการทำงานของ “ปุ่มกด + Debounce” ให้อยู่ใน Class เพื่อให้โค้ดอ่านง่าย นำกลับใช้ซ้ำได้ และรองรับหลายปุ่มได้สบาย
🎯 จุดประสงค์การเรียนรู้
- เข้าใจแนวคิด Class / Object ใน Arduino C++
- สร้างคลาสสำหรับ ปุ่มกด (Button) ที่ดูแลเรื่อง:
- กำหนดขา (pin)
- ใช้
INPUT_PULLUPอัตโนมัติ - จัดการ Debounce
- ตรวจว่า “เพิ่งถูกกดหนึ่งครั้ง”
- ใช้งานปุ่มหลายปุ่มได้ด้วยโค้ดที่สะอาดและสั้นลง
- เห็นความแตกต่างระหว่างโค้ด “ธรรมดา” vs “แบบ OOP”
🧠 1) ทบทวนปัญหาจาก EP1 (แบบไม่ใช้ Class)
โค้ดแบบเดิม (ปุ่มเดียว, Debounce แบบง่าย):
C++
const int button = 4;
const int led = 2;
unsigned long lastPress = 0;
bool ledState = false;
void setup() {
pinMode(button, INPUT_PULLUP);
pinMode(led, OUTPUT);
}
void loop() {
int reading = digitalRead(button);
if (reading == LOW && millis() - lastPress > 300) {
lastPress = millis();
ledState = !ledState;
digitalWrite(led, ledState);
}
}
❗ ถ้ามี 3 ปุ่ม → เราต้องมี button1, button2, button3, lastPress1, lastPress2, ... โค้ดจะยาว เละ และดูแลยาก 👉 ตรงนี้แหละครับที่ OOP / Class เข้ามาช่วย
🧩 2) แนวคิด Class แบบง่าย ๆ
Class = แบบพิมพ์เขียว (Template) ของ “วัตถุ” (Object) ในที่นี้เราจะสร้าง:
class Button = ตัวแทนของ “ปุ่มหนึ่งตัว”
แต่ละปุ่มจะมี:
- หมายเลขขา → pin
- เวลาที่กดล่าสุด →
lastPress - สถานะปัจจุบัน →
pressed - เวลากันเด้ง →
debounceMs
และมี “ความสามารถ” เช่น:
begin()→ ตั้งค่า pinModeupdate()→ อ่านค่า & เช็ก DebouncewasPressed()→ ตอบว่า “มีการกดครั้งใหม่หรือไม่”
💻 3) ตัวอย่าง Class: Button
C++
class Button {
private:
int pin;
bool usePullup;
unsigned long lastChangeTime;
unsigned long debounceMs;
bool lastStableState;
bool currentState;
bool pressedEvent; // true เมื่อตรวจพบการกดครั้งหนึ่ง (ขอบ LOW)
public:
// Constructor: ระบุขา และว่าจะใช้ PULLUP หรือไม่
Button(int pinNumber, bool enablePullup = true, unsigned long debounce = 50) {
pin = pinNumber;
usePullup = enablePullup;
debounceMs = debounce;
lastChangeTime = 0;
lastStableState = HIGH;
currentState = HIGH;
pressedEvent = false;
}
void begin() {
if (usePullup) {
pinMode(pin, INPUT_PULLUP);
lastStableState = HIGH;
} else {
pinMode(pin, INPUT);
lastStableState = LOW;
}
currentState = lastStableState;
}
void update() {
int reading = digitalRead(pin);
unsigned long now = millis();
pressedEvent = false;
// ถ้ามีการเปลี่ยนสถานะแบบฉับพลัน → รอ debounce
if (reading != currentState) {
currentState = reading;
lastChangeTime = now;
}
// เมื่อผ่านมาเกินเวลา debounce → ยืนยันสถานะใหม่
if ((now - lastChangeTime) > debounceMs) {
if (lastStableState != currentState) {
// เกิดการเปลี่ยนสถานะที่ "นิ่งแล้ว"
// ตรวจขอบ: ถ้าใช้ PULLUP, การกด = จาก HIGH → LOW
if (usePullup) {
if (lastStableState == HIGH && currentState == LOW) {
pressedEvent = true; // เพิ่งกดหนึ่งครั้ง
}
} else {
// ถ้าเป็น pull-down, การกด = LOW → HIGH
if (lastStableState == LOW && currentState == HIGH) {
pressedEvent = true;
}
}
lastStableState = currentState;
}
}
}
// ปุ่มตอนนี้อยู่ในสถานะ "กด" หรือไม่ (ค้างอยู่)
bool isPressed() {
if (usePullup) {
return (lastStableState == LOW);
} else {
return (lastStableState == HIGH);
}
}
// ปุ่ม "เพิ่งถูกกดหนึ่งครั้ง" หรือไม่ (ใช้สำหรับ toggle)
bool wasPressed() {
return pressedEvent;
}
};
วาง class Button { … } ไว้ด้านบนสุดของไฟล์ .ino ได้เลย
💻 4) ใช้ Class Button ในโปรแกรมจริง (ปุ่มควบคุม LED)
ตอนนี้โค้ดใน setup() และ loop() จะอ่านง่ายขึ้นมาก 👇
C++
// ประกาศวัตถุ (Object) จากคลาส Button
Button btn(4, true, 80); // ใช้ขา 4, INPUT_PULLUP, debounce 80 ms
const int led = 2;
bool ledState = false;
void setup() {
Serial.begin(115200);
btn.begin(); // ตั้งค่า pinMode ของปุ่ม
pinMode(led, OUTPUT);
}
void loop() {
btn.update(); // อัปเดตสถานะปุ่ม (ต้องเรียกทุก loop)
if (btn.wasPressed()) { // ถ้ามีการกดครั้งใหม่
ledState = !ledState;
digitalWrite(led, ledState);
Serial.println(ledState ? "LED ON" : "LED OFF");
}
}
จุดสังเกต:
- โค้ดใน
loop()สั้นและชัด: “อัปเดตปุ่ม → ถ้าเพิ่งกด → สลับ LED” - เรื่อง Debounce, การตรวจขอบสัญญาณ,
millis()→ ถูกซ่อนไว้ใน Class แล้ว
🧩 5) ข้อดีของการใช้ Class ใน Lab นี้
ก่อนใช้ Class
- มีตัวแปรกระจัดกระจาย:
buttonPin, lastPress, reading, state... - ถ้ามี 3 ปุ่ม → โค้ดยาว 3 เท่า
- เด็กจะเริ่มสับสนระหว่างตัวแปรแต่ละชุด
หลังใช้ Class Button
- เขียน แนวคิด ครั้งเดียว → ใช้ได้กับหลายปุ่ม
- ถ้าต้องการ 3 ปุ่ม:
C++
Button btn1(4, true, 80);
Button btn2(5, true, 80);
Button btn3(18, true, 80);
void setup() {
btn1.begin();
btn2.begin();
btn3.begin();
}
void loop() {
btn1.update();
btn2.update();
btn3.update();
if (btn1.wasPressed()) { /* ทำอย่างหนึ่ง */ }
if (btn2.wasPressed()) { /* ทำอีกอย่าง */ }
if (btn3.wasPressed()) { /* ทำอย่างอื่น */ }
}
โค้ดจะอ่านง่ายขึ้นอย่างชัดเจน และจะเริ่มเห็น “ความหมายของ OOP” แบบจับต้องได้ครับ ✅
💡 6) ตัวอย่างต่อยอด: 3 ปุ่มควบคุม 3 LED
C++
Button btn1(4, true, 50);
Button btn2(5, true, 50);
Button btn3(18, true, 50);
const int led1 = 2;
const int led2 = 16;
const int led3 = 17;
bool led1State = false;
bool led2State = false;
bool led3State = false;
void setup() {
btn1.begin();
btn2.begin();
btn3.begin();
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
}
void loop() {
btn1.update();
btn2.update();
btn3.update();
if (btn1.wasPressed()) {
led1State = !led1State;
digitalWrite(led1, led1State);
}
if (btn2.wasPressed()) {
led2State = !led2State;
digitalWrite(led2, led2State);
}
if (btn3.wasPressed()) {
led3State = !led3State;
digitalWrite(led3, led3State);
}
}
จะเห็นว่า:
- โค้ดไม่ “รก” แม้มีหลายปุ่ม
- ลอจิกชัด: แต่ละปุ่ม → ควบคุมแต่ละ LED
🧪 7) แบบฝึกหัด EP1.1 (แนว OOP)
- แก้ไขคลาส
Buttonให้มีเมธอดเพิ่ม:bool isHeld()→ true ถ้ากดค้างเกิน X ms
- สร้างระบบ:
- กดสั้น → Toggle LED
- กดค้าง 2 วินาที → ปิดไฟทุกดวง
- ทำ “เมนูควบคุม” ด้วย 2 ปุ่ม:
- ปุ่ม A = เลื่อนตำแหน่งเลือก (LED ตัวที่กำลังเลือก)
- ปุ่ม B = เปิด/ปิด LED ตัวนั้น
🧾 8) สรุปแนวคิด EP1.1
| หัวข้อ | สรุป |
|---|---|
| Class / OOP | ช่วยจัดโค้ดให้เป็นระเบียบ แยก “หน้าที่” เป็นกล่อง ๆ |
| Button Class | ซ่อนรายละเอียดการอ่านปุ่ม, debounce, ขอบสัญญาณ ไปในคลาสเดียว |
| การใช้งาน | ประกาศ Button btn(pin); → btn.begin(); → ใน loop: btn.update();, btn.wasPressed() |
| เหมาะกับ | โปรเจกต์ที่มีหลายปุ่ม หลาย I/O และต้องการโค้ดที่อ่านง่าย |
