pulseIn() ใน arduino ทำงานอย่างไร ?

การใช้งานฟังก์ชัน pulseIn() ใน Arduino

การใช้งานฟังก์ชัน pulseIn() ใน Arduino เพื่อวัดสัญญาณพัลส์อย่างละเอียด และตัวอย่างโครงงาน


บทนำ: เมื่อการวัดสัญญาณพัลส์กลายเป็นเรื่องสำคัญ

ในงานด้านไมโครคอนโทรลเลอร์หรือการประยุกต์ใช้ Arduino นั้น “สัญญาณพัลส์” (Pulse) ถือเป็นหัวใจสำคัญของการอินเทอร์แอคต์ระหว่างฮาร์ดแวร์และซอฟต์แวร์ ตั้งแต่การวัดระยะทาง การวัดความเร็ว ไปจนถึงการควบคุมอุปกรณ์ และหนึ่งในฟังก์ชันที่มีบทบาทสำคัญที่ Arduino จัดเตรียมให้ก็คือ pulseIn() ฟังก์ชันตัวนี้ช่วยให้นักพัฒนาสามารถวัดความยาว (ความกว้าง) ของช่วงสัญญาณพัลส์ที่กำลังเกิดขึ้นได้โดยไม่ต้องเขียนโค้ดซับซ้อน การใช้งานก็ไม่ยุ่งยาก แถมยังทรงพลังมากพอที่จะให้เรานำไปสร้างสรรค์โครงงานได้มากมาย

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

สำหรับบทความนี้ นอกจากจะอธิบายถึงฟังก์ชัน pulseIn() แล้ว เรายังตัวอย่างการปฏิบัติจริง บอกเล่าความรู้สึกระหว่างการทำงานว่าอะไรควรระวัง หรือเทคนิคเพิ่มเติมเล็กน้อยที่อาจช่วยให้โค้ดของเรารันได้สมบูรณ์และเสถียรมากขึ้น


1. การวัดสัญญาณพัลส์คืออะไร?

ก่อนอื่นเราควรเข้าใจความหมายของการ “วัดสัญญาณพัลส์” กันเสียก่อน คำว่า “พัลส์” (Pulse) ในที่นี้หมายถึงสัญญาณดิจิทัล (Digital Signal) ที่เปลี่ยนสถานะระหว่าง LOW (0) และ HIGH (1) เมื่อเราพูดถึง “วัดสัญญาณพัลส์” ก็คือการวัดระยะเวลาที่สัญญาณอยู่ในสถานะ HIGH หรือในทางกลับกันอาจวัดระยะเวลาที่สัญญาณอยู่ในสถานะ LOW ก็ได้ ขึ้นอยู่กับการตั้งค่าและการใช้งาน
สัญญาณพัลส์นี้ปรากฏในหลายบริบท ตัวอย่างเช่น

  • สัญญาณอัลตราโซนิก: ในโครงงานเซนเซอร์วัดระยะทางด้วย Ultrasonic Sensor (เช่น HC-SR04) จะมีการปล่อยคลื่นเสียงความถี่สูง และจับเวลาที่คลื่นสะท้อนกลับมาเป็นสัญญาณดิจิทัล นี่เป็นการวัดความยาวของพัลส์เพื่อคำนวณระยะทาง
  • สัญญาณควบคุมเซอร์โว (PWM/PPM): ในการควบคุมเซอร์โวมอเตอร์ สัญญาณที่ใช้ในการบอกตำแหน่งของเซอร์โวเป็นการส่งพัลส์ที่มีความกว้างต่างกันออกไป หากต้องการอ่านค่าตำแหน่ง หรือประมวลผลพัลส์เพื่อทำอย่างอื่น ก็สามารถใช้ pulseIn() ได้
  • สัญญาณ IR Remote: รีโมตอินฟราเรดที่เราใช้กันตามบ้านมักยิงสัญญาณเป็นชุดพัลส์ออกมา หากเราต้องการจับความยาวของพัลส์เพื่อถอดรหัสสัญญาณ ก็สามารถใช้ pulseIn() เช่นเดียวกัน

การวัดสัญญาณพัลส์จึงเป็นพื้นฐานที่สำคัญมาก ถ้าเข้าใจและใช้เป็นก็จะเปิดประตูให้เราสร้างโปรเจกต์สร้างสรรค์ได้มากมาย


2. ทำความรู้จักฟังก์ชัน pulseIn(): ภาพรวมและรูปแบบการเรียกใช้

เมื่อเราต้องการวัดช่วงเวลาที่ขาอินพุตของ Arduino อยู่ในสถานะ HIGH หรือ LOW ฟังก์ชัน pulseIn() จะเป็นตัวเลือกที่สะดวกมาก รูปแบบการเรียกใช้ (Syntax) พื้นฐานของ pulseIn() มีดังนี้:

