Friday, October 9, 2015

การใช้งาน pulseIn ฟังก์ชัน ตอนที่ 1

หัวข้อนี้จะกล่าวถึงการใช้ฟังก์ชัน pulseIn และใช้บอร์ด Arduino UNO R3 เพื่อวัดความกว้างเพาส์สัญญาณ (Pulse width) หลักการทำงานจะอาศัยการตรวจวัดสัญญาณ HIGH หรือ LOW ที่ส่งเข้าขา digital และเซตขานั้นให้เป็น input port ฟังก์ชัน pulseIn มีหลักในการใช้ดังนี้

กรณีที่1

เมื่อตรวจพบสัญญาณขาเข้าเป็น HIGH ฟังก์ชันนี้จะเริ่มนับเวลาในหน่วยไมโครวินาที (microsec, $\mu$s) และรอจนกว่าจะเจอสัญญาณ LOW ถึงจะหยุดนับเวลา


กรณีที่2 กำหนดให้ตรวจสัญญาณ LOW ก่อน ฟังก์ชันนี้จะเริ่มนับเวลาในหน่วยไมโครวินาที และรอจนกว่าจะเจอสัญญาณ HIGH ถึงจะหยุดนับเวลา


รูปที่1 แสดงสัญญาณคลื่นสี่เหลี่ยม (square wave) และตรวจวัดสัญญาณ HIGH ---> LOW และ ตรวจวัดสัญญาณ LOW ---> HIGH

สรุปได้ว่าฟังก์ชัน pulseIn ทำหน้าที่เป็นนาฬิกาจับเวลาโดยใช้สัญญาณ LOW หรือ HIGH เป็นตัวกำหนด start/stop นั่นเอง

ฟังก์ชัน pulseIn มีรูปแบบการใช้งาน (https://www.arduino.cc/en/Reference/PulseIn)

Syntax

1) ===> pulseIn(pin, value)

2) ===> pulseIn(pin, value, timeout)

pin คือ กำหนดสัญญาณเข้าไหน เช่น ขา digital3, pin คือ 3

value คือ พารามิเตอร์ที่เรากำหนดให้บอร์ดตรวจวัดสัญญาณ HIGH หรือ LOW ก่อน (กำหนดให้เริ่มนับเวลาดังกล่าวข้างต้น)

timeout คือช่วงเวลาสูงสุดที่ฟังก์ชันนี้ยังทำงานอยู่ หากไม่กำหนดพารามิเตอร์นี้ เช่นกรณีแรกจะกำหนด default ไว้ที่ 1 วินาที หรือ 1,000,000 ไมโครวินาที   นั่นหมายความว่าถ้าความกว้างเพาส์สัญญาณที่จะวัดมากกว่า 1 วินาที ฟังก์ชันนี้จะไม่ทำงานและส่งค่า 0 ออกมาแทน ดังนั้นหากต้องการกำหนดให้ timeout สูงสุด 5 วินาที จะเขียนได้ว่า

pulseIn(3, HIGH, 5000000)

ตัวอย่างการใช้งาน และแสดงผลทาง serial communication

int pin = 3;
unsigned long duration;    

void setup()

  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 

  pinMode(pin, INPUT);
}

void loop()
{
    duration = pulseIn(pin, HIGH, 10000000);
    float time=duration/1000.00;
    Serial.print("DelT=");
    Serial.print(time);
    Serial.println(" ms");
}


ตัวอย่างการใช้งานและแสดงผลทางจอ LCD

#include
LiquidCrystal lcd(11, 12, 4, 5, 6, 7);

int pin = 3;
unsigned long duration;
 

void setup()

{
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("UP VeloMeterV1.0");
  delay(2500);
  pinMode(pin, INPUT);
}

void loop()

{
    duration = pulseIn(pin, HIGH, 10000000);
    float time=duration/1000.00;
    lcd.setCursor(0, 1);lcd.print("DelT=         ms");
    lcd.setCursor(6, 1);lcd.print(time);
}


ก่อนจะทดสอบกับอุปกรณ์จริงขอทำการจำลองในโปรแกรมก่อน ในภาพใช้สวิทซ์เป็นเพื่อกำหนดสัญญาณ HIGH เมื่อสวิทซ์ on และเป็น LOW เมื่อสวิทซ์ off ได้ผลลัพธ์ดังภาพ โดยต่อสัญญาณเข้าที่ขา digital 3 จากรูปกำหนดแกนเวลาใน oscilloscope เป็น 0.5 s/div หากอ่านค่าเวลาเทียบกับตัวเลขที่ได้จากฟังก์ชัน pulseIn ที่แสดงในจอ LCD สอดคล้องกันเป็นอย่างดี ดังนั้นการทดสอบครั้งนี้ถือว่าบรรลุวัตถุประสงค์ขั้นต้น


   รูปที่2 ภาพการจำลองการวัดความกว้าง pulse ในโปรแกรม proteus ด้วยฟังก์ชัน pulseIn ใน Arduino โดยวัดเวลาที่กดสวิทซ์ 


