สวัสดีครับเพื่อน Maker ทุกคน! วันนี้เราจะมาพูดถึงเรื่องสำคัญที่หลายคนที่เริ่มเขียนโปรแกรม Arduino มักจะพลาดไป นั่นคือการใช้งาน delay() ที่แม้จะดูง่ายและใช้งานสะดวก แต่กลับสร้างปัญหาให้กับโปรแกรมของเราได้มากมาย
ถ้าคุณเคยเขียนโปรแกรม Blink LED หรือโปรเจค Arduino ง่ายๆ คุณคงเคยใช้ delay() กันมาแล้วแน่นอน แต่เมื่อโปรเจคเริ่มซับซ้อนขึ้น คุณจะพบว่า delay() เป็นอุปสรรคใหญ่ต่อการพัฒนาโปรแกรมที่มีประสิทธิภาพ
ทำไมต้องหลีกเลี่ยง delay() ใน Arduino?
ปัญหาหลักของ delay():
1. การบล็อกการทำงาน (Blocking)
2. ไม่สามารถทำงานหลายอย่างพร้อมกันได้ ลองนึกภาพว่าคุณต้องการ:
- LED กระพริบทุก 500ms
 
- อ่านค่าอุณหภูมิทุก 2 วินาที
 
- ตรวจสอบปุ่มกดตลอดเวลา
 
- ส่งข้อมูลผ่าน WiFi ทุก 10 วินาที
 
ด้วย delay() สิ่งเหล่านี้ทำพร้อมกันไม่ได้!
3. ผลกระทบต่อการตอบสนอง (Responsiveness)
รู้จักกับ millis() และ micros() - ทางออกที่ดีกว่า
millis() คืออะไร?
- ฟังก์ชันที่คืนค่าเวลาเป็นมิลลิวินาที นับตั้งแต่ Arduino เริ่มทำงาน
 
- ไม่หยุดการทำงานของโปรแกรม (Non-blocking)
 
- มีความแม่นยำสูงและใช้งานง่าย
 
micros() คืออะไร?
- คล้าย millis() แต่คืนค่าเป็นไมโครวินาที
 
- ใช้สำหรับงานที่ต้องการความแม่นยำสูงมาก
 
- เหมาะสำหรับการวัดเวลาที่สั้นมาก
 
ตารางเปรียบเทียบ:
| ฟังก์ชัน | 
หน่วยเวลา | 
ความแม่นยำ | 
Overflow | 
การใช้งาน | 
| delay() | 
มิลลิวินาที | 
ปานกลาง | 
ไม่มี | 
Blocking | 
| millis() | 
มิลลิวินาที | 
สูง | 
49.7 วัน | 
Non-blocking | 
| micros() | 
ไมโครวินาที | 
สูงมาก | 
71 นาที | 
Non-blocking | 
ตัวอย่างการเปลี่ยนจาก delay() เป็น millis()
1. Blink LED อย่างง่าย
แบบเก่า (ใช้ delay):
แบบใหม่ (ใช้ millis):
2. ทำงานหลายอย่างพร้อมกัน
3. การใช้ micros() สำหรับความแม่นยำสูง
การจัดการ Overflow ใน millis() และ micros()
Overflow คืออะไร?
millis() จะ overflow (กลับไปเป็น 0) หลังจาก 49.7 วัน 
micros() จะ overflow หลังจาก 71 นาที 
- การคำนวณเวลาอาจผิดพลาดเมื่อเกิด overflow
 
วิธีแก้ไขปัญหา Overflow:
ทำไมวิธีนี้ใช้ได้แม้เกิด overflow?
- การลบของ unsigned long จะให้ผลลัพธ์ที่ถูกต้องแม้เกิด overflow
 
- ตัวอย่าง: ถ้า currentMillis = 100, previousMillis = 4294967295 (ก่อน overflow)
 
- ผลการลบ: 100 - 4294967295 = 101 (ผ่านการ wraparound)
 
โปรเจคตัวอย่างจริง: ระบบควบคุมอุณหภูมิอัตโนมัติ
ปัญหาที่พบบ่อยและวิธีแก้ไข
1. เวลาไม่แม่นยำ
2. การใช้หน่วยเวลาผิด
3. ลืมจัดการ Overflow
เทคนิคขั้นสูง: สร้าง Timer Class
การประยุกต์ใช้กับ ESP32/ESP8266
สรุปและข้อแนะนำ
ข้อดีของ millis() และ micros():
✅ Non-blocking: ไม่หยุดการทำงานของโปรแกรม
✅ Multitasking: ทำงานหลายอย่างพร้อมกัน
✅ Responsive: ตอบสนองได้ทันที
✅ Accurate: ความแม่นยำสูง
✅ Flexible: ปรับเปลี่ยนได้ง่าย
ข้อเสียของ delay():
❌ Blocking: หยุดการทำงานทั้งหมด
❌ Single-task: ทำได้ครั้งละงาน
❌ Unresponsive: ตอบสนองช้า
❌ Inflexible: ปรับเปลี่ยนยาก
เมื่อไหร่ควรใช้อะไร:
ใช้ millis() เมื่อ:
- ต้องการทำงานหลายอย่างพร้อมกัน
 
- ต้องการความแม่นยำในการจับเวลา
 
- ต้องการระบบที่ตอบสนองเร็ว
 
- เขียนโปรแกรมที่ซับซ้อน
 
ใช้ micros() เมื่อ:
- ต้องการความแม่นยำสูงมาก (ไมโครวินาที)
 
- วัดเวลาที่สั้นมาก
 
- ทำงานกับ sensor ที่ต้องการ timing แม่นยำ
 
ใช้ delay() ได้เมื่อ:
- โปรแกรมง่ายๆ ที่ทำงานเดียว
 
- ต้องการ debounce ปุ่มกดสั้นๆ
 
- สร้าง prototype เร็วๆ
 
Tips สำหรับการเขียนโปรแกรมที่ดี:
- วางแผนโครงสร้างโปรแกรม - คิดว่างานไหนต้องทำเมื่อไหร่
 
- ใช้ตัวแปรที่มีความหมาย - เช่น 
ledBlinkInterval แทน interval1 
- แยกหน้าที่งาน - สร้างฟังก์ชันแยกสำหรับแต่ละงาน
 
- ใช้ const สำหรับค่าคงที่ - เช่น 
const long interval = 1000; 
- คิดถึง overflow - ใช้การลบแทนการบวกเปรียบเทียบ
 
ทรัพยากรเพิ่มเติม:
หวังว่าบทความนี้จะช่วยให้คุณเข้าใจการใช้ millis() และ micros() แทน delay() และสามารถนำไปประยุกต์ใช้ในโปรเจคของคุณได้นะครับ!
มีคำถามหรือต้องการคำแนะนำเพิ่มเติม สามารถติดต่อ ModuleMore ได้เสมอครับ
Happy Coding! 🚀