ESP32 ไม่ได้ค้างเพราะมันไม่เก่ง แต่หลายครั้งมันค้างเพราะโค้ดของคุณสั่งให้มัน “หยุดรอ” แบบไม่รู้ตัว โดยเฉพาะตอนใช้ delay() ยาว ๆ แล้วหวังให้ระบบอ่าน sensor, เช็กปุ่ม, คุมรีเลย์ และต่อ WiFi ไปพร้อมกัน
บทความนี้จะพาคุณเข้าใจแบบภาษาคนทำของว่า delay() ทำให้ระบบหน่วงยังไง, อาการที่เจอบ่อยคืออะไร, และจะแก้ยังไงด้วยแนวคิด non-blocking โดยใช้ millis() แบบที่เอาไปต่อยอดกับงานจริงได้ทันที
- ถ้าคุณยังใหม่กับบอร์ดนี้ และยังไม่เห็นภาพว่า ESP32 ทำอะไรได้บ้าง, มี WiFi/Bluetooth ในตัวยังไง, หรือทำไมมันเหมาะกับงาน automation และ IoT มากกว่า Arduino บางรุ่น แนะนำให้อ่านบทความแม่ ESP32 คืออะไร? ใช้ทำอะไรได้บ้าง ก่อน แล้วค่อยกลับมาดูเรื่อง
delay()จะเข้าใจภาพรวมง่ายขึ้น
สารบัญ
- delay() คืออะไร และทำไมมือใหม่ถึงชอบใช้
- ทำไมใช้ delay() แล้ว ESP32 ถึงดูเหมือนค้าง
- สัญญาณเตือนว่าโค้ดของคุณกำลังโดน block
- ตัวอย่างโค้ดที่ใช้ delay() แล้วเกิดปัญหา
- วิธีแก้ delay() แบบพื้นฐานด้วย millis()
- เปรียบเทียบ delay() กับ millis() แบบเห็นภาพ
- ทำหลายงานพร้อมกันบน ESP32 โดยไม่ใช้ delay()
- งานแบบไหนที่ควรเลี่ยง delay()
- ถ้าจำเป็นต้องหน่วงเวลา ควรทำยังไงแทน
- สรุป
- FAQ คำถามที่พบบ่อย
delay() คืออะไร และทำไมมือใหม่ถึงชอบใช้
delay() ทำงานยังไงใน Arduino และ ESP32
delay(ms) คือการสั่งให้โปรแกรมหยุดรอเป็นเวลาตามที่กำหนด เช่น delay(1000) คือรอ 1 วินาที ระหว่างนั้นโค้ดส่วนถัดไปจะยังไม่ทำงาน
สำหรับมือใหม่ มันดูง่ายมาก เพราะเขียนตรง ๆ แล้วเข้าใจทันที เช่น เปิด LED → รอ 1 วินาที → ปิด LED → รอ 1 วินาที
ทำไมช่วงเริ่มต้นมันดูง่ายและสะดวก
เพราะในตัวอย่างทดลองเล็ก ๆ มันใช้งานได้จริง และเห็นผลเร็วมาก โดยเฉพาะงานพวก:
- กระพริบ LED
- เปิดรีเลย์ค้าง 2 วินาที
- รอข้อความบน Serial Monitor
- ทดลอง sensor แบบอ่านทีละช่วง
ตัวอย่างงานที่คนมักเขียนด้วย delay()
ช่วงเริ่มต้นหลายคนจะเขียนแบบนี้:ปัญหาคือพอโปรเจกต์โตขึ้น เช่น ต้องอ่าน DHT22, เช็กปุ่ม, ส่งข้อมูลขึ้นมือถือ, หรือควบคุมปั๊มน้ำพร้อมกัน โค้ดลักษณะนี้จะเริ่มทำให้ระบบ “หน่วง” ทันทีdigitalWrite(ledPin, HIGH); delay(1000); digitalWrite(ledPin, LOW); delay(1000);
ยูทูป
ทำไมใช้ delay() แล้ว ESP32 ถึงดูเหมือนค้าง
delay() ทำให้ loop() หยุดรอ
หัวใจของปัญหาคือ ระหว่างที่ delay() กำลังรออยู่ โค้ดใน loop() ก็เดินต่อไม่ได้ นั่นแปลว่า ESP32 ไม่ได้ไปเช็กงานอื่นตามที่คุณหวัง
งานอื่นเลยไม่ได้ทำต่อ
ถ้าในระบบมีหลายงานพร้อมกัน เช่น
- อ่าน sensor ทุก 1 วินาที
- เช็กปุ่มกดตลอดเวลา
- สั่งรีเลย์ตามเงื่อนไข
- คงการเชื่อมต่อ WiFi หรือ WebSocket
เมื่อมี delay() ยาว ๆ อยู่ งานพวกนี้จะเหมือนโดนแช่แข็งชั่วคราว
อาการที่เจอบ่อยในงานจริง
- กดปุ่มแล้วติดช้า
- รีเลย์สั่งงานช้า หรือดูเหมือนตอบสนองไม่ทัน
- sensor อ่านค่าเป็นช่วง ๆ ไม่ลื่น
- หน้าเว็บหรือแอปดูหน่วง
- ระบบ IoT หลุดจังหวะ หรือ reconnect แปลก ๆ
ถ้าคุณเริ่มเห็นแล้วว่า delay() ทำให้ระบบหยุดรอและไปขวางงานอื่นยังไง แต่ยังไม่เห็นภาพรวมชัด ๆ ว่า delay() กับ millis() ต่างกันตรงไหน และแต่ละแบบเหมาะกับงานลักษณะไหน แนะนำให้อ่านบทความเสริม delay() vs millis(): เลือกใช้อะไรดีใน ESP32 และ Arduino? เพิ่ม จะช่วยให้คุณเข้าใจแนวคิด non-blocking ได้ชัดขึ้นก่อนนำไปใช้กับโปรเจกต์จริง
ภาษาคนทำงาน: delay() ไม่ได้ทำให้ชิปพัง แต่มันทำให้ “สมองของระบบมัวแต่นั่งรอ” จนงานอื่นไม่ได้เดิน
สัญญาณเตือนว่าโค้ดของคุณกำลังโดน block
ปุ่มกดไม่ค่อยติด
คุณกดปุ่มแล้วต้องรอจังหวะ หรือกดบางครั้งติดบางครั้งไม่ติด อันนี้เป็นอาการคลาสสิกมาก เพราะระบบไม่ได้วนมาเช็กปุ่มตลอดเวลา
รีเลย์ตอบสนองช้า
เวลาคุมพัดลม ปั๊มน้ำ หรือไฟ ถ้ามี delay ยาวอยู่ รีเลย์จะไม่ตอบสนองทันที เพราะโค้ดยังไม่กลับมาถึงส่วนเช็กเงื่อนไข
WiFi หรือ WebSocket หน่วง
ในงาน IoT ถ้าคุณต้องสื่อสารกับแอป มือถือ หรือ dashboard การมี delay มากเกินไปจะทำให้ข้อมูลอัปเดตไม่ลื่น
sensor อ่านค่าเป็นช่วง ๆ
บางคนคิดว่า sensor เสีย แต่จริง ๆ แล้วเป็นเพราะโค้ดอ่านค่าห่างเกินไปจาก delay ที่แทรกอยู่หลายจุด
ตัวอย่างโค้ดที่ใช้ delay() แล้วเกิดปัญหา
ตัวอย่างกระพริบ LED แบบง่าย
const int ledPin = 2;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}
โค้ดนี้ไม่มีปัญหาถ้าคุณต้องการแค่กระพริบ LED อย่างเดียวพอเพิ่ม sensor และ relay แล้วเริ่มพัง
const int relayPin = 23;
const int buttonPin = 18;
void setup() {
pinMode(relayPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(115200);
}
void loop() {
int buttonState = digitalRead(buttonPin);
if (buttonState == LOW) {
digitalWrite(relayPin, HIGH);
delay(3000);
digitalWrite(relayPin, LOW);
}
Serial.println("reading sensor...");
delay(1000);
}วิเคราะห์ว่าบล็อกตรงไหน
จุดที่ทำให้ระบบดูค้างคือ:
delay(3000)ตอนกดปุ่มแล้วรีเลย์ทำงานdelay(1000)ตอนพิมพ์ข้อความอ่าน sensor
แปลว่าช่วงเวลารวมหลายวินาทีนี้ ระบบจะไม่ค่อยกลับมาเช็กปุ่มใหม่ หรือไปทำงานอื่นต่อได้ทันที
วิธีแก้ delay() แบบพื้นฐานด้วย millis()
non-blocking คืออะไร
non-blocking คือแนวคิดที่ทำให้ระบบ “ไม่หยุดรอ” แต่ใช้วิธีคอยเช็กว่าเวลาผ่านไปพอหรือยัง แล้วค่อยทำงาน
แทนที่จะพูดว่า “หยุดรอ 1 วินาที” เราจะเปลี่ยนเป็น “ถ้าผ่านมาแล้ว 1 วินาที ค่อยทำงาน”
millis() ช่วยจับเวลาโดยไม่หยุดระบบยังไง
millis() จะคืนค่าจำนวนมิลลิวินาทีตั้งแต่บอร์ดเริ่มทำงาน คุณสามารถเอาค่านี้มาเปรียบเทียบกับเวลาล่าสุดที่ทำงาน เพื่อกำหนดช่วงเวลาได้
เปลี่ยนวิธีคิดจาก “รอ” เป็น “เช็กเวลา”
นี่คือจุดเปลี่ยนสำคัญของคนทำ ESP32 จริงจัง
- แบบเดิม: ทำงาน → รอ
- แบบใหม่: วนเช็กตลอด → ถึงเวลาเมื่อไรค่อยทำ
เปรียบเทียบ delay() กับ millis() แบบเห็นภาพ
แบบ delay()
const int LED_PIN = 2;
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH);
Serial.println("LED ON");
delay(1000);
digitalWrite(LED_PIN, LOW);
Serial.println("LED OFF");
delay(1000);
Serial.println("Read Sensor");
delay(2000);
Serial.println("Send Notify");
delay(3000);
}
ข้อดีคือเขียนง่าย แต่ระหว่างรอระบบแทบไม่ได้ทำอะไรอย่างอื่นแบบ millis()
const int LED_PIN = 2;
bool ledState = false;
unsigned long previousLedMillis = 0;
unsigned long previousSensorMillis = 0;
unsigned long previousNotifyMillis = 0;
const unsigned long ledInterval = 500;
const unsigned long sensorInterval = 2000;
const unsigned long notifyInterval = 5000;
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
}
void loop() {
unsigned long now = millis();
if (now - previousLedMillis >= ledInterval) {
previousLedMillis = now;
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
Serial.println("LED Toggle");
}
if (now - previousSensorMillis >= sensorInterval) {
previousSensorMillis = now;
Serial.println("Read Sensor");
}
if (now - previousNotifyMillis >= notifyInterval) {
previousNotifyMillis = now;
Serial.println("Send Notify");
}
}ข้อแตกต่างในงานจริง
- delay() เหมาะกับงานทดลองสั้น ๆ
- millis() เหมาะกับงานที่มีหลายอย่างต้องทำพร้อมกัน
ถ้าคุณกำลังทำระบบจริง เช่น Smart Farm, เปิดปั๊มตามเงื่อนไข, ส่งข้อมูลขึ้น dashboard หรือคุมผ่านมือถือ แนว millis() จะปลอดภัยกว่าเยอะ
ทำหลายงานพร้อมกันบน ESP32 โดยไม่ใช้ delay()
เช็กปุ่มตลอดเวลา
ปุ่มไม่ควรถูกตรวจแค่ทุก 1-3 วินาที แต่ควรถูกเช็กแทบทุกลูป
อ่าน sensor เป็นช่วงเวลา
สมมุติคุณอยากอ่านอุณหภูมิทุก 2 วินาที ก็ใช้ตัวจับเวลาแยกสำหรับงานนี้
คุม relay โดยไม่กระทบ WiFi
เมื่อไม่ใช้ delay() การเปิดปิดรีเลย์จะไม่ไปขวางการทำงานของระบบสื่อสาร
หลายคนเริ่มเจอปัญหา delay() ตอนคุมรีเลย์ เพราะอยากให้เปิด 3 วินาที ปิด 5 วินาที แล้วก็เลยเขียนหน่วงตรง ๆ ลงไป พอระบบเริ่มซับซ้อนก็เกิดอาการหน่วงทันที ยิ่งถ้าคุณยังสับสนเรื่องรีเลย์แบบ Active LOW ด้วย ควรไปเคลียร์เรื่อง ESP32 Relay Active LOW คืออะไร ให้ชัดก่อน จะช่วยลดบั๊กได้เยอะมาก
ตัวอย่างงาน Smart Farm
unsigned long previousSensorMillis = 0;
unsigned long previousPumpMillis = 0;
const unsigned long sensorInterval = 2000;
const unsigned long pumpInterval = 5000;
void loop() {
unsigned long now = millis();
// อ่าน sensor ทุก 2 วินาที
if (now - previousSensorMillis >= sensorInterval) {
previousSensorMillis = now;
Serial.println("read sensor");
}
// เช็กเงื่อนไขปั๊มทุก 5 วินาที
if (now - previousPumpMillis >= pumpInterval) {
previousPumpMillis = now;
Serial.println("check pump logic");
}
// เช็กปุ่มตลอดเวลา
if (digitalRead(18) == LOW) {
Serial.println("button pressed");
}
// งาน WiFi / WebSocket / App communication ก็ยังวิ่งต่อได้
}นี่แหละแนวคิดของระบบที่ “ลื่น” ขึ้นแบบรู้สึกได้จริง
งานแบบไหนที่ควรเลี่ยง delay()
งาน WiFi / IoT
- Web Server
- Blynk
- Firebase
- Telegram
- WebSocket
งานพวกนี้ต้องอาศัยการวนเช็กและตอบสนองต่อเนื่อง ถ้าใส่ delay หนัก ๆ ระบบจะเสียจังหวะง่าย
ปัญหาของ delay() จะชัดขึ้นมากเมื่อคุณเริ่มทำงานด้าน WiFi เช่น เชื่อมต่อ router, ขอ IP, reconnect, scan network หรือคุยกับระบบผ่าน web server เพราะงานพวกนี้ต้องอาศัยการวนเช็กสถานะตลอด ถ้าคุณยังไม่คุ้นกับ flow ฝั่งเครือข่าย แนะนำให้อ่าน ESP32 WiFi Library Functions เพิ่ม จะเห็นเลยว่าทำไมโค้ดแบบ non-blocking ถึงสำคัญ
งานที่ต้องตอบสนองเร็ว
- ปุ่มกด
- Interrupt-triggered logic
- การเช็กสถานะหลายอุปกรณ์
- ระบบที่มี timeout และ retry
งานควบคุมรีเลย์และปั๊มน้ำ
ถ้าคุณคุมปั๊มน้ำ พัดลม หรือหมอกในระบบปลูกผัก การตอบสนองช้าบางครั้งไม่ใช่แค่ “น่ารำคาญ” แต่มีผลต่อการทำงานของระบบจริง
ถ้าจำเป็นต้องหน่วงเวลา ควรทำยังไงแทน
ใช้ millis()
นี่คือพื้นฐานที่ควรฝึกให้คล่องก่อน เพราะเป็นแกนของโค้ด non-blocking เกือบทุกแบบ
ใช้ state machine
ถ้างานของคุณมีหลายขั้น เช่น เปิดปั๊ม 5 วินาที → รอ 10 วินาที → เปิดวาล์ว → อ่าน sensor วิธีคิดแบบ state machine จะช่วยให้โค้ดชัดกว่าใช้ delay ซ้อนกัน
ใช้ timer library
ถ้าคุณเริ่มรู้สึกว่าเขียน millis() เองหลายชุดแล้วเริ่มรก การใช้ timer library จะช่วยให้โค้ดอ่านง่ายขึ้น โดยเฉพาะโปรเจกต์ที่มีหลายงานพร้อมกัน
สรุป
delay() ไม่ใช่คำสั่งที่ผิด แต่มันเหมาะกับงานทดลองเล็ก ๆ มากกว่างานระบบจริง ถ้าคุณเริ่มอยากให้ ESP32 ทำหลายอย่างพร้อมกัน เช่น อ่าน sensor, คุม relay, เช็กปุ่ม, ต่อ WiFi, ส่งข้อมูลขึ้นมือถือ คุณควรเริ่มเปลี่ยนจากแนวคิดแบบ “หยุดรอ” ไปเป็น “เช็กเวลาแล้วค่อยทำ”
ถ้าคุณรู้สึกว่า ESP32 ของคุณค้าง ทั้งที่บอร์ดไม่ได้เสีย ให้กลับไปสำรวจโค้ดก่อนเลยว่าใส่ delay() ไว้กี่จุด หลายครั้งปัญหาไม่ได้อยู่ที่ฮาร์ดแวร์ แต่อยู่ที่ flow ของโปรแกรมล้วน ๆ
เริ่มจากฝึก millis() ให้คล่อง แล้วคุณจะเขียนโค้ดที่ลื่นขึ้น ขยายง่ายขึ้น และพร้อมต่อยอดไปงาน IoT จริงได้อีกเยอะ
FAQ: คำถามที่พบบ่อยเกี่ยวกับ delay() บน ESP32
ESP32 ใช้ delay() ได้ไหม
delay(1000) อันตรายไหม
ถ้าใช้ในงานเล็กอาจไม่เป็นไร แต่ถ้าอยู่ในระบบที่ต้องตอบสนองเร็ว มันทำให้ระบบหน่วงชัดเจน
ทำไมปุ่มกดถึงไม่ค่อยติดเมื่อใช้ delay()
เพราะระหว่าง delay ระบบไม่ได้กลับมาเช็กปุ่มตลอดเวลา จึงพลาดจังหวะกดได้
millis() ยากกว่าจริงไหม
งาน WiFi ควรเลี่ยง delay() ไหม
ควรเลี่ยง โดยเฉพาะถ้าต้องมีการรับส่งข้อมูลต่อเนื่อง เช่น WebSocket, Blynk หรือ dashboard
ถ้าต้องการทำงานทุก 2 วินาที ควรใช้อะไร
ควรใช้ millis() หรือ timer library แทน delay()
delay() ทำให้ ESP32 รีสตาร์ตเองได้ไหม
บางกรณีถ้าระบบรวม ๆ หน่วงหรือจัดการงานไม่ดี อาจมีผลทางอ้อมได้ แต่ส่วนใหญ่ปัญหาคือระบบตอบสนองช้า ไม่ใช่ delay ตัวเดียวล้วน ๆ
ถ้ามี sensor ตัวเดียว ใช้ delay() ได้ไหม
ได้ในงานทดลอง แต่ถ้าคิดจะขยายระบบในอนาคต แนะนำให้ฝึกเขียนแบบ non-blocking ตั้งแต่ต้น
มีวิธีดูไหมว่าโค้ดเรากำลังโดน block
ให้สังเกตอาการ เช่น ปุ่มช้า, รีเลย์หน่วง, sensor อ่านเป็นช่วง, การเชื่อมต่อ WiFi ไม่ลื่น หรือระบบทำงานทีละอย่าง
หลังจากเลิกใช้ delay() ควรไปศึกษาอะไรต่อ
แนะนำให้ศึกษาต่อเรื่อง millis(), state machine, timer library, และโครงสร้าง loop ที่ไม่บล็อก เพราะทั้งหมดนี้เป็นฐานสำคัญของงาน ESP32 ที่ใช้งานจริง
อ่านต่อบทความที่เกี่ยวข้อง
ถ้าคุณอยากเข้าใจ ESP32 ให้ลึกขึ้นแบบค่อย ๆ ต่อจากพื้นฐานไปงานจริง แนะนำให้อ่านต่อจากหัวข้อเหล่านี้



