ESP32 Interrupt คืออะไร? ใช้งานตอนไหนถึงเหมาะกับโปรเจกต์จริง

เคยไหมครับ เขียนโปรเจกต์ ESP32 อยู่ดี ๆ แล้วเจอคำว่า Interrupt โผล่มาในตัวอย่างโค้ด แต่ไม่แน่ใจว่ามันจำเป็นจริงไหม? หลายคนรีบใช้เพราะคิดว่ามันดูโปร แต่พอใช้ผิดจุด โค้ดยุ่งกว่าเดิม แถมระบบยังงงอีกต่างหาก

บทความนี้จะพาคุณเข้าใจว่า ESP32 Interrupt คืออะไร ใช้ตอนไหนถึงคุ้ม ใช้ตอนไหนไม่จำเป็น และมีตัวอย่างแบบภาษาคนทำโปรเจกต์จริง เพื่อให้คุณตัดสินใจได้ว่าโปรเจกต์ของคุณควรใช้ Interrupt หรือใช้แค่ loop() กับ millis() ก็พอแล้ว

  • ถ้าคุณยังใหม่กับบอร์ดนี้ แนะนำเริ่มจากบทความ ESP32 คืออะไร ก่อน แล้วค่อยกลับมาอ่านเรื่อง Interrupt ต่อ
  • และถ้ายังไม่คุ้นกับขา input/output แนะนำดู ESP32 Pinout DevKit V1 ควบคู่กัน
ESP32 Interrupt คือการตอบสนองต่อเหตุการณ์ทันทีจากปุ่มกดหรือสัญญาณภายนอก

Interrupt คืออะไรแบบภาษาคนทำโปรเจกต์

Interrupt คือการขัดจังหวะให้ ESP32 ไปทำงานสำคัญทันที

ถ้าอธิบายแบบง่ายมาก ๆ Interrupt คือกลไกที่บอก ESP32 ว่า
“หยุดงานที่กำลังทำไว้ก่อน แล้วรีบมาดูเหตุการณ์นี้เดี๋ยวนี้”
สมมติว่า ESP32 กำลังอ่านค่าเซนเซอร์ทุก ๆ รอบใน loop() แต่จู่ ๆ มีปุ่มฉุกเฉินถูกกด หรือมีพัลส์จาก flow sensor เข้ามาเร็วมาก ถ้ารอให้ loop() วิ่งมาเช็กเอง บางครั้งอาจช้าไปหรือพลาดเหตุการณ์นั้นได้ตรงนี้แหละครับที่ Interrupt มีประโยชน์ เพราะมันช่วยให้ ESP32 ตอบสนองกับเหตุการณ์สำคัญได้ทันที

ถ้าไม่ใช้ Interrupt ระบบจะทำงานแบบไหน

ถ้าไม่ใช้ Interrupt โปรแกรมส่วนใหญ่จะทำงานแบบ Polling คือเช็กไปเรื่อย ๆ ใน loop() เช่น
  • เช็กว่าปุ่มถูกกดหรือยัง
  • เช็กว่า sensor เปลี่ยนค่าหรือยัง
  • เช็กว่า input เป็น HIGH หรือ LOW
วิธีนี้ไม่ได้ผิด และในหลายงานก็เพียงพอมาก แต่ถ้าเหตุการณ์เกิดเร็วมาก สั้นมาก หรือสำคัญมาก การรอเช็กใน loop อาจไม่ทัน

ทำไมมือใหม่ควรรู้เรื่องนี้ตั้งแต่เริ่มทำโปรเจกต์

เพราะมือใหม่มักเจอ 2 แบบครับ

  • แบบแรก: ไม่ใช้ Interrupt ทั้งที่งานควรใช้ ทำให้ระบบพลาด event
  • แบบสอง: ใช้ Interrupt กับทุกอย่าง จนโค้ดซับซ้อนเกินจำเป็น

ถ้าคุณเข้าใจหลักตั้งแต่ต้น คุณจะเลือกเครื่องมือได้ถูกว่า งานนี้ควรใช้ loop()millis() หรือ Interrupt

ESP32 Interrupt ทำงานยังไง

แผนภาพการทำงานของ ISR เมื่อ ESP32 เกิด Interrupt

CPU กำลังทำงานอยู่ แล้วเกิด event แทรกเข้ามา