unsigned long pulseIn(uint8_t pin, uint8_t value, unsigned long timeout)

ซึ่งในภาษาที่เข้าใจได้ง่ายก็คือ

  1. pin: หมายเลขพินดิจิทัลที่เราต้องการจะอ่านค่าสัญญาณพัลส์
  2. value: กำหนดว่าจะจับเวลาพัลส์ที่เป็น HIGH หรือ LOW โดยสามารถใส่ได้สองค่า คือ HIGH หรือ LOW
  3. timeout (ไม่บังคับ): เป็นค่ากำหนดระยะเวลา (หน่วยไมโครวินาที) ที่จะให้ pulseIn() รอการเกิดพัลส์ก่อนจะรีเทิร์นกลับมาเป็น 0 ถ้าหากสัญญาณพัลส์ไม่เกิดในเวลาที่กำหนด ฟังก์ชันก็จะส่งค่ากลับเป็น 0 ทันที เพื่อป้องกันโค้ดค้างรอ (Default คือ 1,000,000 ไมโครวินาที หรือ 1 วินาที)

ผลลัพธ์ที่ฟังก์ชัน pulseIn() ส่งกลับมาคือค่าประเภท unsigned long ที่ระบุระยะเวลา (หน่วยไมโครวินาที) ที่สัญญาณอยู่ในสถานะ value (คือ HIGH หรือ LOW) ซึ่งจะเปิดโอกาสให้เรานำค่าเวลานั้นไปคำนวณหรือประมวลผลต่อไป


3. กลไกการทำงานภายในของ pulseIn()

การที่ pulseIn() สามารถจับระยะเวลาของพัลส์ได้ก็เพราะ Arduino จะคอยตรวจสอบสถานะของพินแบบวนลูปตลอดเวลา จนกระทั่งมันเจอสภาวะแบบ “ก่อนพัลส์” “ระหว่างพัลส์” และ “หลังพัลส์” เรียงตามขั้นตอนคร่าว ๆ ดังนี้

  1. รอให้สัญญาณเปลี่ยนไปเป็นตรงข้ามกับที่เราต้องการวัด
    เพื่อเป็นการข้ามช่วงเปลี่ยนแปลงสถานะเก่า ให้แน่ใจว่าเราจะวัดพัลส์ถัดไปจริง ๆ ยกตัวอย่าง ถ้าเราสั่งวัด HIGH Arduino จะรอให้สัญญาณอยู่ในสถานะ LOW เสียก่อน แล้วจึงเข้าเงื่อนไขเตรียมวัด
  2. เมื่อเจอสัญญาณเปลี่ยนไปเป็น value ที่กำหนด (HIGH หรือ LOW)
    Arduino จะจับเวลาว่าเริ่มต้นเมื่อใด
  3. รอจนกว่าสัญญาณจะเปลี่ยนกลับ
    สมมติว่ากำลังวัด HIGH เมื่อ Arduino ตรวจพบว่าสัญญาณกลับไปเป็น LOW ก็จะหยุดจับเวลา
  4. ส่งค่าที่จับได้ (ระยะเวลาในหน่วยไมโครวินาที)
    จากนั้นโปรแกรมก็จะส่งค่าดังกล่าวกลับมาเป็น unsigned long ให้เรา

ระหว่างขั้นตอนนี้ หากมีการกำหนด timeout ไว้และช่วงเวลาที่รอนานเกินค่าที่กำหนด เช่น วัดไปแล้วไม่พบว่าพัลส์สลับสถานะตามที่คาดไว้ ฟังก์ชันก็จะตัดจบ (timeout) ส่งค่ากลับเป็น 0 เพื่อให้โปรแกรมไม่ค้างรอ

ตรงนี้เป็นเหตุผลว่าทำไม pulseIn() อาจทำให้ Arduino “หยุดการทำงาน” ชั่วคราวได้ ถ้ารอแล้วสัญญาณไม่มา ก็จะค้างรออยู่จนถึง timeout ซึ่งสามารถลดปัญหาโดยกำหนด timeout ให้เหมาะสมตามโครงงานของเรา