ลำดับต่อไปจะทดสอบกับอุปกรณ์จริงโดยใช้ function generator (GWINSTEK รุ่น SFG-1013) เป็นเครื่องกำเนิดสัญญาณคลื่นสี่เหลี่ยมความถี่ต่างๆ เพื่อทดสอบความถูกต้อง (accuracy) และความแม่นยำ (precise) จากนั้นใช้ oscilloscope (Tektronix รุ่น TDS-1002) เพื่อวัดความกว้างเพาส์ (pulse width) เทียบกับฟังก์ชัน pulseIn ของ Arduino เพื่อให้เห็นภาพที่ชัดเจนขึ้นขออธิบายเกี่ยวสัญญาณสี่เหลี่ยมและการคำนวณเล็กน้อย

เวลาที่คลื่นเคลื่อนที่ครบ 1 รอบ เรียกว่า คาบการเคลื่อนที่ หรือ period มักใช้สัญลักษณ์เป็น "T" มีหน่วยเป็น วินาที ปริมาณที่เป็นส่วนกลับของคาบเรียกว่า ความถี่ หรือ frequency มักใช้สัญลักษณ์เป็น "f" มีหน่วยเป็น รอบต่อวนาที หรือ Hertz (Hz) หรือจะกล่าวว่าความถี่กำลังบอกว่า ใน 1 วินาที คลื่นเคลื่อนที่ผ่านจุดใดจุดหนึ่งไปกี่ลูกนั่นเองเขียนความสัมพันธ์ทางคณิตศาสตร์ ระหว่าง T กับ f ได้เป็น

$T=\frac{1}{f}$

สำหรับคลื่นสี่เหลี่ยมมีค่า ดิวตี้ไซเคิล (duty cycle) 50 % ดังภาพด้านล่างนั้น คลื่น 1 ลูกจะประกอบด้วยสัญญาณค่าสูงครึ่งหนึ่ง และอีกครึ่งหนึ่งเป็นสัญญาณค่าต่ำ หรือกล่าวได้ว่าความกว้างของเพาส์สัญญาณสูงเท่ากับความกว้างเพาส์สัญญาณต่ำ สมมติสัญญาณสี่เหลี่ยมมีความถี่ 50 Hz คาบการเคลื่อนที่ T คำนวณได้ดังนี้

$T=\frac{1}{50\, Hz}=0.02\, s=20\, \, ms$

ดังนั้น pulse width จะเท่ากับ 10 ms


รูปที่ 3 แสดงคลื่นสี่เหลี่ยมและความกว้างสัญญาณ (ภาพจาก www.electronics-tutorials.ws)

ในการทดสอบครั้งนี้จะอ่านค่า ความกว้างเพาส์ในหน่วย มิลลิวินาที และขอยกตัวอย่างมาให้เห็นเพียงสองความถี่ คือ 5 และ 25 Hz รูปที่ 4 แสดงความถี่ของสัญญาณสี่เหลี่ยมที่ตั้ง 25 Hz (รูปที่4 ก) รูปสัญญาณและความกว้างเพาส์จากออสซิลโลสโคป (รูปที่4 ข) และค่าความกว้างเพาส์ที่อ่านได้จาก Arduino (รูปที่4 ค) จากการทดลองพบว่า Arduino อ่านค่าความกว้างเพาส์ได้สอดคล้องกับการคำนวณและค่าที่อ่านได้จากออสซิลโลสโคป เป็นอย่างดี

                      รูปที่ 4 แสดงความถี่ รูปสัญญาณ และความกว้างเพาส์ที่ความถี่ 25 Hz

สำหรับที่ความถี่ต่ำ พบว่าค่าที่อ่านได้จาก Arduino ไม่ตรงกับผลการคำนวณและค่าที่อ่านได้จากออสซิลโลสโคป ซึ่งความผิดเพี้ยนส่วนนี้จะได้ทำการทดลองและทดสอบต่อไปเพื่อหาความคลาดเคลื่อนที่เกิดขึ้น


                   รูปที่ 5 แสดงความถี่ รูปสัญญาณ และความกว้างเพาส์ที่ความถี่ 5 Hz