ลองนึกภาพว่า ESP32 กำลังทำงานตามปกติอยู่ใน loop() เช่น อ่านค่า sensor, อัปเดตจอ, ส่งข้อมูล WiFi

เมื่อมีเหตุการณ์ที่เรากำหนดไว้เกิดขึ้น เช่น

  • ขา GPIO เปลี่ยนจาก LOW เป็น HIGH
  • ขา GPIO เปลี่ยนจาก HIGH เป็น LOW
  • มีสัญญาณพัลส์เข้ามา

ESP32 จะกระโดดไปทำฟังก์ชันพิเศษตัวหนึ่งทันที ฟังก์ชันนั้นเรียกว่า ISR

ISR คืออะไร และหน้าที่จริงของมันคืออะไร

ISR ย่อมาจาก Interrupt Service Routine เป็นฟังก์ชันที่ถูกเรียกตอนเกิด Interrupt

หน้าที่ของ ISR ไม่ใช่ทำทุกอย่างให้เสร็จในนั้น แต่ควรทำแค่เรื่องสั้น ๆ เช่น

  • ตั้ง flag ว่าเกิด event แล้ว
  • นับจำนวนพัลส์เพิ่ม 1
  • จำเวลาที่เกิด event

แนวคิดสำคัญ: ISR ควรสั้น เร็ว และไม่หนัก

RISING, FALLING, CHANGE ต่างกันยังไง

เวลาผูก Interrupt กับ GPIO เรามักกำหนดโหมดประมาณนี้

  • RISING = ทำงานเมื่อสัญญาณเปลี่ยนจาก LOW ไป HIGH
  • FALLING = ทำงานเมื่อสัญญาณเปลี่ยนจาก HIGH ไป LOW
  • CHANGE = ทำงานทุกครั้งที่มีการเปลี่ยนทั้งขึ้นและลง

ตัวอย่างเช่น ถ้าคุณต่อปุ่มแบบกดแล้วดึงลง LOW อาจเลือกใช้ FALLING เพื่อให้ Interrupt ทำงานตอนกดปุ่ม

ESP32 Interrupt ใช้งานตอนไหน

ใช้กับปุ่มกดหรือสวิตช์ที่ต้องตอบสนองไว

ถ้าคุณมีปุ่มกดที่มีความสำคัญ เช่น

  • ปุ่มหยุดมอเตอร์
  • ปุ่มฉุกเฉิน
  • ปุ่มเริ่ม/หยุดโหมดการทำงาน

การใช้ Interrupt จะช่วยให้ระบบตอบสนองได้เร็ว โดยไม่ต้องรอให้ loop วิ่งมาเช็ก

ใช้กับการนับพัลส์ เช่น flow sensor หรือ encoder

งานแบบนี้เหมาะมาก เพราะสัญญาณอาจมาเร็วและถี่ เช่น

  • นับพัลส์จาก water flow sensor
  • นับรอบจาก rotary encoder
  • นับจำนวนวัตถุผ่าน sensor

ถ้าคุณใช้แต่ loop แล้วในระบบมีงานอื่นทำเยอะ เช่น WiFi, จอ, web server ก็มีโอกาสพลาดพัลส์ได้

ใช้จับเหตุการณ์สั้น ๆ ที่ loop() อาจพลาด

บาง event อยู่ไม่นาน เช่น สัญญาณขอบขึ้นขอบลงเร็ว ๆ ถ้า loop ของคุณหมุนไม่เร็วพอ มันอาจมองไม่ทัน แต่ Interrupt ถูกออกแบบมาเพื่อเรื่องนี้โดยตรง

ใช้กับงาน event-driven มากกว่างานอ่านค่าเป็นรอบ

ถ้างานของคุณคือ “รอให้มีเหตุการณ์เกิด แล้วค่อยตอบสนอง” แบบนี้ Interrupt เหมาะมาก

แต่ถ้างานของคุณคือ “อ่านค่าทุก 1 วินาที” หรือ “เช็กทุก 5 วินาที” แบบนี้มักไม่จำเป็นต้องใช้ Interrupt

งานแบบไหนไม่จำเป็นต้องใช้ Interrupt

งานอ่านค่า sensor ช้า ๆ เช่นอุณหภูมิและความชื้น

เซนเซอร์หลายตัวไม่ได้เปลี่ยนเร็วขนาดต้องใช้ Interrupt เช่น

  • DHT11 / DHT22
  • อุณหภูมิห้อง
  • ความชื้นอากาศ
  • ค่าแสงทั่วไป