4. รูปแบบการใช้งานและความหมายของพารามิเตอร์

  • 4.1 pin :
    • เป็นค่าตัวเลขแทนขาของ Arduino ที่จะใช้วัดพัลส์
    • ต้องตั้งขานั้นเป็น “อินพุต” ไม่จำเป็นต้องใช้ pinMode(pin, INPUT) หรือไม่ก็ได้ เพราะโดยทั่วไป pulseIn() จะปรับเอง แต่เพื่อความเคลียร์ควรตั้งเองก่อน
  • 4.2 value :
    • มีสองค่าให้เลือก ได้แก่ HIGH หรือ LOW
    • สมมติเราต้องการวัดระยะเวลาสัญญาณสูง ก็ใช้ HIGH หรือถ้าต้องการวัดระยะเวลาสัญญาณต่ำ ก็ใช้ LOW
    • ขึ้นอยู่กับลักษณะของโครงงาน เช่น ถ้าเป็นโครงงานวัดสัญญาณ PWM บางครั้งอาจต้องวัดช่วงที่สัญญาณเป็น HIGH หรือช่วงที่สัญญาณเป็น LOW เพื่อถอดรหัสข้อมูล
  • 4.3 timeout :
    • เป็นค่าที่ไม่บังคับ (Optional) ถ้าไม่ใส่จะเท่ากับ 1,000,000 ไมโครวินาที (1 วินาที)
    • ถ้าใส่ลงไปก็จะเป็นการกำหนดว่าควรรอการเกิดพัลส์นานแค่ไหนก่อนที่จะรีเทิร์นเป็น 0
    • หากโครงงานของเรารู้ช่วงเวลาคาดหวังของสัญญาณอยู่แล้ว เช่น เรารู้ว่าสัญญาณจะเกิดขึ้นทุก 50 มิลลิวินาที อาจตั้ง timeout สัก 100,000 ไมโครวินาที เพื่อไม่ให้โปรแกรมค้างนานเกินไป

5. ตัวอย่างการใช้งาน pulseIn() พื้นฐาน

ลองมาดูโค้ดตัวอย่างสั้น ๆ ที่ใช้ pulseIn() เพื่ออ่านระยะเวลาที่สัญญาณดิจิทัลอยู่ในสถานะ HIGH สมมติว่าเราใช้พิน 7 เป็นอินพุต:

void setup() {
  Serial.begin(9600);
  pinMode(7, INPUT);   // ตั้งเป็นอินพุตเพื่อรับสัญญาณ
}

void loop() {
  unsigned long duration;
  
  // วัดเวลาที่สัญญาณอยู่ใน HIGH
  duration = pulseIn(7, HIGH);

  // แสดงค่าที่วัดได้บน Serial Monitor
  Serial.println(duration);

  // หน่วงเวลาเล็กน้อย
  delay(500);
}

ในตัวอย่างนี้ เมื่อ pulseIn(7, HIGH) ถูกเรียกใช้งาน Arduino จะ:

  1. รอให้ขา 7 กลับมาเป็น LOW (เพื่อล้างสถานะก่อนหน้านี้)
  2. รอให้ขา 7 เปลี่ยนเป็น HIGH (เริ่มจับเวลา)
  3. รอจนกระทั่งขา 7 เปลี่ยนเป็น LOW (หยุดจับเวลา)
  4. ส่งค่าระยะเวลา (หน่วยไมโครวินาที) ออกมาในตัวแปร duration

จากนั้นเราจะพิมพ์ค่าดังกล่าวลง Serial Monitor ซึ่งเราก็จะได้เห็นตัวเลขเป็นไมโครวินาที ถ้าเราลองเอา Generator หรือสัญญาณ PWM เข้าไป ก็จะเห็นค่าที่แตกต่างกันไปตามความกว้างของพัลส์

6. ข้อควรระวังเมื่อใช้ pulseIn()

  1. การ Block การทำงานของโค้ด (Blocking Function)
    ขณะที่ pulseIn() กำลังทำการตรวจจับพัลส์ ฟังก์ชันจะ “หยุด” (หรือ block) การทำงานของโปรแกรมในส่วนอื่น ๆ ชั่วคราว ซึ่งถ้าโครงงานของเราจำเป็นต้องทำงานหลายอย่างพร้อมกัน (Multitasking) การใช้ pulseIn() อย่างไม่ระวังอาจทำให้โค้ดส่วนอื่นไม่ทำงานได้ ควรตั้งค่า timeout ให้เหมาะสมเพื่อหลีกเลี่ยงปัญหานี้
  2. สัญญาณที่เข้ามาไม่เสถียร
    สัญญาณที่มี Noise หรือมีสัญญาณกวนอื่น ๆ อาจส่งผลให้ pulseIn() จับเวลาได้ไม่ถูกต้อง วิธีแก้ไขคือการใช้ตัวเก็บประจุ (Capacitor) หรือฟิลเตอร์อื่น ๆ ร่วมด้วย หรืออาจต้องเขียนโค้ดกรองสัญญาณที่ไม่ต้องการ
  3. การขาดการอ่านพัลส์เล็ก ๆ ระหว่างรอ
    ถ้าพัลส์ที่เข้ามามีความถี่สูงมากและมีความกว้างพัลส์สั้น pulseIn() อาจตรวจไม่เจอขึ้นอยู่กับจังหวะที่ฟังก์ชันเริ่มอ่าน เพราะมันจะต้องรอเหตุการณ์เปลี่ยนสถานะก่อนหน้าด้วย ถ้าพัลส์สั้นเกินไปอาจหลุดไปได้ การใช้ฟังก์ชันแบบ interrupt หรือใช้ฮาร์ดแวร์จับเวลา (Timer) อาจเหมาะสมกว่า
  4. มองหา Library อื่นหรือฮาร์ดแวร์เสริมในกรณีที่ต้องการวัดหลายช่องพร้อมกัน
    pulseIn() ออกแบบมาให้ใช้งานกับช่องสัญญาณเดียวในช่วงเวลาเดียว หากต้องการวัดพัลส์หลาย ๆ เส้นสัญญาณพร้อมกัน อาจต้องหาไลบรารีภายนอก หรือใช้ไมโครคอนโทรลเลอร์ที่มี Input Capture Unit เพื่อทำให้ไม่สูญเสียการจับพัลส์

