การสร้าง LEDController Class ทำให้โค้ดควบคุม LED มีระเบียบ เป็นระบบ และขยายไปเป็น “ไฟวิ่ง / ไฟเตือน / ไฟสถานะหลายดวง” ได้ง่ายมาก ออกแบบคลาสสำหรับควบคุม LED หนึ่งดวงหรือหลายดวง แล้วใช้ร่วมกับ Button Class จาก EP1.1


🎯 เป้าหมายการเรียนรู้เมื่อจบ EP1.2 นักเรียนจะ:

  • เข้าใจแนวคิด “แยกหน้าที่” เป็นคลาส:
    • Button = จัดการปุ่ม
    • LEDController = จัดการไฟ
  • ใช้คลาสควบคุม LED ได้ เช่น:
    • เปิด/ปิด (On/Off)
    • Toggle (สลับสถานะ)
    • กระพริบด้วยเวลา (Blink แบบไม่บล็อกด้วย millis())
  • ทำระบบ “ปุ่มกด → ควบคุม LED หลายดวง / หลายรูปแบบ” ได้อย่างเป็นระเบียบ

🧩 1) ทบทวนแนวคิด: แยก “Input Logic” ออกจาก “Output Logic”

  • คลาส Button → ทำหน้าที่จัดการปุ่ม: debounce, ตรวจ wasPressed(), isPressed()
  • คลาส LEDController → ทำหน้าที่จัดการ LED: on/off/toggle/blink

ข้อดี:

  • โค้ดใน loop() จะอ่านเป็นประโยคภาษาไทยได้เลย: “อัปเดตปุ่ม → ถ้าปุ่มกด → สั่ง LED นี้ toggle หรือ blink”

💻 2) ตัวอย่างคลาส LEDController (แบบควบคุม LED 1 ดวง)

วางคลาสนี้ไว้ด้านบนของไฟล์ .ino

C++
class LEDController {
  private:
    int pin;
    bool state;               // ON/OFF ปัจจุบัน
    bool blinking;            // โหมดกระพริบหรือไม่
    unsigned long interval;   // ช่วงเวลาการกระพริบ (ms)
    unsigned long lastToggle; // เวลาที่เปลี่ยนสถานะล่าสุด

  public:
    LEDController(int pinNumber) {
      pin = pinNumber;
      state = false;
      blinking = false;
      interval = 500;
      lastToggle = 0;
    }

    void begin() {
      pinMode(pin, OUTPUT);
      off();
    }

    void on() {
      state = true;
      blinking = false;       // ถ้าเปิดค้าง ให้หยุดโหมดกระพริบ
      digitalWrite(pin, HIGH);
    }

    void off() {
      state = false;
      blinking = false;
      digitalWrite(pin, LOW);
    }

    void toggle() {
      state = !state;
      blinking = false;
      digitalWrite(pin, state ? HIGH : LOW);
    }

    void startBlink(unsigned long ms) {
      interval = ms;
      blinking = true;
      lastToggle = millis();
    }

    void stopBlink() {
      blinking = false;
      // ไม่บังคับเปลี่ยนสถานะไฟ ให้คงตาม state ปัจจุบัน
      digitalWrite(pin, state ? HIGH : LOW);
    }

    // ต้องเรียกใน loop() เสมอ เพื่อให้โหมด blink ทำงาน
    void update() {
      if (blinking) {
        unsigned long now = millis();
        if (now - lastToggle >= interval) {
          lastToggle = now;
          state = !state;
          digitalWrite(pin, state ? HIGH : LOW);
        }
      }
    }

    bool isOn() {
      return state;
    }
};

💡 3) ใช้ร่วมกับ Button Class (รีวิวตัวอย่างย่อจาก EP1.1)

สมมติเรามี Button แบบ EP1.1 (begin(), update(), wasPressed()) ตัวอย่าง:

C++
class Button {
  private:
    int pin;
    bool usePullup;
    unsigned long lastChangeTime;
    unsigned long debounceMs;
    bool lastStableState;
    bool currentState;
    bool pressedEvent;

  public:
    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;

      if (reading != currentState) {
        currentState = reading;
        lastChangeTime = now;
      }

      if (now - lastChangeTime > debounceMs) {
        if (lastStableState != currentState) {
          if (usePullup) {
            if (lastStableState == HIGH && currentState == LOW) {
              pressedEvent = true;
            }
          } else {
            if (lastStableState == LOW && currentState == HIGH) {
              pressedEvent = true;
            }
          }
          lastStableState = currentState;
        }
      }
    }

    bool wasPressed() {
      return pressedEvent;
    }

    bool isPressed() {
      if (usePullup) return (lastStableState == LOW);
      else return (lastStableState == HIGH);
    }
};