อ่านเป็นรอบด้วย millis() ก็เพียงพอและดูแลง่ายกว่า

งานที่ใช้ millis() ก็พอแล้ว

ถ้าคุณแค่อยากให้ระบบทำงานหลายอย่างโดยไม่ใช้ delay() การใช้ millis() จะเหมาะกว่าในหลายเคส เช่น

  • อ่าน sensor ทุก 2 วินาที
  • กระพริบ LED ทุก 500 ms
  • เช็กปุ่มทุกไม่กี่ ms

มือใหม่หลายคนข้าม millis() ไปหา Interrupt เลย ทั้งที่จริงแล้วปัญหาหลายอย่างแก้ได้ง่ายกว่าด้วยโค้ด non-blocking ปกติ

ถ้างานของคุณแค่อ่านค่าเป็นรอบ ๆ การใช้ millis() แบบ non-blocking มักง่ายกว่าและดูแลง่ายกว่า

ใช้ Interrupt ผิดที่ โค้ดจะยุ่งโดยไม่จำเป็น

นี่คือสิ่งที่เจอจริงบ่อยมากครับ บางโปรเจกต์ใช้ Interrupt กับทุก input ทั้งที่ไม่จำเป็น สุดท้าย

  • ดีบักยาก
  • โค้ดอ่านยาก
  • เกิดบั๊กแปลก ๆ
  • ระบบไม่เสถียร

สรุปง่าย ๆ: อย่าใช้ Interrupt เพราะมันดูเท่ ใช้เมื่อมันช่วยแก้ปัญหาจริง

ตัวอย่างการใช้ attachInterrupt() บน ESP32

โครงสร้างคำสั่ง attachInterrupt()

ตัวอย่างพื้นฐานบน ESP32 จะหน้าตาประมาณนี้

attachInterrupt(digitalPinToInterrupt(pin), isrFunction, mode);

ความหมายคือ

  • pin = ขาที่ต้องการจับเหตุการณ์
  • isrFunction = ฟังก์ชัน ISR ที่จะให้ทำงาน
  • mode = รูปแบบการเปลี่ยนสัญญาณ เช่น RISING, FALLING, CHANGE

ตัวอย่างนับจำนวนครั้งที่กดปุ่ม

const int buttonPin = 18;
volatile int pressCount = 0;

void IRAM_ATTR handleButtonPress() {
pressCount++;
}

void setup() {
Serial.begin(115200);
pinMode(buttonPin, INPUT_PULLUP);

attachInterrupt(digitalPinToInterrupt(buttonPin), handleButtonPress, FALLING);
}

void loop() {
static int lastCount = 0;

if (pressCount != lastCount) {
lastCount = pressCount;
Serial.print("Button pressed: ");
Serial.println(pressCount);
}
}

โค้ดนี้ทำงานแบบนี้

  • เมื่อกดปุ่ม ขาเปลี่ยนสถานะ
  • Interrupt ทำงานทันที
  • ISR เพิ่มค่า pressCount
  • ใน loop() ค่อยเอาค่านั้นมาแสดงผล

นี่คือรูปแบบที่ดีสำหรับมือใหม่ เพราะ ISR ทำงานสั้น ๆ ส่วนงานแสดงผลไปทำใน loop แทน

ทำไมต้องใช้ volatile กับตัวแปรบางตัว

ตัวแปรที่ถูกแก้ไขใน ISR และถูกอ่านใน loop() ควรประกาศเป็น volatile เพื่อบอกคอมไพเลอร์ว่า

“ค่านี้อาจเปลี่ยนเมื่อไรก็ได้จากภายนอก flow ปกติของโปรแกรม”

ถ้าไม่ใส่ บางครั้งคอมไพเลอร์อาจ optimize จนเกิดผลลัพธ์แปลก ๆ ได้

ข้อควรระวังเวลาเขียน ISR บน ESP32

ISR ต้องสั้นและไว

หลักจำง่ายมากครับ

ISR เอาไว้ “รับรู้” ว่าเกิด event ไม่ได้เอาไว้ “จัดการทุกอย่าง”

ให้ทำแค่

  • ตั้ง flag
  • เพิ่มตัวนับ
  • เก็บ timestamp

แล้วค่อยให้ loop() หรือ task อื่นมาจัดการงานหนักต่อ

ไม่ควรใช้ delay() หรือโค้ดหนักใน ISR