7. การประยุกต์ใช้งาน pulseIn() ในโครงงานต่าง ๆ

เมื่อเข้าใจพื้นฐานแล้ว เรามาดูว่าโครงงานที่นิยมใช้งาน pulseIn() มีอะไรบ้าง เพื่อให้เห็นภาพชัดเจนขึ้น

7.1 การวัดระยะทางด้วย Ultrasonic Sensor (HC-SR04)

เซนเซอร์อัลตราโซนิกอย่าง HC-SR04 ใช้หลักการปล่อยคลื่นเสียงความถี่สูง และรับสัญญาณที่สะท้อนกลับมา เพื่อนำระยะเวลาที่คลื่นเดินทางไป-กลับ มาคำนวณเป็นระยะทาง ในกระบวนการนี้:

  1. Arduino จะส่งสัญญาณ Trigger (สูง 10 ไมโครวินาที) ไปยังขา TRIG ของเซนเซอร์
  2. เซนเซอร์จะปล่อยคลื่นเสียง และเมื่อคลื่นสะท้อนกลับ เซนเซอร์จะส่งสัญญาณพัลส์ HIGH ออกมาที่ขา ECHO
  3. ความยาวของสัญญาณ ECHO จะเป็นตัวระบุระยะเวลาเดินทางไปกลับของคลื่น
  4. เราสามารถใช้ pulseIn(echoPin, HIGH) เพื่ออ่านเวลาที่สัญญาณ ECHO อยู่ใน HIGH จากนั้นจึงคำนวณเป็นระยะทางได้

สูตรที่นิยมใช้คือ:

ระยะทาง (เซนติเมตร) = (ระยะเวลา (ไมโครวินาที) / 2)
                       * (ความเร็วเสียง (340 เมตร/วินาที) * 100 / 1,000,000)

เพราะเราแบ่ง 2 ให้กับเวลาเดินทางไปกลับให้กลายเป็นระยะทางขาเดียว และแปลงหน่วยไมโครวินาทีเป็นวินาที แล้วคูณกับความเร็วเสียง (340 เมตรต่อวินาที) และในที่สุดคูณด้วย 100 เพื่อให้เป็นเซนติเมตร

7.2 การอ่านสัญญาณ IR Remote

รีโมตทีวีหรืออุปกรณ์อื่น ๆ ที่ใช้คลื่นอินฟราเรดส่งข้อมูล มักจะส่งข้อมูลด้วยวิธีการเปิด-ปิด LED อินฟราเรดด้วยความเร็วสูง ส่งเป็นแพตเทิร์นของพัลส์ HIGH และ LOW ที่แตกต่างกันไป การถอดรหัสต้องใช้วิธีการอ่านความยาวของพัลส์ ตัวอย่างการใช้ pulseIn() คือ:

  1. ใช้ไดโอด IR Receiver (เช่น TSOP1738) ต่อเข้ากับ Arduino
  2. เมื่อมีการกดปุ่มที่รีโมต ไดโอดจะส่งสัญญาณเป็นพัลส์ดิจิทัลออกมา
  3. Arduino สามารถใช้ pulseIn() จับช่วงเวลาของแต่ละพัลส์ได้ แล้วนำมาเทียบกับค่ามาตรฐานของโปรโตคอล (เช่น NEC, RC5) เพื่อถอดออกมาเป็นหมายเลขปุ่ม