เมื่อทดสอบวัดความกว้างเพาส์เพื่อนำมาเทียบกับผลการคำนวณเชิงทฤษฎี ได้ผลลัพธ์ดังนี้พบว่าที่ความถี่สูงขึ้น (กรณีนี้ f สูงกว่า 10 Hz) ฟังก์ชัน pulseIn จะวัดได้ถูกต้อง แสดงดังกราฟด้านล่าง

 รูปที่ 6 ภาพซ้ายมือแสดง กราฟระหว่างความกว้างเพาส์ (positive pulse width) กับความถี่ โดยเปรียบเทียบระหว่าง ค่าการคำนวณกับการทดลอง ส่วนภาพด้านขวามือแสดง เปรียบเทียบผลต่างของ pulse width กับความถี่

ภาพที่ 6 บ่งบอกความคลาดเคลื่อนของฟังก์ชัน pulse in พบว่าความถี่ต่ำกว่า 10 Hz จะทำให้ความคลาดเคลื่อนมีค่ามากขึ้น  ดังนั้นจากผลการทดลองนี้การอ่านค่าความถี่ต่ำไม่ควรใช้ฟังก์ชัน pulseIn

นอกจากนี้ข้าพเจ้ายังนำ pusleIn ฟังก์ชัน ไปเขียนรวมกับวิธีการส่งข้อมูลไปให้ MS excel มีรายละเอียดของ source code ดังนี้ (การส่งข้อมูลถึง MS excel ลองติดตามในนี้เลย Arduino2Excel)

#include
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

int x = 0;
int row = 0;
int pin = 3;
unsigned long duration;

void setup()

{
  Serial.begin(128000); // opens serial port, sets data rate to 9600 bps
  Serial.println("CLEARDATA");
  Serial.println("LABEL,Time,Index,SensorValueA0");
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("UP VeloMeterV1.0");
  delay(2500);
  pinMode(pin, INPUT);
}

void loop()

{
    row++;
    x++;

    Serial.print("DATA,TIME");
    Serial.print(","); Serial.print(x);
    duration = pulseIn(pin, HIGH, 10000000);
    float time=duration/1000.00;
    Serial.print(","); Serial.println(time);
    lcd.setCursor(0, 1);lcd.print("DelT=         ms");
    lcd.setCursor(6, 1);lcd.print(time);
}


และได้ผลดังภาพด้านล่างนี้ (ส่วนรายละเอียดจะมาเล่าให้ฟังในโอกาสต่อไป)



Saturday, February 21, 2015

How to send data from Arduino to Python

จากบทความที่แล้ว Arduino+MS Excel ที่ว่าผมมีความคิดอยากส่งข้อมูลไปให้ MS Excel ซึ่งทำไปแล้วเอาไปใช้งานให้เกิดประโยชน์มาแล้ว ถึงตอนนี้ผมเริ่มสนุกเลยคิดลองติดต่อผ่าน Python ดูบ้าง นี่เป็นครั้งแรกเลยแต่มาขอปักธงไว้ก่อน ทั้งนี้ไม่มีเหตุผลอื่นครับ แต่มันเป็นเพราะเป็นเรื่องใหม่สำหรับข้าพเจ้าอีกแล้ว (แต่อยากเล่นสนุกๆ) อย่างไรก็ดีผมจะเกาะติดตามคุณลุง Paul (ผมว่าแกเจ๋งดี) ในเวบนี้ครับ Technology tutorials    ผมยืม source code แกมาปรับนิดๆหน่อยๆ (แกคงไม่ทราบหรอก) ก็ใช้ได้เลยในการทดลองนี้ผมอ่านค่าจาก IR sensor ที่ขาอะนาลอก A0 ผลการทดลองมีหลักฐานดังภาพเลยครับ (ต้องขออภัยไม่สามารถลง source code ให้ได้เพราะพึ่งเริ่ม ถ้าผมเข้าใจอย่างดีแล้วจะนำมาลงและอธิบายในภานหน้า)
 


ส่วนบอร์ดผมใช้ Arduino Uno R3 และเซนเซอร์เป็นแบบสะท้อนครับ ดูจากรูปได้เลยครับ แต่นี่คือเริ่มต้นเท่านั้นครับ หลังจากบทความนี้ผมคงจะห่างหายไปสักระยะเพราะต้องไปฝึกใช้งานอย่างจริงจังก่อน


ขอขอบคุณ คุณลุง Paul (แกไม่รู้หรอก แต่จะเป็นครูออนไลน์ของผมแน่นอน)

http://www.toptechboy.com/using-python-with-arduino-lessons/

Tuesday, February 10, 2015