สิ่งที่ไม่ควรยัดใส่ ISR เช่น

  • delay()
  • โค้ดยาว ๆ
  • การสื่อสารที่กินเวลา
  • งานคำนวณหนัก
  • การจัดการ logic ใหญ่ทั้งระบบ

เพราะจะทำให้ระบบตอบสนองช้าลง หรือมีปัญหาแปลก ๆ ตามมา

ระวัง debounce จากปุ่มกด

ปุ่มกดจริงมักไม่ได้เปลี่ยนสถานะนิ่ง ๆ ครั้งเดียว แต่มันอาจเด้งหลายรอบในช่วงเวลาสั้นมาก ทำให้กดครั้งเดียวแต่ interrupt วิ่งหลายครั้ง

วิธีรับมือ เช่น

  • ใช้ debounce ด้วย software
  • ใช้เวลาเช็กห่างกัน เช่นไม่นับซ้ำใน 30-50 ms
  • ใช้วงจรช่วยกรองสัญญาณ ถ้าจำเป็น

ตัวอย่างแนวคิดแบบง่าย

volatile unsigned long lastInterruptTime = 0;
volatile int pressCount = 0;

void IRAM_ATTR handleButtonPress() {
  unsigned long now = millis();
  if (now - lastInterruptTime > 50) {
    pressCount++;
    lastInterruptTime = now;
  }
}

หมายเหตุ: ตัวอย่างนี้ช่วยให้เห็นภาพเรื่อง debounce แต่ในงานจริงควรออกแบบให้เหมาะกับข้อจำกัดของ ISR และระบบที่ใช้

อย่าใส่ทุกอย่างลงใน interrupt

หลายคนเริ่มต้นแล้วทำแบบนี้

  • กดปุ่ม → ISR เปิดรีเลย์
  • ส่ง WiFi
  • อัปเดตจอ
  • บันทึกข้อมูล
  • เช็ก sensor ต่ออีกหลายตัว

แบบนี้เสี่ยงพังครับ วิธีที่ดีกว่าคือให้ ISR แค่แจ้งว่า “เกิด event แล้ว” แล้วไปจัดการต่อใน flow หลัก

ข้อควรระวังในการใช้ ESP32 Interrupt และ ISR สำหรับมือใหม่

Interrupt กับ Polling ต่างกันยังไง

Polling เหมาะกับงานแบบไหน

Polling เหมาะกับงานที่

  • ไม่ต้องตอบสนองเร็วมาก
  • อ่านค่าเป็นช่วง ๆ ได้
  • logic ไม่ซับซ้อน
  • ต้องการโค้ดที่เข้าใจง่าย

ตัวอย่างเช่น อ่านอุณหภูมิทุก 2 วินาที หรือเช็กสถานะน้ำในถังทุก 500 ms

Interrupt เหมาะกับงานแบบไหน

Interrupt เหมาะกับงานที่

  • event เกิดเร็ว
  • event สั้น
  • ห้ามพลาดสัญญาณ
  • ต้องตอบสนองไว

เช่น นับพัลส์ flow sensor, ปุ่มหยุดฉุกเฉิน, encoder, reed switch บางประเภท

ภาพเปรียบเทียบ Polling กับ Interrupt บน ESP32 สำหรับมือใหม่

วิธีเลือกให้เหมาะกับโปรเจกต์จริง

สถานการณ์เหมาะกับเหตุผล
อ่านค่าอุณหภูมิทุก 2 วินาทีPolling / millis()ไม่ใช่ event เร็ว
นับพัลส์จาก flow sensorInterruptพัลส์อาจมาถี่และสั้น
ปุ่มเมนูทั่วไปPollingเขียนง่าย ดูแลง่าย
ปุ่มหยุดฉุกเฉินInterruptต้องตอบสนองไว

ตัวอย่างโปรเจกต์จริงที่ควรใช้ Interrupt

ปุ่มหยุดฉุกเฉิน

ถ้าระบบของคุณควบคุมมอเตอร์ ปั๊ม หรืออุปกรณ์ที่มีความเสี่ยง การใช้ Interrupt กับปุ่มหยุดฉุกเฉินช่วยให้ระบบตอบสนองเร็วขึ้นกว่าการรอ loop

ระบบวัดการไหลของน้ำ