อย่างไรก็ตาม ในงานถอดรหัส IR Remote โดยทั่วไปอาจนิยมใช้ไลบรารีสำเร็จรูป เช่น “IRremote” เพื่อความสะดวก แต่การใช้ pulseIn() ตรง ๆ ก็เป็นวิธีศึกษาพื้นฐานได้ดี

7.3 การวัดความกว้างพัลส์ PWM สำหรับวัดค่า Duty Cycle

ถ้าเรามีพัลส์ PWM ที่สร้างจากแหล่งสัญญาณภายนอก แล้วเราต้องการทราบ “Duty Cycle” ว่าเป็นกี่เปอร์เซ็นต์ เราสามารถทำได้โดยอ่านเวลาที่สัญญาณอยู่ใน HIGH และอ่านเวลาที่สัญญาณอยู่ใน LOW จากนั้นคำนวณค่า Duty Cycle = (เวลาสัญญาณ HIGH) / (คาบทั้งหมด) * 100

highTime = pulseIn(pin, HIGH);
lowTime  = pulseIn(pin, LOW);
totalTime = highTime + lowTime;

dutyCycle = ((float)highTime / (float)totalTime) * 100.0;

ค่าที่ได้ก็จะเป็นเปอร์เซ็นต์ Duty Cycle ของ PWM นั้น ๆ


8. ตัวอย่างโครงงานด้วย pulseIn(): สร้างเครื่องวัดระยะทางอัตโนมัติด้วย HC-SR04 และแสดงผลบนจอ LCD

arduino วัดระยะด้วย ultrasonic
arduino วัดระยะด้วย ultrasonic แสดงผลผ่าน lcd1602

8.1 อุปกรณ์ที่ต้องใช้

  1. Arduino UNO (หรือบอร์ดที่เข้ากันได้)
  2. Ultrasonic Sensor รุ่น HC-SR04
  3. จอ LCD 16×2 (พร้อมตัวปรับไฟแบคไลท์และตัวต้านทานถ้าจำเป็น)
  4. สาย Jumper สำหรับการต่อวงจร
  5. Breadboard (ถ้ามี)

8.2 การต่อวงจร

  • HC-SR04 จะมีขา VCC, Trig, Echo, GND
    • VCC ต่อกับ 5V ของ Arduino
    • GND ต่อกับ GND ของ Arduino
    • Trig ต่อกับ Digital Pin (เช่น 8)
    • Echo ต่อกับ Digital Pin (เช่น 9)

  • การใช้งานจอ lcd1602 ด้วยวงจร bus i2c ให้ทำการต่อดังนี้ :
    • Vcc -> 5v
    • Gnd -> Gnd
    • SCL -> A5
    • SDA -> A4

8.3 โค้ดตัวอย่าง


#include <LCD_I2C.h>

// ประกาศตัวแปรแบบ object ชื่อว่า lcd 
//กำหนดคอนสตั๊กพารามิเตอร์สามตัวคือ ตำแหน่งแอดเรด, คอลัม, แถว
LCD_I2C lcd(0x27, 16, 2);

// กำหนดพินสำหรับ Ultrasonic
const int trigPin = 8;
const int echoPin = 9;

// ตัวแปรสำหรับเก็บเวลาและระยะทาง
long duration;
float distance;

void setup() {
  // กำหนดโหมดพิน
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  // เริ่มการสื่อสารกับ Serial Monitor
  Serial.begin(9600);

  // เริ่มการทำงานของ LCD
  lcd.begin(); 
  lcd.print("Ultrasonic!");
}

void loop() {
  // เคลียร์หน้าจอ LCD ก่อนพิมพ์ข้อมูลใหม่
  lcd.clear();

  // 1. สั่งให้ TRIG เป็น LOW สักระยะเพื่อเคลียร์ค่าสะสม
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);

  // 2. ยิงพัลส์สูง 10us เพื่อสั่งให้ HC-SR04 ส่งคลื่นอัลตราโซนิก
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // 3. วัดระยะเวลาที่ ECHO เป็น HIGH ด้วย pulseIn()
  duration = pulseIn(echoPin, HIGH);

  // 4. คำนวณระยะทางเป็นเซนติเมตร
  // ความเร็วเสียงประมาณ 340 m/s หรือ 29.412 uS/cm ไป-กลับ
  distance = (duration * 0.0343) / 2;  

  // พิมพ์ค่าลงใน Serial Monitor (Debug)
  Serial.print("Distance (cm): ");
  Serial.println(distance);

  // พิมพ์ค่าลง LCD
  lcd.setCursor(0, 0);
  lcd.print("Distance:");
  lcd.setCursor(0, 1);
  lcd.print(distance);
  lcd.print(" cm");

  // หน่วงเวลาซักครู่
  delay(500);
}

