ภาพรวม
“แต่พวกมันจะรบกวนสัญญาณเราได้ยังไง ถ้าไม่รู้ว่า… เรากำลังมา?” – แลนโด คาลริสเซียน
นั่นคงเป็นคำพูดเดียวจากสตาร์วอร์สที่ผมนึกออกเวลาพูดถึงการสื่อสารไร้สาย/วิทยุเนี่ยแหละ
ตอนทำโปรเจกต์ Arduino ตัวนึง ซึ่งเป็นเครื่องวัดระยะอัลตราโซนิกเพื่อให้แน่ใจว่ารถจอดอยู่ในตำแหน่งที่ดีที่สุดในโรงจอด ผมเจอความต้องการที่จะส่งและรับข้อมูลระหว่างอุปกรณ์ Arduino สองตัวแบบไร้สาย ผมจะต่อทั้งสองเครื่องเข้ากับเน็ตเวิร์กแล้วให้มันคุยกันทางนั้นก็ได้นะ แต่ผมอยากเลี่ยงการเชื่อมต่อเน็ตเวิร์กสำหรับตัวนึง – มันไม่สะดวกที่จะลากสายเน็ตเวิร์กไปตรงนั้นน่ะ ผมคิดถึง Arduino Wi-Fi shield แต่ก็รู้สึกว่าไม่คุ้มค่าตังค์สำหรับงานง่ายๆ แบบนี้
ตอนที่ 1
ดังนั้นผมต้องหาวิธีอื่นที่จะให้ Arduino สองตัวคุยกันได้โดยไม่ต้องต่อสายเชื่อมกันทางกายภาพ นี่แหละที่ทำให้ผมตัดสินใจมองไปที่มุมของการสื่อสาร RF
มาดูชิ้นส่วนบางส่วนที่ผมซื้อมาบ้าง (อีกแล้ว ส่วนใหญ่ก็จาก element14 นั่นแหละ)
- 1 x QAM-TX1 AM Transmitter Module
- 1 x QAM-RX2 AM Receiver Module
- 1 x HT-12E Encoder IC
- 1 x HT-12D Decoder IC
หลักการพื้นฐานคือโมดูล AM Transmitter และ Receiver ใช้การส่งสัญญาณ RF AM ความถี่ 433 MHz ดีงาม แต่ผมอยากจะส่งสัญญาณหลายแบบระหว่างสองอุปกรณ์นี้ได้ด้วย ตัวอย่างเช่น ผมอยากจะสื่อสารสถานะของประตูโรงจอดไปยังหน่วยรับ ซึ่งหมายความว่าผมต้องสามารถส่งสัญญาณวิทยุสำหรับ "ประตูปิด" และสัญญาณวิทยุสำหรับ "ประตูเปิด" ได้
หลังจากค้นคว้าเพิ่มอีกนิด ผมก็เจอไอซี Encoder/Decoder สิ่งที่เจ้านี่ทำให้ผมทำได้คือตั้งค่าให้แต่ละอุปกรณ์มีที่อยู่ (address) 8 บิตที่ตรงกัน – เพื่อให้ตัวถอดรหัสฟังแต่สัญญาณที่ตรงกับที่อยู่ที่ตั้งค่าไว้ – และสามารถส่งข้อความประเภทต่างๆ ได้ถึง 16 แบบระหว่างกัน เยี่ยมเป๊ะ
นั่นคือองค์ประกอบพื้นฐาน มีอุปกรณ์ Arduino สองตัว แต่ละตัวมี Encoder หรือ Decoder และมี Transmitter หรือ Receiver ตามลำดับ
สำหรับชิ้นส่วนอิเล็กทรอนิกส์ใดๆ ที่คุณใช้ โดยทั่วไปคุณจะต้องดาวน์โหลด datasheet ออนไลน์ที่บอกคุณเกี่ยวกับแรงดัน/กระแสที่คาดหวัง/สูงสุด, การจัดขา (pinouts) และพฤติกรรมที่คาดหวังของชิ้นส่วนนั้นๆ
ตอนที่ 2
ผมสร้างวงจรส่งดังรูปด้านล่าง:
ขา A0 – A7 ใช้สำหรับตั้งค่าที่อยู่ ผมตั้งค่าที่อยู่ตัวอย่างไว้ตามรูป – ขา A0, A1, A2, A5, A7 เป็น HIGH ส่วนขา A3, A4 และ A6 ดึงลงไปเป็น LOW ตราบใดที่ชุดค่าผสมนี้ตรงกับที่ตั้งไว้บนชิป Decoder ของผม พวกมันก็จะสื่อสารกันได้
ขา DOUT (Data Out) จาก Encoder ส่งสัญญาณออกไปยังขา DATA ของโมดูล Transmitter
ตามคำแนะนำบนตัวส่ง ผมต้องสร้างเสาอากาศยาว 17 ซม. – ผมแค่ใช้ลวดแข็งเส้นเดียวจากสายอีเธอร์เน็ตเก่า ยาว 17 ซม. อันที่จริงคือสายอีเธอร์เน็ตใหม่ที่ผมซื้อมาแยกชิ้นส่วนน่ะ
สำหรับ Encoder ตัวนี้ ตาม datasheet ผมต้องต่อตัวต้านทาน (Resistor) 1.1M โอห์ม (หรือคือตัวต้านทาน 1M และ 100K โอห์มต่ออนุกรมกัน) ระหว่างขา OSC1 และ OSC2
ตอนนี้เหลือแค่ขา TE กับ AD11 ขา TE ต้องถูกตั้งค่าเป็น LOW เมื่อผมต้องการส่งสัญญาณ เพื่อให้ทำได้ ผมใช้ทรานซิสเตอร์แบบ NPN (รุ่น 2N2222A) เมื่อผมใช้ digitalWrite(3, HIGH) บน Arduino และส่งเอาต์พุต +5V ออกทางขา D3 กระแสจะไปที่ขาเบสของทรานซิสเตอร์ ทำให้เกิดการไหลของกระแสจากคอลเลกเตอร์ไปยังอีมิตเตอร์ (ลองเสิร์ชพื้นฐานของทรานซิสเตอร์ดูสักนิด) ดังนั้น เมื่อขา D3 มี +5V ทรานซิสเตอร์จะเปิด และขา TE จะถูกตั้งเป็น LOW ส่งผลให้ Encoder ส่งข้อมูลออกทางขา DOUT ตัวต้านทาน 4.7K โอห์มมีไว้เพื่อลดกระแสจาก Arduino ก่อนจะไปถึงขาเบสของทรานซิสเตอร์นั่นเอง จัดไปวัยรุ่น!
มันส่งข้อมูลอะไรไปบ้าง? ก็คือขา AD8 ถึง AD11 นี่แหละที่เป็นตัวแทนของ 4 บิตข้อมูลที่เราส่งออกไป เนื่องจากแต่ละบิตมี 2 สถานะ (HIGH กับ LOW) ก็เลยได้ทั้งหมด 2^4 (16) ค่าที่เลือกได้ แต่พี่ส่งแค่ 2 ข้อความ เลยต่อแค่ขา AD11 ตัวเดียวเท่านั้น ดังนั้นข้อความที่พี่ส่ง ขา AD8 – AD10 จะถูกเซตเป็น HIGH ตลอด ส่วน AD11 จะเป็น HIGH หรือ LOW ก็ขึ้นอยู่กับผลลัพธ์จากขา D4 บน Arduino นั่นเอง
มาดูโค้ดส่งพื้นฐานกันสักหน่อยดีกว่า วัยรุ่น
#define PIN_TRANSMIT 3
#define PIN_DATA 4
/***************************************************************/
/* Function: setup() */
/***************************************************************/
void setup()
{
Serial.begin(9600);
Serial.println("setup()");
pinMode(PIN_TRANSMIT, OUTPUT);
pinMode(PIN_DATA, OUTPUT);
}
/***************************************************************/
/* Function: loop() */
/***************************************************************/
void loop()
{
Serial.println("loop()");
Serial.println("Transmitting Message 1");
digitalWrite(PIN_DATA, HIGH); // Message 1
digitalWrite(PIN_TRANSMIT, HIGH);
delay(100);
digitalWrite(PIN_TRANSMIT, LOW);
// Wait 10 seconds between messages
delay(10000);
Serial.println("Transmitting Message 2");
digitalWrite(PIN_DATA, LOW); // Message 2
digitalWrite(PIN_TRANSMIT, HIGH);
delay(100);
digitalWrite(PIN_TRANSMIT, LOW);
// Wait 10 seconds between messages
delay(10000);
}
พอเสร็จขั้นตอนนี้และต่อวงจรเบื้องต้นบนเบรดบอร์ดเรียบร้อย ก็ได้ตัวส่งแล้ว ต่อไปก็ไปที่ตัวรับกันเลย
ในวงจรนี้จะเห็นว่าพี่ตั้งขา A3, A4, A6 ลงกราวด์ ทำให้ IC ถอดรหัสมีบิตแอดเดรสเหมือนกับ IC เข้ารหัสเป๊ะ ถ้าบิตพวกนี้ตรงกันตอนได้รับข้อความ IC ถอดรหัสจะเซตขา VT เป็น HIGH – VT = Valid Transmission นี่แหละคือวิธีที่เรารู้ว่า IC ถอดรหัสมันโอเคกับสิ่งที่ได้รับมาแล้ว
Part 3
หลังจากนั้นก็ตรงไปตรงมาเลย – ขา D10 และ D11 บน Arduino ถูกดึงลงกราวด์ด้วยตัวต้านทาน (Resistor) 10K (ตามที่ Arduino แนะนำในหน้า reference ของขาดิจิตอล) เมื่อ VT หรือ D11 ถูกเซตเป็น HIGH โดยตัวถอดรหัส พี่ก็สามารถอ่านสัญญาณ HIGH เหล่านั้นจากขา D10 และ D11 ได้
#define PIN_RECEIVE 10
#define PIN_DATA 11
/***************************************************************/
/* Function: setup() */
/***************************************************************/
void setup()
{
Serial.begin(9600);
Serial.println("setup()");
pinMode(PIN_RECEIVE, INPUT);
pinMode(PIN_DATA, INPUT);
}
/***************************************************************/
/* Function: loop() */
/***************************************************************/
void loop()
{
Serial.println("loop()");
if (digitalRead(PIN_RECEIVE) == HIGH)
{
Serial.println("Receiving Message");
if (digitalRead(PIN_DATA) == HIGH)
Serial.println("- Message 2");
else
Serial.println("- Message 1");
}
delay(100);
}
โค้ดรับนี่ง่ายกว่าเล็กน้อย เราตรวจสอบว่าขา VT ถูกเซตเป็น HIGH เพื่อบ่งชี้ว่าการส่งข้อมูลนั้นใช้ได้หรือเปล่า ถ้าใช่ เราก็ตรวจสอบขา Data ต่อไป ถ้าน้องสงสัยว่าทำไมข้อความดูเหมือนจะสลับกันในวงจรรับ นั่นเป็นเพราะการออกแบบวงจรส่งนั่นเอง ถ้าย้อนกลับไปดูวงจรส่ง ขา Data AD8 – AD11 มันเริ่มต้นที่สถานะ HIGH ถ้าพี่อยากเปลี่ยนสถานะขา Data สักขา พี่ต้องต่อขานั้นลงกราวด์เพื่อให้ได้สถานะ LOW พี่ทำยังไง? ก็เซตขา Data ของ Arduino เป็น HIGH ซึ่งจะทำให้ทรานซิสเตอร์เปิด (ON) ส่งผลให้ขา Data AD11 กลายเป็น LOW นั่นเอง ดังนั้นตอนที่พี่เซตขาของ Arduino เป็น HIGH จริงๆ แล้วพี่กำลังส่งบิตข้อมูลที่เป็น LOW อยู่ นี่คือสาเหตุที่ในวงจรรับ พี่ต้องตีความว่าการอ่านค่า LOW บนขา Data คือ "ข้อความที่ 1" พี่อาจออกแบบวงจรส่งใหม่ให้กลับสัญญาณจาก Arduino ก็ได้ เพื่อให้เอาต์พุต LOW ของ Arduino เท่ากับบิต Data LOW แต่พี่ก็โอเคกับวิธีนี้แล้วล่ะ สู้งานนะน้อง
ระบบส่งข้อมูลไร้สายราคาประหยัด
โปรเจกต์นี้เป็นเหมือนพิมพ์เขียวทางเทคนิคสำหรับสร้างการเชื่อมโยงไร้สายระยะไกลที่เสถียรระหว่าง Arduino สองตัว โดยใช้โมดูล RF 433MHz แม้ว่าตัวอย่างนี้จะใช้ชุดเข้ารหัส/ถอดรหัส HT-12E/D เพื่อความง่าย แต่โปรเจกต์ระดับเทพกว่านี้สามารถใช้ไลบรารีอย่าง RadioHead (RH_ASK) ได้เลย ไลบรารีพวกนี้มันจัดการเรื่องยุ่งยากระดับบิตให้หมด เช่น การซิงค์สัญญาณและพรีแอมเบิล ที่จำเป็นต้องมีเพื่อส่งข้อมูลผ่านสภาพแวดล้อม RF ที่มีสัญญาณรบกวน โดยไม่ต้องพึ่งเราเตอร์ WiFi เลยสักนิด จุดเด่นของไลบรารีแบบนี้คือ แพ็กเกจข้อมูลที่ตรวจสอบความถูกต้องด้วย checksum ทุกครั้งที่ส่งข้อมูลจะมี CRC16 checksum ไปด้วย และ Arduino ฝั่งรับจะทิ้งแพ็กเกจที่ข้อมูลเพี้ยนเพราะสัญญาณรบกวนอัตโนมัติ รับประกันความสมบูรณ์ของข้อมูล 100%
ประสิทธิภาพ
วงจรง่ายๆ นี้ถูกออกแบบมาให้มีความหน่วง (latency) ต่ำกว่า 100ms ทำให้ได้ลิงก์ไร้สายที่ "ตอบสนองทันที" เหมาะสำหรับโปรเจกต์รีโมทคอนโทรล เช่น เปิดปิดประตูโรงรถ หรือระบบส่งข้อมูลจากสถานีตรวจอากาศ
สรุป
ของเจ๋งมากๆ เลยตัวนี้ มันทำให้ Arduino สองตัวคุยกันได้โดยไม่ต้องต่อเข้าระบบเน็ตเวิร์กเดียวกัน หรือต้องลากสายข้อมูลให้ยุ่งยากเลย
~ ไมค์