💻 4) ตัวอย่างโปรแกรม: ปุ่ม 1 ปุ่ม ควบคุม LEDController 1 ดวง

โจทย์:

  • กดปุ่มครั้งที่ 1 → LED ติดค้าง
  • กดครั้งที่ 2 → LED กระพริบช้า (1 วินาที)
  • กดครั้งที่ 3 → LED กระพริบเร็ว (200ms)
  • กดครั้งที่ 4 → LED ดับ
  • แล้ววนลูปกลับไปสถานะแรก
C++
Button btn(4, true, 80);          // ปุ่มที่ขา 4, ใช้ PULLUP
LEDController led(2);             // LED ที่ขา 2

int mode = 0; // 0 = off, 1 = on, 2 = blink slow, 3 = blink fast

void setup() {
  Serial.begin(115200);
  btn.begin();
  led.begin();
}

void loop() {
  btn.update();   // อัปเดตสถานะปุ่ม
  led.update();   // ให้ LEDController ทำงานโหมด blink ถ้ามี

  if (btn.wasPressed()) {
    mode = (mode + 1) % 4;  // 0 → 1 → 2 → 3 → 0 → ...
    Serial.print("Mode: ");
    Serial.println(mode);

    switch (mode) {
      case 0:
        led.off();
        break;
      case 1:
        led.on();
        break;
      case 2:
        led.startBlink(1000); // 1 วินาที
        break;
      case 3:
        led.startBlink(200);  // 0.2 วินาที
        break;
    }
  }
}

จะเห็นว่า:

  • logics (โหมด, การเปลี่ยนสถานะ) → อยู่ใน loop()
  • การจัดการปุ่ม → ฝั่ง Button
  • การจัดการ LED → ฝั่ง LEDController

โค้ดใน loop() เลยอ่านง่ายมาก:

“อัปเดตปุ่ม → ถ้าปุ่มถูกกด → เปลี่ยนโหมด → สั่ง LED”


💡 5) ต่อยอด: หลาย LED + หลายปุ่ม

สมมติครูอยากทำ “ป้ายไฟเล็ก ๆ” หรือ “แผงสถานะ 3 ดวง” แบบนี้:

  • ปุ่ม 1: Toggle ไฟ LED1
  • ปุ่ม 2: ให้ LED2 กระพริบ
  • ปุ่ม 3: ปิดทุกดวง
C++
Button btn1(4, true, 80);
Button btn2(5, true, 80);
Button btn3(18, true, 80);

LEDController led1(2);
LEDController led2(16);
LEDController led3(17);

void setup() {
  btn1.begin();
  btn2.begin();
  btn3.begin();

  led1.begin();
  led2.begin();
  led3.begin();
}

void loop() {
  // อัปเดตปุ่มและ LED ทุกตัว
  btn1.update();
  btn2.update();
  btn3.update();

  led1.update();
  led2.update();
  led3.update();

  // ปุ่ม 1: toggle LED1
  if (btn1.wasPressed()) {
    led1.toggle();
  }

  // ปุ่ม 2: toggle โหมดกระพริบ LED2
  if (btn2.wasPressed()) {
    if (led2.isOn()) {
      led2.startBlink(300);
    } else {
      led2.on();
    }
  }

  // ปุ่ม 3: ปิดทุกดวง
  if (btn3.wasPressed()) {
    led1.off();
    led2.off();
    led3.off();
  }
}

🧪 6) แบบฝึกหัด EP1.2

ปรับ LEDController ให้มีฟังก์ชัน

  • setInterval(ms) เพื่อเปลี่ยนความเร็วตอน blink โดยไม่ต้องเรียก startBlink ซ้ำ
  1. ทำ “โหมดไฟจราจร” ด้วย 3 LED:
    • แดงติด 3 วิ → เขียวติด 3 วิ → เหลืองติด 1 วิ → วนซ้ำ
  2. ใช้ปุ่ม 1 ปุ่ม เป็น “สวิตช์เปลี่ยน Pattern”
    • Pattern 1 = ไฟจราจร
    • Pattern 2 = ไฟวิ่งซ้าย–ขวา

🧠 7) สรุป

แนวคิดสรุป
การใช้ Class หลายตัวทำให้โค้ดแบ่ง “หน้าที่” ชัดเจน
LEDControllerเป็นตัวจัดการไฟ ON/OFF/BLINK
update()แนวคิดสำคัญสำหรับงานที่ต้องใช้เวลา (millis(), non-blocking)
การชั้นเชิง (Layering)Input (Button) → Logic (Mode) → Output (LEDController)
การขยายโค้ดเพิ่ม LED/ปุ่มได้โดยไม่ทำให้โค้ดเละ