การทำงานของโค้ด

  1. เราเคลียร์ TRIG เป็น LOW ก่อน เพื่อป้องกันสัญญาณค้าง
  2. ส่งพัลส์ HIGH ระยะเวลา 10 ไมโครวินาทีไปที่ TRIG
  3. รอและอ่านค่าที่ ECHO เป็น HIGH ด้วย pulseIn(echoPin, HIGH)
  4. เอา duration ที่ได้ (หน่วยไมโครวินาที) ไปคำนวณเป็นระยะทางด้วยสูตร
  5. แสดงผลบน Serial และบน LCD

เท่านี้ Arduino ก็ทำหน้าที่เหมือนเป็นมิเตอร์วัดระยะทาง เล็งเซนเซอร์ไปที่สิ่งกีดขวาง ก็จะได้ระยะห่างขึ้นมา และเราใช้ pulseIn() เป็นแกนหลักในการวัดเวลาที่ ECHO เป็นพัลส์ HIGH


9. เทคนิคและเคล็ดลับเพิ่มเติม

  1. การตั้งค่า Timeout
    หากพบว่าโค้ดของเราค้างหรือช้าเกินไป เพราะบางครั้งวัตถุอาจอยู่ไกลจนสัญญาณสะท้อนไม่กลับมา ลองตั้งค่า timeout ใน pulseIn(echoPin, HIGH, 30000) (30 ms) หรือค่าน้อยกว่าที่เหมาะสมกับระยะสูงสุดที่ต้องการวัด
  2. ความไวของการวัด (Resolution)
    ค่าที่ pulseIn() คืนให้เป็นไมโครวินาที ความละเอียดการวัดจึงขึ้นอยู่กับความเร็วสัญญาณของ Arduino และฟังก์ชัน โดยทั่วไปเพียงพอสำหรับงานที่ไม่ต้องการความละเอียดสูงมาก แต่ถ้าเราต้องวัดพัลส์ระดับนาโนวินาที อาจต้องหาวิธีอื่นที่แม่นยำกว่า
  3. การวัดหลายครั้งเพื่อค่าเฉลี่ย
    หากพบว่าสัญญาณแกว่งไม่เสถียร สามารถทำ Loop อ่านหลาย ๆ ครั้ง แล้วหา “ค่าเฉลี่ย” เพื่อลด Noise ได้ เช่น อ่าน 5 ครั้งแล้วเอาค่ามาบวกกันหาร 5 เป็นต้น
  4. การจัดการสัญญาณรบกวน
    ถ้าใช้สายยาวหรือมีอุปกรณ์อื่นรบกวน บางครั้งการวัดอาจผิดเพี้ยนได้มาก ควรออกแบบวงจรให้สั้นที่สุด หรือใช้สาย Shield หรือกรอง Noise ทางฮาร์ดแวร์
  5. หลีกเลี่ยงการใช้ pulseIn() ในระบบ Multitasking
    ถ้าต้องการรันโค้ดหลายส่วนพร้อมกัน (เช่น ควบคุมมอเตอร์หมุน ตอบสนองเซนเซอร์หลายตัว) การใช้ pulseIn() อาจไม่ตอบโจทย์เพราะมันเป็น Blocking Function อาจต้องใช้การ Interrupt หรือใช้ Hardware Timer แทน

10. เล่าเรื่องความรู้สึกและประสบการณ์การใช้งาน

การใช้งาน pulseIn() เป็นจุดเริ่มต้นที่ทำให้เราเข้าใจธรรมชาติของการรับ-ส่งสัญญาณดิจิทัลอย่างเรียบง่าย แต่เปี่ยมด้วยพลัง ใครหลายคนอาจมองว่ามันเป็นฟังก์ชันเล็ก ๆ ไม่มีอะไรซับซ้อน แต่แท้จริงแล้วมันเป็นเหมือนกระจกที่สะท้อนให้เห็นว่า สัญญาณดิจิทัลที่ไหลผ่านขา I/O ของไมโครคอนโทรลเลอร์นั้น เต็มไปด้วยข้อมูลที่มีค่าสำหรับโครงงานมากมาย

ความน่าสนใจอย่างหนึ่งของการใช้ pulseIn() ก็คือ “ผลลัพธ์ที่จับต้องได้” เมื่อเราได้เห็นตัวเลขบน Serial Monitor หรือบนจอ LCD ที่เปลี่ยนแปลงไปตามการขยับหรือสิ่งกีดขวางที่ผ่านหน้าตัวเซนเซอร์ หรือได้เห็นค่าดิวตี้ไซเคิลของ PWM ที่เปลี่ยนไปแบบเรียลไทม์ มันสร้างความรู้สึกภูมิใจว่า “เรากำลังทำให้ Arduino เข้าใจและสื่อสารกับสัญญาณได้”