How to send data from Arduino to MS.Excel

           ผมไม่ค่อยคุ้นเคยกับการทำ data logger ไม่มีความรู้เลย ไม่มีเงินซื้อชอฟต์แวร์บางตัวที่มีลิขสิทธิ์ (ปัจจุบันใช้ชอฟต์แวร์ผ่านมหาวิทยาลัย) และมีความต้องจะเก็บข้อมูลจากบอร์ด Arduino UNO R3 (จริงๆ รุ่นไหนได้ทั้งนั้นขอให้สั่งมันเป็น) จึงตั้งคำถามกับตัวเองว่าจะส่งข้อมูลมาเขียนกราฟใน Excel ได้หรือไม่ ทำอย่างไร (ไม่รู้ทำไงครับ) เพราะส่วนตัวคิดว่าเครื่องคอมพิวเตอร์ที่ใช้ Windows ย่อมต้องมี Excel ติดไว้บ้างแน่นอนหากใช้โปรแกรมที่มีอยู่ในเครื่องอยู่แล้วน่าจะสะดวก ประหยัดเงิน จึงค้นหาจากพี่ google ปรากฎว่าชาวบ้านชาวเมืองเขาทำกันเยอะแยะครับ โปรแกรมหนึ่งที่น่าสนใจ มันทำหน้าที่เชื่อมต่อระหว่างบอร์ด Arduino กับ Computer เพื่อส่งข้อมูลไปยังโปรแกรม Excel โปรแกรมที่กล่าวถึงชื่อว่า PLX-DAQ เป็นของค่าย Parallax ก็ดาวโหลดมาแล้วติดตั้งใช้งานได้เลย (เครื่องที่ใช้เป็น Windows 7 (64-bit) MS2010 ) ผลการใช้งานดังรูปเลยครับ


การใช้งาน ต้องโปรแกรมลงในบอร์ด Arduino เพื่อสั่งให้ส่งข้อมูลผ่านโปรแกรม PLX-DAQ ก่อน โปรแกรมใช้งานที่ให้ผลลัพธ์ตามรูปด้านบนมีดังนี้

int x = 0;
int row = 0;

void setup() {
Serial.begin(128000); // opens serial port, sets data rate to 128000 bps
Serial.println("CLEARDATA");
Serial.println("LABEL,Time,Index,SensorValueA0,SensorValueA1,SensorValueA2,SensorValueA3");
}

void loop() {
  
  int sensorValue1 = analogRead(A0);
  int sensorValue2 = analogRead(A1);
  int sensorValue3 = analogRead(A2);
  int sensorValue4 = analogRead(A3);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage1 = sensorValue1 * (5.0 / 1023.0);
  float voltage2 = sensorValue2 * (5.0 / 1023.0);
  float voltage3 = sensorValue3 * (5.0 / 1023.0);
  float voltage4 = sensorValue4 * (5.0 / 1023.0);
  
  // print out the value you read:

row++;
x++;  

Serial.print("DATA,TIME");
Serial.print(","); Serial.print(x); 
Serial.print(","); Serial.print(voltage1);
Serial.print(","); Serial.print(voltage2);
Serial.print(","); Serial.print(voltage3);
Serial.print(","); Serial.println(voltage4);

delay(1000);
}

ส่วนรูปด้านล่างแสดงรูปวงจรอย่างง่ายอ่านค่า voltage จากวงจรแบ่งแรงดันสำหรับการทดสอบในครั้งนี้ 



อธิบายเพิ่มเติม ผมรับค่าจาก Analog A0-A3 ซึ่งเป็นค่าความต่างศักย์ไฟฟ้าคงที่ทั้งสี่ขาสัญญาณ ดังนั้นไม่ว่าจะมีเซนเซอร์กี่ตัวก็ตาม สามารถรับค่าและเก็บไว้ใน Excel ได้หมดเลย ข้อจำกัดการรับสัญญาณของ PLX-DAQ น่าจะอยู่ที่ 26 ช่องสัญญาณ

สุดท้ายผมขอขอบคุณผู้ร่วมงาน ที่ให้คำแนะนำ และได้คำตอบทุกครั้งไป (มิต้องเอ่ยนาม เพราะท่านทั้งหลายรู้ในใจแล้ว)

แหล่งอ้างอิง (อ่านและฟังไม่รู้เรื่องแต่ ดูวิธีและแกะโปรแกรมจากเขา)

- ดูจากวีดิโอ ที่นี่ https://www.youtube.com/watch?v=LIMyz2GBW28
- ตามจากวีดิโอด้านบนที่บอกไว้ http://www.gioblu.com/tutorials/programmazione/189-arduino-e-i-grafici-in-tempo-reale-su-excel