ในงาน Smart Farm หรือระบบจ่ายน้ำอัตโนมัติ flow sensor มักส่งพัลส์ออกมาตามอัตราการไหล ถ้าไม่นับพัลส์ให้ทัน ปริมาณน้ำที่คำนวณได้จะคลาดเคลื่อน

ระบบนับรอบมอเตอร์

ถ้าคุณใช้ sensor ตรวจรอบ เช่น Hall sensor หรือ encoder การใช้ Interrupt จะช่วยให้นับรอบได้แม่นขึ้น โดยเฉพาะตอนความเร็วสูง

ระบบตรวจจับการเปิดปิดประตู

งานแบบ reed switch หรือ magnetic switch ก็เหมาะ เพราะมันเป็น event ชัดเจนว่าเปิดหรือปิดเมื่อไร แล้วค่อยให้ระบบไปทำต่อ เช่น แจ้งเตือนหรือบันทึก log

ตัวอย่างการใช้งาน ESP32 Interrupt ในโปรเจกต์จริง เช่น flow sensor และปุ่มกด

สรุป ESP32 Interrupt สำหรับมือใหม่

เข้าใจหลักให้ถูกก่อนลงมือเขียนโค้ด

Interrupt คือเครื่องมือสำหรับรับมือกับเหตุการณ์ที่ต้องตอบสนองไวหรือห้ามพลาด ไม่ใช่คำสั่งพิเศษที่ต้องยัดใส่ทุกโปรเจกต์

ใช้เมื่อจำเป็น ไม่ใช่ใช้เพราะดูเท่

ถ้างานของคุณอ่านค่าเป็นรอบ ๆ ใช้ millis() หรือ loop ที่ออกแบบดี ๆ ก็พอ แต่ถ้างานของคุณเป็นพัลส์เร็ว ปุ่มฉุกเฉิน หรือ event สั้น ๆ Interrupt จะช่วยได้มาก

เริ่มจากโปรเจกต์เล็กแล้วค่อยต่อยอด

ถ้าคุณเพิ่งเริ่ม แนะนำให้ลองจาก

  • ปุ่มกด + LED
  • นับจำนวนพัลส์จาก sensor
  • ทดลองเปรียบเทียบ Polling กับ Interrupt

พอเข้าใจหลักแล้วค่อยต่อยอดไปงานจริง เช่น Smart Farm, automation หรือระบบวัดการไหลของน้ำ

สรุปสั้นแบบช่างหน้างาน: ถ้า event มาเร็ว มาไว และพลาดไม่ได้ ใช้ Interrupt ได้เลย แต่ถ้าเป็นงานอ่านค่าเป็นรอบ ใช้ loop หรือ millis() ก่อน จะง่ายและนิ่งกว่าครับ

FAQ คำถามที่คนเริ่มต้นมักสงสัย

ESP32 ทุกขาใช้ Interrupt ได้ไหม

โดยทั่วไป ESP32 รองรับ interrupt ได้หลาย GPIO แต่เวลาใช้งานจริงควรเช็กข้อจำกัดของขาแต่ละขา เช่น ขาที่ใช้ตอน boot หรือขาที่มีหน้าที่พิเศษร่วมอยู่แล้ว

ไม่แนะนำครับ เพราะ ISR ควรสั้นและไว การพิมพ์ Serial อาจทำให้เกิดพฤติกรรมที่ไม่เสถียรในบางสถานการณ์ ควรตั้ง flag หรือเก็บค่าไว้ แล้วค่อยพิมพ์ใน loop

ส่วนใหญ่เกิดจาก debounce ของปุ่มกด สวิตช์จริงมักเด้งหลายครั้งในช่วงเวลาสั้นมาก จึงต้องมีการกรองทั้งด้วย software หรือ hardware

millis() เหมาะกับงานที่ต้องการทำเป็นช่วงเวลาแบบ non-blocking ส่วน Interrupt เหมาะกับงานที่ต้องตอบสนองเมื่อเกิด event ทันที ทั้งสองอย่างไม่ใช่คู่แข่งกัน แต่เป็นเครื่องมือคนละงาน

เช่น งานนับพัลส์ flow sensor, ปุ่มหยุดฉุกเฉิน, sensor ตรวจรอบ หรือ input บางอย่างที่เกิดเร็วและไม่ควรพลาด แต่ sensor ช้า ๆ อย่างอุณหภูมิหรือความชื้นทั่วไป มักไม่จำเป็นต้องใช้

Shopping Cart
Scroll to Top