แต่ก็ต้องยอมรับว่า pulseIn() ไม่ได้เหมาะกับทุกบริบท เมื่อโครงงานของเราเริ่มขยายตัว มีการอ่านเซนเซอร์หลาย ๆ ตัวพร้อมกัน หรือมีการเชื่อมต่ออุปกรณ์สื่อสารภายนอกที่ต้องตอบสนองรวดเร็ว ฟังก์ชัน pulseIn() อาจกลายเป็นข้อจำกัดได้ เพราะมันบล็อกโปรแกรมจนกว่าจะจับพัลส์เสร็จ แต่ในโปรเจกต์ขนาดเล็กหรือการเรียนรู้เบื้องต้น มันยังคงเป็นอาวุธลับที่ช่วยให้ชีวิตการเรียนรู้ Arduino ง่ายขึ้นมาก

ในมุมมองของผม การได้สัมผัส pulseIn() เป็นครั้งแรกก็คล้ายกับการได้เข้าไปสู่วงการเรียนรู้ไมโครคอนโทรลเลอร์ในเชิงสัญญาณดิจิทัล เพราะหลังจากนั้นไม่นาน ผมก็ได้ทดลองวัดสัญญาณอย่างอื่น ๆ ไม่ว่าจะเป็นสัญญาณจากเซนเซอร์วัดความเร็วลม สัญญาณ IR Remote รวมถึงใช้ตรวจจับการเต้นของชีพจร (ผ่านวงจรเซนเซอร์ชีพจรง่าย ๆ) ซึ่งทำให้ผมรู้สึกว่าศักยภาพของ Arduino นั้นกว้างมาก ต่อไปจะสร้างโปรเจกต์ที่จริงจังยิ่งขึ้น ก็แค่มองหาวิธีต่อยอดจากสิ่งที่ได้เรียนรู้จาก pulseIn() นี้


11. การต่อยอด และปรับปรุง

หากเราสนใจขยายต่อจากโครงงานที่ใช้ pulseIn() หรืออยากจะปรับปรุงโปรแกรมของตัวเอง มีแนวทางหลายอย่างที่สามารถทำได้ เช่น

  1. ใช้ Interrupt
    หากไม่ต้องการให้โค้ดค้าง เมื่อมีการเปลี่ยนสถานะของสัญญาณจึงค่อยเรียกฟังก์ชัน ให้ Arduino ทำอย่างอื่นไปเรื่อย ๆ จนกว่าจะถึงเวลาต้องอ่านค่า แต่รูปแบบการใช้งานจะซับซ้อนขึ้น
  2. ผสาน pulseIn() เข้ากับ IoT
    หากต้องการส่งค่าการวัดสัญญาณพัลส์ขึ้น Cloud หรือ SmartPhone อาจเพิ่มโมดูล WiFi (ESP8266, ESP32) หรือโมดูล Bluetooth ให้ Arduino ส่งข้อมูลแบบไร้สาย
  3. วัดหลายพัลส์พร้อมกัน (Multi-channel)
    เราอาจนำแนวคิดการวัดพัลส์ด้วยการเวียนสลับพินไปมา หรืออาจเลือกใช้บอร์ดที่มีทรัพยากรมากกว่า หรือมีไลบรารีเสริม เพื่อจับพัลส์หลาย ๆ ช่องพร้อมกัน
  4. ปรับใช้กับสัญญาณเฉพาะทาง
    ถ้าสัญญาณที่ต้องวัดมีรูปแบบพิเศษ เช่น โค้ดส่งข้อมูลหลายบิตผ่านพัลส์ ลองเขียนโค้ดวิเคราะห์ค่าเวลาทีละช่วงเพื่อถอดรหัสข้อมูล นี่เป็นอีกด่านหนึ่งที่ท้าทายและสนุกมาก

บทสรุป: pulseIn() กับพลังที่มากกว่าแค่ฟังก์ชันอ่านสัญญาณ


ถึงแม้ pulseIn() จะเป็นฟังก์ชันสั้น ๆ ใน Arduino IDE แต่มันเป็นกุญแจดอกหนึ่งที่เปิดประตูให้เราสามารถวัด “ความยาว” ของสัญญาณดิจิทัลได้อย่างง่ายดาย งานใดก็ตามที่ต้องมีการวัดช่วงเวลาและสัญญาณ HIGH/LOW ไม่ว่าจะเป็นงานวัดระยะทาง, ควบคุมมอเตอร์, การรับข้อมูลดิจิทัลเชิงเวลา ฯลฯ pulseIn() สามารถเป็นตัวช่วยให้เราเริ่มต้นได้ดี

