การต่อวงจรปุ่มกด (Button Switch) เพื่อรับสัญญาณ Digital และการเขียนโปรแกรมอ่านค่าปุ่มอย่างถูกต้องด้วยการใช้ Pull-up / Pull-down Resistor


🎯 จุดประสงค์การเรียนรู้

  1. อธิบายหลักการทำงานของสวิตช์กดติด-ปล่อยดับ (Push Button Switch) ได้
  2. อธิบายความแตกต่างของวงจร Pull-up และ Pull-down
  3. สามารถต่อวงจรปุ่มกดเข้ากับ ICON-32 และเขียนโปรแกรมอ่านค่าสัญญาณได้
  4. ป้องกันปัญหา Floating Input และการเด้งของสวิตช์ (Switch Bounce)
  5. ประยุกต์ใช้งานร่วมกับ LED เพื่อแสดงผลสถานะได้

🧩 อุปกรณ์ที่ใช้

ลำดับรายการอุปกรณ์จำนวนหมายเหตุ
1บอร์ดไมโครคอนโทรลเลอร์ ICON-321(ESP32 compatible)
2สวิตช์กดติดปล่อยดับ (Push Button)1แบบ 4 ขา หรือ 2 ขา
3ตัวต้านทาน 10 kΩ1ใช้เป็น Pull-down resistor
4ตัวต้านทาน 220 Ω1ต่อกับ LED
5LED สีใดก็ได้1แสดงผลสถานะ
6สาย Jumper5–6 เส้นสำหรับต่อบน Breadboard

🧠 หลักการทำงานของสวิตช์ (Button Switch)

เมื่อเรากดปุ่ม สวิตช์จะเชื่อมต่อขา IN กับ VCC หรือ GND ทำให้บอร์ดอ่านได้ว่าเป็น HIGH (1) หรือ LOW (0)

แต่ถ้าไม่มีตัวต้านทาน “ดึง” ค่าให้คงที่เมื่อไม่กด →จะเกิดสถานะ “ลอย” (Floating) และค่าที่อ่านได้จะไม่แน่นอน

แก้ไขโดยใช้

  • Pull-up Resistor: ดึงขา Input ขึ้นไปที่ VCC
  • Pull-down Resistor: ดึงขา Input ลงไปที่ GND

⚙️ 1. การต่อวงจรแบบ Pull-down

🔌 การต่อวงจร

C++
  +3.3V ──┬─────────────┐
          │             │
[Button]
         [10kΩ]         │
          │             │
         GND          GPIO 4 (ICON-32)
  • ขณะไม่กด → ขา GPIO ถูกดึงลง GND → อ่านค่า LOW
  • ขณะกด → GPIO ต่อกับ +3.3 V → อ่านค่า HIGH

💻 โปรแกรมทดสอบ Pull-down

C++
const int button = 4;
const int led = 2;

