
เหตุผลที่ต้องทำ
พี่อยากทำคอนโทรลยูนิตที่ใช้ WiFi ทำงาน จะได้ไม่ต้องมีสายระโยงระยาง และถูกที่สุดเท่าที่จะทำได้ โปรเจกต์นี้พี่จัดการควบคุมอุปกรณ์เอาท์พุตได้ถึง 10 ตัว (เปิด/ปิด) และรับอินพุตได้ถึง 8 ช่องเพื่อตรวจสอบสภาพแวดล้อม
WiFi add-on ที่เข้ากับ Arduino ได้และถูกสุดตอนนั้นคือ ESP-01 จาก Espressif Inc หาซื้อได้ง่ายๆ ใน eBay ราคาถูกมาก ของพี่ซื้อมาแค่ 1.42 ปอนด์เอง
น้องต้องไปดาวน์โหลดคู่มือการโปรแกรม (ไฟล์ pdf) จากเว็บ Espressif.com มาไว้ด้วยนะ ชื่อไฟล์คือ 4a-esp8266_at_instruction_set_en.pdf
คู่มือนี้ครอบคลุมทุกเวอร์ชันของชิปเซ็ตเมื่อใช้เป็นโมเด็ม ซึ่งเป็นสิ่งที่เราจะทำกัน
เราจะใช้ Arduino เป็นบอร์ดควบคุมหลัก เพราะมันมีพอร์ตอินพุต/เอาท์พุตให้ใช้พร้อม และมีภาษาสำหรับควบคุมอยู่แล้ว Arduino มีหลายเวอร์ชัน แต่พี่สร้างโปรเจกต์นี้บน UNO R3 เพราะมันพื้นฐานสุดและถูกสุด (แม้จะไม่เล็กสุดก็ตาม)
พี่ก็ซื้อบอร์ดนี้จาก eBay เช่นกัน ราคา 3.20 ปอนด์
แต่โค้ดน่าจะใช้ได้กับทุกเวอร์ชันนะ เพราะเราใช้แค่คำสั่งพื้นฐานๆ เท่านั้น มันจะเปิดใช้งานดิจิตอลเอาท์พุตทั้งหมดและอ่านค่าจากอนาล็อกอินพุต
แม้ว่าจะมีไลบรารีสำหรับ Arduino เยอะแยะ แต่ไม่มีไลบรารีไหนทำงานบน UNO ได้เลย เพราะมันมีฮาร์ดแวร์ซีเรียลพอร์ตแค่พอร์ตเดียว ซึ่งพอร์ตนั้น USB ใช้เชื่อมต่อกับ PC สำหรับการอัพโหลดโปรแกรม ดังนั้นพี่ต้องใช้ดิจิตอลพอร์ตสองพอร์ต และใช้ซอฟต์แวร์ชื่อ SoftwareSerial.h แทน ซึ่งเป็นส่วนหนึ่งของโปรแกรมหลัก Arduino
บอร์ด WiFi ESP-01 จะเชื่อมต่อกับ Arduino ผ่านการต่อสายตามนี้ การต่อสายอื่นๆ ที่พี่ใช้มีแค่สายไฟเลี้ยง (บอร์ดใช้ไฟ 3.3v จากบอร์ด Arduino) ซึ่งก็ต่อไปที่พิน Chip enable เพื่อเปิด WiFi และสายกราวด์ (0 โวลต์)
พี่เลี่ยงการใช้ฟีเจอร์โปรแกรมของ ESP-01 เพื่อให้มันง่ายๆ และใช้เฟิร์มแวร์ที่มากับชิปเซ็ตเลย
ฮาร์ดแวร์
เนื่องจาก UNO R3 มีพื้นที่บนบอร์ดสำหรับเสียบซ็อกเก็ตเพื่อเชื่อมต่อกับอุปกรณ์อื่น พี่ก็ใช้จุดนี้เพื่อต่อบอร์ดเข้ากับไฟเลี้ยงและทำเป็นที่ยึดสำหรับบอร์ด ESP-01 ด้วย พี่ใช้สตริปเชื่อมต่อมาตรฐานสำหรับซ็อกเก็ตและบัดกรีมันไว้ใต้บอร์ดให้เสมอกับตัว UNO ทำให้ได้ฐานที่แข็งแรงสำหรับที่ยึด บอร์ด ESP-01 มี 8 พิน ดังนั้นพี่ต้องใช้ซ็อกเก็ตอีกชุดเพื่อเชื่อมต่อมันเข้ากับบอร์ดโดยไม่ต้องบัดกรีตรงๆ ซึ่งต้องใช้พินแบบเดี่ยวสองชุดติดตั้งบนสตริปบอร์ด โดยให้ด้านที่มีทองแดงหงายขึ้นด้านบน เพื่อที่พินด้านสั้นจะได้บัดกรีเข้ากับแผ่นทองแดงและเหลือพื้นที่สำหรับเดินสายระหว่างซ็อกเก็ต ESP-01 กับพิน