ในโครงงานต้นแบบที่เราได้ยกตัวอย่างเครื่องวัดระยะทางด้วย HC-SR04 นั้นก็เห็นได้ว่าเพียงใช้ pulseIn() ไม่กี่บรรทัด เราก็ได้โค้ดที่มีประโยชน์อย่างจริงจัง ใช้ในชีวิตประจำวันหรือต่อยอดไปเป็นหุ่นยนต์ตรวจจับสิ่งกีดขวางก็ยังได้

นอกจากนั้น องค์ความรู้จากการใช้ pulseIn() ยังเสริมรากฐานในด้านการวัดสัญญาณดิจิทัลและเวลา เพราะเราจะเริ่มเข้าใจอย่างลึกซึ้งว่า “Arduino ทำงานอย่างไร เมื่อมันเฝ้ารอเปลี่ยนสถานะจาก LOW เป็น HIGH และจาก HIGH กลับมา LOW” นี่คือจุดเริ่มต้นของการต่อยอดอีกมากในศาสตร์ Embedded System เช่น การเขียนโค้ดระดับ Register หรือการใช้งาน Interrupt

ท้ายที่สุด อย่าลืมว่าข้อจำกัดของ pulseIn() ก็มีอยู่ ไม่ว่าจะเป็นความสามารถในการอ่านได้แค่ช่องสัญญาณเดียวในช่วงเวลาเดียว หรือการที่มันเป็น Blocking Function ที่อาจไม่เหมาะสมในระบบที่ต้องทำงานแบบ Multitask อย่างไรก็ตาม สิ่งเหล่านี้ไม่ใช่อุปสรรคแต่เป็นโอกาสให้เราได้เข้าใจสถาปัตยกรรมของไมโครคอนโทรลเลอร์เชิงลึก และพร้อมจะขยับไปเรียนรู้การใช้งาน Timer/Counter, Input Capture หรืออินเตอร์รัปต์ต่อไป

สรุป

  • ฟังก์ชัน pulseIn() คือเครื่องมือเบื้องต้นแต่ทรงพลังในการวัดระยะเวลาของสัญญาณดิจิทัล (HIGH/LOW)
  • เหมาะสำหรับโครงงานเล็กถึงปานกลางที่ต้องการวัดช่วงพัลส์ เช่น Ultrasonic Sensor, IR Remote, PWM Analysis
  • ควรระวังเรื่อง Blocking และตั้งค่า Timeout เพื่อไม่ให้โค้ดค้าง
  • สามารถปรับแต่งหรือขยายไปสู่โครงงาน IoT, Multiple Sensors หรือระบบซับซ้อนขึ้นได้

เมื่อลองนำไปใช้งานจริง ๆ เราจะค้นพบว่าการวัดพัลส์คือความสนุกอย่างหนึ่ง คล้ายกับการไขปริศนาแห่งคลื่นสัญญาณ เมื่อวัดเวลาได้ เราก็สามารถแปลงเป็นข้อมูล มีประโยชน์ต่อการควบคุมหรือวิเคราะห์ต่าง ๆ ได้อย่างมหาศาล


ปิดท้าย: ก้าวต่อไปหลังจากรู้จัก pulseIn()

หลังจากเราได้ทดลองและสนุกกับ pulseIn() จนเข้าใจดีแล้ว ลองตั้งคำถามต่อว่า “โครงงานของเราต้องการประสิทธิภาพมากกว่านี้ไหม?” “เราจำเป็นต้องวัดหลายช่องสัญญาณพร้อมกันหรือไม่?” หากคำตอบคือใช่ เราจะพบว่ามันถึงเวลาที่ต้องศึกษาเรื่อง Timer Hardware และ Input Capture ซึ่งเป็นวิธีระดับล่าง (Low-level) ที่สามารถจับเวลาสัญญาณได้แม่นยำและไม่บล็อกโปรแกรม หรืออาจศึกษาเรื่องการใช้ External Interrupt เพื่อตรวจจับสัญญาณเปลี่ยนสถานะ

แต่ถ้าเราต้องการเพียงวัดพัลส์เป็นระยะ ๆ และไม่มีเงื่อนไขความแม่นยำหรือความเร็วที่สูงมาก pulseIn() นั้นเพียงพอและประหยัดทรัพยากรสุด ๆ เพียงแค่เราวางแผนการเรียกใช้อย่างระมัดระวัง (ไม่วางในลูปที่สำคัญเกินไปหรือนานเกินไป) และตั้ง timeout เสมอ ก็จะทำให้โค้ดของเราทำงานได้อย่างมีประสิทธิภาพ