void setup() {
  pinMode(button, INPUT);
  pinMode(led, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  int val = digitalRead(button);
  digitalWrite(led, val);
  Serial.println(val);
  delay(100);
}

🔹 เมื่อกดปุ่ม → LED จะสว่าง
🔹 เมื่อปล่อยปุ่ม → LED ดับ

🍼 หลักการทำงานทีละขั้นตอน

จะแบ่งการทำงานเป็น 2 ส่วนหลัก ตามโครงสร้างของ Arduino/ESP32:

1. ส่วน const int (การประกาศตัวแปร)

  • const int button = 4;
    • กำหนดตัวแปรชื่อ button ให้มีค่าคงที่ (const) เป็น 4
    • หมายความว่า เราจะใช้ GPIO 4 ของ ESP32 เพื่อต่อกับสวิตช์ปุ่มกด
  • const int led = 2;
    • กำหนดตัวแปรชื่อ led ให้มีค่าคงที่ (const) เป็น 2
    • หมายความว่า เราจะใช้ GPIO 2 (ซึ่งเป็นขาที่มี LED สีฟ้าติดอยู่บนบอร์ด ESP32 ส่วนใหญ่) เพื่อเป็น Output

2. ส่วน void setup() (ทำงานครั้งเดียวตอนเปิดเครื่อง)

  • pinMode(button, INPUT);
    • สั่งให้ ESP32 ตั้งค่าขา button (GPIO 4) ให้เป็นโหมด รับค่า (Input)
  • pinMode(led, OUTPUT);
    • สั่งให้ ESP32 ตั้งค่าขา led (GPIO 2) ให้เป็นโหมด ส่งค่า (Output)
  • Serial.begin(115200);
    • เปิดการสื่อสารแบบอนุกรม (Serial) ที่ความเร็ว 115200 bps เพื่อใช้ดูค่าผ่าน Serial Monitor ในคอมพิวเตอร์

3. ส่วน void loop() (ทำงานวนซ้ำไปเรื่อยๆ)

  • int val = digitalRead(button);
    • (สำคัญ) อ่านค่าดิจิทัล (สถานะ 0 หรือ 1) จากขา button (GPIO 4) แล้วเก็บไว้ในตัวแปรชื่อ val
    • ถ้าขานี้ได้รับไฟ (HIGH หรือ 3.3V) val จะเท่ากับ 1
    • ถ้าขานี้ต่ออยู่กับกราวด์ (LOW หรือ 0V) val จะเท่ากับ 0
  • digitalWrite(led, val);
    • (สำคัญ) สั่งให้ขา led (GPIO 2) ปล่อยสถานะตามค่าในตัวแปร val
    • ถ้า val เป็น 1 (HIGH) -> LED จะติด
    • ถ้า val เป็น 0 (LOW) -> LED จะดับ
  • Serial.println(val);
    • ส่งค่า val (ที่เป็น 0 หรือ 1) ไปแสดงผลที่ Serial Monitor เพื่อให้เราตรวจสอบได้
  • delay(100);
    • หน่วงเวลา 100 มิลลิวินาที (0.1 วินาที) เพื่อให้โปรแกรมไม่ทำงานเร็วเกินไป ช่วยลดภาระการทำงานและทำให้อ่านค่าใน Serial Monitor ได้ง่ายขึ้น

⚙️ 2. การต่อวงจรแบบ Pull-up

ใน ICON-32 (ESP32) เราสามารถใช้ “ตัวต้านทาน Pull-up ภายในบอร์ด” ได้เลย
โดยไม่ต้องต่อต้านทานจริงจากภายนอก

🔌 การต่อวงจร

C++
GPIO 4 ──[Button]───► GND
  • ขณะไม่กด → ขา GPIO ถูกดึงขึ้นด้วย Pull-up ภายใน → HIGH
  • ขณะกด → ต่อกับ GND → LOW

💻 โปรแกรมทดสอบ Pull-up

C++
const int button = 4;
const int led = 2;

void setup() {
  pinMode(button, INPUT_PULLUP); // เปิด Pull-up ภายใน
  pinMode(led, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  int val = digitalRead(button);
  digitalWrite(led, !val);  // กด = LED ติด
  Serial.println(val);
  delay(100);
}

🔹 กดปุ่ม = LOW (LED ติด)
🔹 ปล่อยปุ่ม = HIGH (LED ดับ)


⚡ 3. ปัญหาสัญญาณ “เด้ง” (Switch Bounce)

เมื่อกดปุ่มจริง สัญญาณอาจเด้ง HIGH-LOW หลายครั้งในไม่กี่มิลลิวินาที
ทำให้ไมโครคอนโทรลเลอร์เข้าใจว่ากดหลายครั้ง

💻 โปรแกรมแก้ด้วย Software Debounce

C++
const int button = 4;
const int led = 2;

int ledState = LOW;
int buttonState = HIGH;
int lastButtonState = HIGH;

unsigned long lastDebounceTime = 0;
long debounceDelay = 50;

void setup() {
  pinMode(button, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  digitalWrite(led, ledState);
}

void loop() {
  int reading = digitalRead(button);
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == LOW) {
        ledState = !ledState;
        digitalWrite(led, ledState);
      }
    }
  }
  
  lastButtonState = reading;
}

🍼 หลักการทำงานทีละขั้นตอน

โปรแกรมนี้แบ่งการทำงานใน loop() เป็น 3 ส่วนอย่างชัดเจน: 1. กันสั่น (Debounce), 2. ตรวจจับการเปลี่ยนแปลง (State Change), และ 3. อัปเดตความจำ (Update)

1. ส่วนประกาศตัวแปร (Global Variables)

  • ledState: จำสถานะ LED ( LOW หรือ HIGH)
  • buttonState: “ความจำ” ของสถานะปุ่มที่ นิ่งแล้ว (Stable State)
  • lastButtonState: “ความจำ” ของสถานะปุ่ม ดิบ (Raw State) ที่อ่านได้ใน loop รอบที่แล้ว
  • lastDebounceTime, debounceDelay: ตัวแปรสำหรับจับเวลา Debounce (50ms)

2. ส่วน void setup()

  • pinMode(button, INPUT_PULLUP): ตั้งค่าขาปุ่มเป็น Input Pull-up (ปกติเป็น HIGH, กดแล้วเป็น LOW)
  • pinMode(led, OUTPUT): ตั้งค่าขา LED เป็น Output
  • digitalWrite(led, ledState): สั่งให้ LED เริ่มต้นในสถานะ “ดับ” (ตามค่า ledState = LOW)