พี่ยังใช้กาวอีพอกซีติดลวดทองแดงหนาๆ ทำเป็นที่ยึดเพื่อแขวนบอร์ด ESP-01 ไว้เหนือบอร์ด Arduino ลวดทองแดงจะพันรอบแต่ไม่สัมผัสพินที่อยู่ด้านล่างของสตริปบอร์ด หลังจากที่บัดกรีพินเรียบร้อยแล้ว อีกด้านหนึ่งของลวดจะไปยึดกับด้านใดด้านหนึ่งของซ็อกเก็ตที่ถือ ESP-ไว้ โดยใช้กาวอีพอกซียึดอีกครั้ง

วิธีนี้ทำให้โครงสร้างแข็งแรง ที่ยึดสามารถถอดออกจากบอร์ด Arduino ได้อย่างปลอดภัยเมื่อไหร่ก็ได้ และบอร์ด ESP-01 ก็สามารถถอดออกได้ถ้ามันเสียหรือต้องเอาไปใช้กับโปรเจกต์อื่น
การต่อสายมีดังนี้:-

ESP-01 พิน 1 ต่อกับ GND บนที่ยึด Arduino ESP-01 พิน 5 ต่อกับ 3.3v บนที่ยึด Arduino ESP-01 พิน 7 ต่อกับ 3.3v บนที่ยึด Arduino ESP-01 พิน 8 ต่อกับ Digital Port 2 บน Arduino (ผ่านสายที่บัดกรีกับซ็อกเก็ตและพินเดี่ยวที่เสียบเข้าไปในซ็อกเก็ต Arduino)
ESP-01 ขา 4 ต่อเข้ากับ Digital Port 3 บน Arduino (ผ่านสายไฟที่บัดกรีกับซ็อกเก็ต แล้วก็เอา Single Pin ไปเสียบลงซ็อกเก็ต Arduino)

ห้ามใช้ พอร์ต RX, TX บนบอร์ดเด็ดขาดนะน้อง เพราะมันไว้สำหรับ USB อยู่ ถ้าใช้ผิดอาจพัง ESP-01 ได้เลย ห้ามใช้ แรงดัน +5v ไฟเลี้ยงด้วยนะ ไม่งั้น ESP-01 ก็อาจจะช็อตได้เหมือนกัน ที่เหลือก็แค่เรื่องซอฟต์แวร์ล้วนๆ!

LED ในรูปนั่นเอาไว้แค่ทดสอบว่าเอาต์พุตทำงานมั้ยเท่านั้นแหละ น้องต้องต่อตัวต้านทาน (Resistor) 1K ระหว่าง LED กับขาเอาต์พุตนะ อีกข้างของ LED ก็ต่อลง GND ของบอร์ด Arduino สีตัวต้านทานในรูปอาจจะไม่ตรงตามจริง นี่เป็นข้อจำกัดของโปรแกรม Fritzing นะจ๊ะ
ส่วนขา Analog (A0-A5) ก็ปล่อยว่างไว้เลย ไว้ใช้รับอินพุตจากอุปกรณ์อื่นหรือสวิตช์ ไม่จำเป็นต้องต่ออะไรเพื่อทดสอบบอร์ดหรือซอฟต์แวร์ เพราะมันจะคืนค่ามาให้เองแม้จะไม่ต่ออะไรเลย
ซอฟต์แวร์
ต้องมี Arduino IDE (Integrated Development Environment) ก่อนนะ ฟรีโหลดได้จากเว็บ Arduino.cc ส่วนโปรแกรม .ino ที่พี่เขียนไว้ และโปรแกรม TCPclient สำหรับควบคุมบอร์ดผ่าน WiFi จาก PC, Android หรือ iPhone ก็ฟรีเหมือนกัน หาโหลดได้จากช่องทางปกติของแต่ละแพลตฟอร์ม (เช่น Google Play) หรือเสิร์ชหาใน Google.com ได้เลย
ต้องติดตั้งโปรแกรม IDE ลงใน PC หรือเครื่อง Linux ก่อน เพื่อจะได้อัปโหลดโปรแกรมลงบอร์ด UNO
บอร์ดรุ่น R3 จะมีซ็อกเก็ต USB บนบอร์ดให้ ใช้สาย data มาตรฐาน (อย่าใช้สายชาร์จ) แบบที่ใช้กับมือถือสมัยใหม่ต่อเข้าไป อีกข้างก็เสียบเข้ากับพอร์ต USB 2 บน PC
ใช้สายนี้สำหรับโปรแกรมบอร์ดนะ แต่เวลาบอร์ดทำงานจริงๆ ต้องใช้แบตเตอรี่ 7-12 โวลต์ ต่อเข้ากับพอร์ตไฟเลี้ยงของบอร์ด UNO เพื่อจ่ายไฟให้ทั้งสองบอร์ด มันทำงานได้จริงๆ นะ ไม่ต้องสนคอมเมนต์ในฟอรั่มต่างๆ
พอบอร์ดเชื่อมต่อกับซอฟต์แวร์ IDE ได้แล้ว ก็คัดลอกโปรแกรมไปวางใน "sketch" ใหม่ (เขาเรียกโปรแกรมแบบนี้) ได้เลย พอเซฟใน IDE มันจะแปลงเป็นไฟล์ .ino ให้ แล้วน้องจะตั้งชื่ออะไรก็ได้ตามใจ
โค้ดโปรแกรม
[code]
// You may use on board LED connected to pin 13
// Elminated delays and serial monitor commands, reduced response time to 5 seconds.
#include <SoftwareSerial.h>
int serialRx = 2; // software serial RX TX
int serialTx = 3; // ESP-01 RX goes to this port, TX goes to Port 2
int d = 0;
// ESP-01 CH_PD pin 7 Must go to 3.3v, not +5v to enable operation as UART (AP)
// UNO R3 has enough power to make it work.
// No other connections required
String inMsg ; // variable to collect strings
int sensorPin = 8; // set maximum number of pins for the Analouge inputs
int LEDPin = 13 ; // variable to assign pin to LED.
int responseTime = 10; //communication timeout
SoftwareSerial portOne(serialRx, serialTx); // communications port to ESP-01
String Msg = ""; // to collect Analouge input data
void setup() {
// Serial.begin(19200); // for debugging only
String msg ="" ;
portOne.begin(115200); // Start software serial port
delay(200);
sendToWifi("ATE0",100); // set Echo Off. Otherwise echo was ON.
sendToWifi("AT+CWMODE=2",responseTime); // configure as access point on Chinese version
// sendToWifi("AT+CWMODE_CUR = 2",responseTime); // configure as access point (AP)
sendToWifi("AT+CIPMUX=1",responseTime); // configure for multiple connections
// AT+CIPAP=<ip>[,<gateway>,<netmask>]
sendToWifi("AT+CIPAP_CUR=\"192.168.5.1\"",responseTime); // Set IP address for AP
sendToWifi("AT+CIPSERVER=1,80",responseTime); // Create and turn on server on port 80
sendToWifi("AT+CWDHCP_CUR=0,1",responseTime); // Set AP with DHCP ON (required)
//AT+CWSAP=<ssid>,<pwd>,<chl>,<ecn>[,<max conn>][,<ssid hidden>]
// Open AP Port
sendToWifi("AT+CWSAP=\"ESP8266\",\"1234567890\",5,3,1,0",responseTime); // Set Soft AP parameters problems sending " characters
sendToWifi("AT+CIPAP_CUR?",responseTime);
sendToWifi("AT+CIPSTO=600",responseTime); // ตั้งค่า timeout เป็น 10 นาที
pinMode(LEDPin,OUTPUT); // เปิดพอร์ตเพื่อควบคุม LED บนบอร์ด
// //serial.println("Setup is done!");
// delay(2000);
}
void loop() {
//Serial.println("Waiting....");
if(portOne.available()>0){
inMsg = readFromWifi();
//serial.print("Received msg inside loop = ");
inMsg.toUpperCase();
// Serial.println(inMsg);
// ข้อความเป็นตัวพิมพ์ใหญ่
if (inMsg.endsWith("HELLO") ) {
// Serial.println ("Wifi says : Hello");
sendData("Wifi says : Hello\r"); // ส่งการยืนยันคำสั่ง + CR
Send_inputs();
}
else if (inMsg.endsWith("LEDON") ){
digitalWrite(LEDPin,HIGH); // ถ้าเป็นแค่ LEDON ให้เปิด Pin 13
sendData("Wifi says : LEDON\r"); // ส่งการยืนยันคำสั่ง + CR
Send_inputs(); // ส่งค่าแอนะล็อกอินพุต
}
else if (inMsg.endsWith("LEDOFF") ){
String msg = SETLEDS(0) ; // ปิด LED ทั้งหมด
sendData("Wifi says : LEDOFF\r"); // ส่งการยืนยันคำสั่ง + CR
Send_inputs(); // ส่งค่าแอนะล็อกอินพุต
}
else if (inMsg.indexOf("LEDONN",0)>6 ){ // ถ้าคำสั่งคือการตั้งค่าเป็นตัวเลข
d = inMsg.indexOf("LEDONN",0)+6; // ตั้งค่า d ให้เป็นตัวอักษรถัดไปหลังคำสั่ง // string เริ่มต้นที่ศูนย์
int a = 0; // ตัวเลขที่เก็บได้เริ่มต้นที่ศูนย์
int dt = inMsg.length(); // หาตำแหน่งสิ้นสุดสตริง (CR)
if ( d+3 < dt){ // ถ้ามีตัวเลข 4 หลักหลังคำสั่ง
a = inMsg.substring(d, d+4).toInt(); // เก็บตัวเลข 4 หลักในตัวแปร a
String msg ="ERROR";
if ((a > 0) & (a < 1024) ) { // คำสั่งนี้ไม่ใช้สำหรับศูนย์ (ให้ใช้ LEDOFF แทน), ป้องกันกรณีไม่มีตัวเลข
msg = SETLEDS(a) ; // ตั้งค่า LED ทั้งหมดเป็นเปิดหรือปิด, ส่งกลับสตริงไบนารีเป็นการยืนยัน
}
msg = "Wifi says : LEDONN="+msg+"\r";
sendData(msg ); // ส่งการยืนยันคำสั่ง + CR
Send_inputs(); // ส่งค่าแอนะล็อกอินพุต
}
}
// delay(9000); // ตั้งดีเลย์ให้ยาวจงใจเพื่อช่วยรวบรวมข้อมูลจากพอร์ตอนุกรม
}
inMsg=""; // ล้างบัฟเฟอร์
delay(800); // ดีเลย์เพื่อป้องกันข้อความซ้ำ
}
void sendToWifi(String command, const int timeout){
// ส่งแค่คำสั่ง, ไม่สนใจการตอบกลับ
String response = "";
// Serial.print("Sent command to ESP8266-E12: ");
// Serial.print(command);
delay(50);
portOne.println(command);
long int time = millis();
while( (time+timeout) > millis())
{
while(portOne.available())
{
char c = portOne.read();
response+=c;
}
}
// Serial.print("Response from ESP8266-E12: ");
// Serial.print(response);
}
String readFromWifi(){
char arrayInMsg[100];
String tempStr;
int count =0;
while( portOne.available() > 0 ){
arrayInMsg[count]= portOne.read();
delay(80);
// ออกลูปถ้าเจอ CR หรือ LF
if ((arrayInMsg[count]=='\n') or (arrayInMsg[count]=='\r')) break;
// เปลี่ยนการเปรียบเทียบอาร์เรย์ของตัวอักษรไม่งั้นจะเกิดข้อผิดพลาด
count++;
}
arrayInMsg[count] = '\0'; // สุดท้ายให้จบด้วย Null
tempStr = String (arrayInMsg); // แปลงเป็นสตริง
tempStr.trim(); // กันไว้ก่อน
// Serial.print(tempStr);
return tempStr;
}
/*
* ชื่อ: sendData
* คำอธิบาย: ฟังก์ชันสำหรับส่งสตริงไปยัง tcp client โดยใช้ cipsend
* พารามิเตอร์:
* คืนค่า: void
*/
void sendData(String str){
String len="";
len+=str.length();
sendToWifi("AT+CIPSEND=0,"+len,responseTime); // เซ็ตคำสั่งเพื่อส่งความยาวข้อมูลสตริงไปยังช่องทาง 0
delay(100);
sendToWifi(str,responseTime);// ส่งสตริง str โดยปิดการส่งด้วย 0 และกลับสู่โหมด AT
delay(100);
do {
delay(100);
} while (portOne.available()==0); // รอจนกว่าจะมีข้อมูลตอบกลับว่า "sent" หรือ "failed"
}
String SETLEDS(int long pulse) {
pulse= pulse << 4; // เลื่อนตัวเลขขึ้นไปที่ pin 4
String response2 = "" ;
for ( int x = 13; x >3 ; x--) { // Pins 13 ลงไปถึง 4
if (pulse & (1 << x)) { // เปรียบเทียบ pulse กับบิตหมายเลขของพอร์ตดิจิทัลเอาต์พุต
digitalWrite(x,HIGH);
response2 = response2 + "1";
// Serial.print("1"); // just for testing
}
else {
digitalWrite(x,LOW);
response2 = response2 + "0";
// Serial.print("0"); // just for testing
}
}
// Serial.println(""); // just for testing
return response2; // Return Binary string
}
// Send Analouge data
void Send_inputs(){
delay (1000);
Msg = Sensor_Read(); // replace Msg with Analouge input string
Msg = "Analoue Reads 0-7:"+ Msg + "\r";
sendData(Msg); // Send Analouge inputs + CR
}
// Read all analouge inputs routine
String Sensor_Read(){
String response3 = "" ;
for (int y = 0; y < sensorPin; y++) { // read ALL analouge inputs
String an = "A" + String(y,DEC); // format for Pin numbers
int f = an.toInt(); // turn string into a Analouge Input pin number
response3 = response3 + String(analogRead(f),DEC); // add value to string
if (y < sensorPin-1){
response3 = response3 +",";
}
}
return response3; // return string of numbers to loop
}
มาดูกันว่าโปรแกรมนี้มันทำอะไรบ้าง
บรรทัดที่ขึ้นต้นด้วย // นั่นคือคอมเมนต์เอาไว้ดีบั๊กตอนเขียนโค้ดน่ะน้อง มันไม่จำเป็นสำหรับโปรแกรมที่รันจริง แถมยังทำให้โปรแกรมช้าลงอีก แต่เจ้าโค้ดพวกนี้แหละที่ช่วยให้เรารู้ว่าแต่ละส่วนของโปรแกรมมันทำงานได้จริงๆ ผ่าน Serial Monitor ถ้าเราเชื่อมต่อ Arduino กับ USB ไว้ มันก็จะส่งข้อมูลกลับมาที่หน้าจอคอมเราให้ดู
ส่วนที่อยู่ก่อน Setup() นั่นคือการตั้งค่า Serial Port สำหรับบอร์ด WiFi และกำหนด Digital Ports ที่จะใช้กับมัน รวมถึงกำหนดพอร์ตสำหรับคำสั่ง LEDON ด้วย
ไลบรารี SoftwareSerial ถูกตั้งค่าเป็น portone เพื่อให้โปรแกรมของเราส่งข้อมูลไปหาบอร์ด WiFi ได้
ในส่วน Setup() นี่แหละที่เราตั้งค่าบอร์ด WiFi ด้วยคำสั่ง AT แบบม็อดแมสมัยก่อนเลย
- ตรงนี้แหละที่น้องจะต้องใส่ พาสเวิร์ด, IP Address ของ AP (Access Point), และ ชื่อ AP (SSID)
- มันยังตั้งค่า DNS ซึ่งจำเป็นต้องมีเพื่อแจก IP Address ให้กับ Client
- และทำให้ SSID ปรากฏให้เห็นด้วย
ในส่วน Loop()
- มันตั้งค่าคำสั่งไว้ 4 คำสั่ง:
HELLO,LEDON,LEDOFF,LEDONNxxxx(โดยที่xxxxคือตัวเลข 4 หลัก ระหว่าง 1 ถึง 1023 ตัวพิมพ์ใหญ่หรือเล็กก็ได้) - ทั้งสี่คำสั่งนี้จะส่งข้อความยืนยันกลับไปหา TCP Client ผ่านฟังก์ชันย่อย
sendDataว่าได้รับและทำงานตามคำสั่งเรียบร้อยแล้ว - ส่วนคำสั่งอื่นๆ ที่ไม่รู้จัก โปรแกรมจะทำเป็นมองไม่เห็นเฉยๆ
- คำสั่งสำหรับ WiFi จะถูกส่งไปประมวลผลที่ฟังก์ชันย่อย
sendToWifi - การอ่านข้อมูลจากบอร์ด WiFi จะทำผ่านฟังก์ชันย่อย
readFromWifi
วิธีใช้ TCP Client
- เชื่อมต่อคอมพิวเตอร์กับบอร์ด WiFi โดยเลือก SSID ที่เราตั้งไว้ และใส่พาสเวิร์ดให้ถูกต้อง คอมควรจะ "เชื่อมต่อ" ได้ และสัญญาณ WiFi จะแรงขึ้น (เพราะมันออกจากโหมดประหยัดพลังงานแล้ว)
- รันโปรแกรม TCP Client (เช่น RealTerm บน PC หรือแอพบน Android)
- ใส่ IP Address ที่เราตั้งไว้ในโปรแกรม Arduino (ในตัวอย่างนี้คือ
192.168.5.1) - ใส่พอร์ต 80 วิธีใส่จะต่างกันไปตามโปรแกรม เช่นใน RealTerm ใส่เป็น
192.168.5.1:80ส่วนในแอพ Android TCP Client อาจจะมีช่องแยกให้ใส่ - จากนั้นโปรแกรมก็จะสามารถเชื่อมต่อกับโปรแกรมบน Arduino ผ่าน TCP/IP ได้แล้ว
- น้องก็สามารถส่ง 4 คำสั่งที่ว่านั้นไปทดสอบได้เลย