3. ส่วน void loop() (หัวใจของโปรแกรม)

  1. int reading = digitalRead(button);
    • อ่านค่า “ดิบ” (Raw) จากปุ่มในรอบนี้ (อาจจะสั่นเป็น 0 หรือ 1 ก็ได้)
  2. if (reading != lastButtonState)
    • “ตัวตรวจจับการสั่น”: “ค่าดิบ (reading) รอบนี้ ไม่เหมือน ค่าดิบ (lastButtonState) รอบที่แล้วใช่ไหม?”
    • ถ้าใช่: แปลว่ามีการเปลี่ยนแปลงเกิดขึ้น (เริ่มกด, เริ่มปล่อย, หรือแค่สั่น)
  3. lastDebounceTime = millis();
    • รีเซ็ตตัวจับเวลา ทันทีที่ตรวจพบการเปลี่ยนแปลง
  4. if ((millis() - lastDebounceTime) > debounceDelay)
    • “ตัวตรวจสอบความเสถียร”: “เวลาผ่านไปนานเกิน 50ms โดยที่ ไม่มีการเปลี่ยนแปลงค่าดิบ (ตามข้อ 2) แล้วหรือยัง?”
    • ถ้าใช่: แปลว่าค่า reading ที่อ่านได้ตอนนี้ “นิ่ง” และ “เสถียร” แล้ว
  5. if (reading != buttonState)
    • “ตัวตรวจจับการเปลี่ยนแปลงสถานะ” (State Change Detector):
    • (ทำงานต่อเมื่อผ่านข้อ 4 มาแล้ว)
    • “สถานะที่นิ่งแล้ว (reading) ไม่เหมือนกับ สถานะที่นิ่งที่เราจำไว้ (buttonState) ใช่หรือไม่?”
    • ถ้าใช่: แปลว่าสถานะ “นิ่ง” ได้เปลี่ยนไปแล้ว (เช่น จาก “ปล่อย” เป็น “กด” หรือ “กด” เป็น “ปล่อย”)
  6. buttonState = reading;
    • อัปเดต “ความจำ” สถานะที่นิ่งแล้ว (buttonState) ให้เป็นสถานะใหม่ทันที
  7. if (buttonState == LOW)
    • ตรวจสอบว่าสถานะใหม่ที่นิ่งแล้วนั้น คือ “ถูกกด” (LOW) ใช่หรือไม่
    • ถ้าใช่: นี่คือ “จังหวะขอบขาลง” (Falling Edge) ที่เราตามหา!
    • ledState = !ledState; -> สลับค่า (Toggle) สถานะ LED ใน “ความจำ”
    • digitalWrite(led, ledState); -> สั่งงาน LED จริง
  8. lastButtonState = reading;
    • (สำคัญมาก) อัปเดต “ความจำ” สถานะดิบ (lastButtonState) ให้เป็นค่าปัจจุบัน (reading)
    • นี่คือการเตรียมพร้อมสำหรับ loop รอบถัดไป เพื่อใช้เทียบในข้อ 2

✅ โปรแกรมนี้จะเปลี่ยนสถานะ LED ทุกครั้งที่กดปุ่มครั้งเดียว และป้องกันการเด้งของสวิตช์


📊 ตารางเปรียบเทียบ Pull-up vs Pull-down

รายการPull-upPull-down
ค่าเมื่อ “ไม่กด”HIGHLOW
ค่าเมื่อ “กด”LOWHIGH
ใช้ตัวต้านทานภายในได้ไหม✅ ใช้ได้ (INPUT_PULLUP)❌ ต้องต่อต้านทานภายนอก
การต่อปุ่มกับGNDVCC
ความนิยมใช้⭐⭐⭐⭐⭐⭐⭐
แรงดันที่ใช้3.3 V3.3 V

📘 4. การทดลองเพิ่มเติม

1️⃣ เพิ่ม Serial Monitor แสดงข้อความ "Pressed" / "Released"
2️⃣ ใช้ LED 2 ดวงแสดงสถานะ ON / OFF
3️⃣ กดปุ่มค้าง → ให้ LED กระพริบต่อเนื่อง
4️⃣ ต่อ 2 ปุ่ม แล้วเขียนโค้ดควบคุม LED ซ้าย-ขวา


🧠 สรุป

หัวข้อสรุป
Button Switchใช้ส่งสัญญาณควบคุม Digital (0/1)
Floating Inputค่าสัญญาณไม่แน่นอนเมื่อไม่กด
Pull-up / Pull-downใช้ตัวต้านทานดึงค่าให้คงที่
INPUT_PULLUPใช้ตัวต้านทานภายใน ESP32
Debounceหน่วงเวลาเพื่อป้องกันสัญญาณซ้ำ