เราจะเอาสิ่งที่เราเรียนรู้ Arduino จากที่่ผ่านๆมา มาประกอบเป็นรถบังคับ ควบคุมด้วย สมาร์ทโฟน แอนดรอยด์ ผ่านทาง Bluetooth HC-05 กันนะครับ
โปรเจครถบังคับ ขับเคลื่อน 2 ล้อ Arduino กับ แอพแอนดรอยด์ อุปกรณ์ที่ต้องใช้ก็คือ
1. 2WD Smart Car Robot Chassis Kits
2. Arduino UNO R3 - Made in italy
3. Motor Drive Module L298N
4. HC-05 Bluetooth Master Slave
5. สาย Jumper Female to Male ยาว 20cm.
6. สาย Jumper Male to Male ยาว 20cm.
7. รางถ่านแบบ 18650 ใส่ถ่าน 3 ก้อน
8. แบตเตอรี่ลิเธียม 18650 จำนวน 3 ก้อน
9. เสารองแผ่นพีซีบีโลหะแบบเหลี่ยม 6 mm
10. สายไฟแดงดำ ขนาด 22AWG
เริ่มต้นด้วยการ ประกอบ Smart Robot Car Chassis Kit
แนะนำให้ใช้แบตเตอรี่ลิเธียม 18650 (ขนาดใหญ่กว่า แบตเตอรี่ ขนาด AA ปรกติ) แรงดันไฟเฉลี่ย 3.7V (3400 mAh) จำนวน 3 ก้อน ได้ไฟรวมประมาณ 11.1 โวลต์ และ ก็ต้องเปลี่ยน รางถ่าน เป็นแบบ 18650 ใส่ถ่านได้ 3 ก้อน แบบวงจรอนุกรม ด้วย
รุูจัก : Li-ion (ลิเธี่ยมไอออน) และ Li-Mn (ลิเธี่ยมแมงกานีส หรือเรียกอีกอย่างว่า High Drain) ขนาดของตัวจะมาเป็นรหัสครับเช่น 18650 ข้อดี คือ สามารถชาร์จได้ แต่ ไม่ควรใช้จนไฟหมดหรือแรงดันต่ำกว่าที่กำหนดเอาไว้เพราะจะทำให้เซลล์แบตฯ เสียหายไม่สามารถนำมาใช้ได้อีก
V คือ Volt (โวลต์) เป็นค่าความต่างศักดิ์ไฟฟ้า คล้ายๆแรงดันไฟ นึกภาพถึง แรงดันน้ำ แรงดันลม ความดันเลือด (เป็นแรงดันไฟ หรือ แรงดันไฟฟ้า)
mAh คือ m = มิลลิ , A = แอมป์ , h = ชั่วโมง หมายถึง ถ่านก้อนนี้สามารถจ่ายกระแสได้ กี่มิลิแอมป์ ใน 1 ชั่วโมง (เป็นความจุไฟ หรือ กระแสไฟฟ้า)
เริ่มด้วย ต่อวงจร Arduino UNO กับ L298N Motor Driver ตามรูปการต่อวงวงจร
นำไฟจากแบตเตอรี่ลิเธียม 18650 ไปต่อตรงกับ L298N Motor Driver (ไม่ต่อกับ พอร์ต Power Supply ของ บอร์ด Arduino UNO R3 ) และ นำไฟ 5 โวลต์ ที่ออกจาก L298N Motor Driver ต่อออกไปเลี้ยง บอร์ด Arduino UNO R3 ตามรูปการต่อวงวงจร
*** VCC ของ Arduino UNO R3 คือ 5V ***
เรียนรู้เพิ่มเติมตามลิงค์นี้ http://robotsiam.blogspot.com/2016/08/l298n-motor-driver-connect-arduino-r3.html
*** ถ้า HC-05 ไม่ทำงาน ให้ ทดลอง กดที่ปุ่ม BUTTON SWITCH ของ HC-05 ***
เรียนรู้เพิ่มเติมตามลิงค์นี้ http://robotsiam.blogspot.com/2016/08/hc-05-bluetooth-module.html
เปิดโปรแกรม Arduino (IDE) เขียน โค้ด และ Upload ไปยังบอร์ด Arduino UNO ดังนี้
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(9, 10);
int dir1PinA = 2;
int dir2PinA = 3;
int speedPinA = 6;
int dir1PinB = 4;
int dir2PinB = 5;
int speedPinB = 7;
void setup()
{
Serial.begin(9600);
pinMode(dir1PinA,OUTPUT);
pinMode(dir2PinA,OUTPUT);
pinMode(speedPinA,OUTPUT);
pinMode(dir1PinB,OUTPUT);
pinMode(dir2PinB,OUTPUT);
pinMode(speedPinB,OUTPUT);
pinMode(8,OUTPUT);
digitalWrite(8, HIGH);
Serial.begin(9600);
BTSerial.begin(9600);
}
void loop()
{
if (BTSerial.available())
Serial.write(BTSerial.read());
if (Serial.available())
BTSerial.write(Serial.read());
if (BTSerial.available() > 0) {
int inByte = BTSerial.read();
int speed;
switch (inByte) {
case 'F':
analogWrite(speedPinA, 255);
analogWrite(speedPinB, 255);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinA, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 1 Forward");
Serial.println("Motor 2 Forward");
Serial.println(" ");
break;
case 'S':
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Stop");
analogWrite(speedPinB, 0);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Stop");
Serial.println(" ");
break;
case 'B':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back");
Serial.println(" ");
break;
case 'L':
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Left");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 2 Left");
Serial.println(" ");
break;
case 'R':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Right");
analogWrite(speedPinB, 0);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Right");
Serial.println(" ");
break;
case 'I':
analogWrite(speedPinA, 150);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Forward L");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 2 Forward L");
Serial.println(" ");
break;
case 'G':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Forward R");
analogWrite(speedPinB, 150);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 2 Forward R");
Serial.println(" ");
break;
case 'J':
analogWrite(speedPinA, 200);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back L");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back L");
Serial.println(" ");
break;
case 'H':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back R");
analogWrite(speedPinB, 200);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back R");
Serial.println(" ");
break;
default:
for (int thisPin = 2; thisPin < 11; thisPin++)
{
digitalWrite(thisPin, LOW);
}
}
}
}
ประโยค switch หนึ่งประโยคจะมีกี่ case ก็ได้ หรือไม่มีเลยก็ได้ และอาจมี default เป็นตัวเลือกเสริม
ประโยคคำสั่ง break
เป็นคำสั่งที่ใช้ในการหลุดออกจากเงื่อนไข โดยไม่ต้องทำงานจนจบบล๊อกของคำสั่ง
การนำคำสั่ง break มาซ้อนไว้ใน case ต่าง ๆ ของคำสั่ง switch จะช่วยให้โปรแกรมไม่ล่วงล้ำเข้าไปทำใน case ที่อยู่ถัดไป แต่ถ้าไม่มีประโยคคำสั่ง break เมื่อทำ case ใด ๆ เสร็จเรียบร้อยแล้ว คอมไพล์เลอร์ก็จะให้ไปทำใน case ที่อยู่ถัดไปเรื่อย ๆ จนกว่าจะจบบล๊อกของประโยคคำสั่ง switch จากนั้นจะทำงานต่อไป ในประโยคคำสั่งที่อยู่ถัดไป
SoftwareSerial BTSerial(9, 10);
int dir1PinA = 2;
int dir2PinA = 3;
int speedPinA = 6;
int dir1PinB = 4;
int dir2PinB = 5;
int speedPinB = 7;
void setup()
{
Serial.begin(9600);
pinMode(dir1PinA,OUTPUT);
pinMode(dir2PinA,OUTPUT);
pinMode(speedPinA,OUTPUT);
pinMode(dir1PinB,OUTPUT);
pinMode(dir2PinB,OUTPUT);
pinMode(speedPinB,OUTPUT);
pinMode(8,OUTPUT);
digitalWrite(8, HIGH);
Serial.begin(9600);
BTSerial.begin(9600);
}
void loop()
{
if (BTSerial.available())
Serial.write(BTSerial.read());
if (Serial.available())
BTSerial.write(Serial.read());
if (BTSerial.available() > 0) {
int inByte = BTSerial.read();
int speed;
switch (inByte) {
case 'F':
analogWrite(speedPinA, 255);
analogWrite(speedPinB, 255);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinA, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 1 Forward");
Serial.println("Motor 2 Forward");
Serial.println(" ");
break;
case 'S':
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Stop");
analogWrite(speedPinB, 0);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Stop");
Serial.println(" ");
break;
case 'B':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back");
Serial.println(" ");
break;
case 'L':
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Left");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 2 Left");
Serial.println(" ");
break;
case 'R':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Right");
analogWrite(speedPinB, 0);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Right");
Serial.println(" ");
break;
case 'I':
analogWrite(speedPinA, 150);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Forward L");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 2 Forward L");
Serial.println(" ");
break;
case 'G':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
Serial.println("Motor 1 Forward R");
analogWrite(speedPinB, 150);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 2 Forward R");
Serial.println(" ");
break;
case 'J':
analogWrite(speedPinA, 200);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back L");
analogWrite(speedPinB, 255);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back L");
Serial.println(" ");
break;
case 'H':
analogWrite(speedPinA, 255);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back R");
analogWrite(speedPinB, 200);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back R");
Serial.println(" ");
break;
default:
for (int thisPin = 2; thisPin < 11; thisPin++)
{
digitalWrite(thisPin, LOW);
}
}
}
}
เพิ่มเติมการเรียนรู้ ภาษา C
นอกจากการใช้คำสั่ง if เพื่อกำหนดเงื่อนไขเพื่อให้โปรแกรมเลือกที่จะทำงานสายงานใดแล้ว ในภาษา C ยังมีคำสั่ง switch อีกคำสั่งหนึ่ง เพื่ออำนวยความสะดวกแก่ผู้เขียนโปรแกรม ในการที่นำมาใช้แทนคำสั่ง if ที่ซ้อนกันหลาย ๆ ชั้น โดยที่คำสั่ง switch จะนำค่าของตัวแปรที่อยู่หลังคำสั่ง switch มาเปรียบเทียบกับค่าที่อยู่หลัง case แต่ละคำสั่ง ถ้าตรงกัน ก็จะทำสายงานที่อยู่ใน case นั้น ๆ แต่ถ้าไม่ตรงกับ case ใด ๆ เลย จะทำหลังคำสั่ง default โดยมีรูปแบบประโยคคำสั่งดังนี้
นอกจากการใช้คำสั่ง if เพื่อกำหนดเงื่อนไขเพื่อให้โปรแกรมเลือกที่จะทำงานสายงานใดแล้ว ในภาษา C ยังมีคำสั่ง switch อีกคำสั่งหนึ่ง เพื่ออำนวยความสะดวกแก่ผู้เขียนโปรแกรม ในการที่นำมาใช้แทนคำสั่ง if ที่ซ้อนกันหลาย ๆ ชั้น โดยที่คำสั่ง switch จะนำค่าของตัวแปรที่อยู่หลังคำสั่ง switch มาเปรียบเทียบกับค่าที่อยู่หลัง case แต่ละคำสั่ง ถ้าตรงกัน ก็จะทำสายงานที่อยู่ใน case นั้น ๆ แต่ถ้าไม่ตรงกับ case ใด ๆ เลย จะทำหลังคำสั่ง default โดยมีรูปแบบประโยคคำสั่งดังนี้
ประโยค switch หนึ่งประโยคจะมีกี่ case ก็ได้ หรือไม่มีเลยก็ได้ และอาจมี default เป็นตัวเลือกเสริม
ประโยคคำสั่ง break
เป็นคำสั่งที่ใช้ในการหลุดออกจากเงื่อนไข โดยไม่ต้องทำงานจนจบบล๊อกของคำสั่ง
ตัวอย่างตามโค้ดด้านบน เช่น
switch (inByte) {
case 'F':
analogWrite(speedPinA, 500);
analogWrite(speedPinB, 500);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinA, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 1 Forward");
Serial.println("Motor 2 Forward");
Serial.println(" ");
break;
case 'H':
analogWrite(speedPinA, 500);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back R");
analogWrite(speedPinB, 200);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back R");
Serial.println(" ");
break;
default:
case 'F':
analogWrite(speedPinA, 500);
analogWrite(speedPinB, 500);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir1PinB, HIGH);
digitalWrite(dir2PinA, HIGH);
digitalWrite(dir2PinB, LOW);
Serial.println("Motor 1 Forward");
Serial.println("Motor 2 Forward");
Serial.println(" ");
break;
case 'H':
analogWrite(speedPinA, 500);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
Serial.println("Motor 1 Back R");
analogWrite(speedPinB, 200);
digitalWrite(dir1PinB, LOW);
digitalWrite(dir2PinB, HIGH);
Serial.println("Motor 2 Back R");
Serial.println(" ");
break;
default:
การนำคำสั่ง break มาซ้อนไว้ใน case ต่าง ๆ ของคำสั่ง switch จะช่วยให้โปรแกรมไม่ล่วงล้ำเข้าไปทำใน case ที่อยู่ถัดไป แต่ถ้าไม่มีประโยคคำสั่ง break เมื่อทำ case ใด ๆ เสร็จเรียบร้อยแล้ว คอมไพล์เลอร์ก็จะให้ไปทำใน case ที่อยู่ถัดไปเรื่อย ๆ จนกว่าจะจบบล๊อกของประโยคคำสั่ง switch จากนั้นจะทำงานต่อไป ในประโยคคำสั่งที่อยู่ถัดไป
จากนั้น ทดลองนำ โทรศัพท์มือถือ แอนดรอยด์ เปิด บลูทูธ แล้ว ดาวน์โหลดและติดตั้ง โปรแกรม Arduino Bluetooth RC Car ดังนี้
สังเกตุ เมื่อเปิดขึ้นมา รูปวงกลมซ้ายมือ จะเป็นสีแดง
คลิกที่ ไอคอนเฟือง ขวามือสุด แล้ว เลือก Connect to car
เลือก HC-05 (ถ้าถามหา พาสเวิร์ด ให้คีย์ 1234)
สังเกตุ รูปวงกลมซ้ายมือเป็นสีเขียวแสดงว่า โปรแกรมสามารถใช้งานได้แล้ว ทดลองควบคุมดูเลยครับ
วีดีโอผลลัพธ์การทำงานของ โปรเจครถบังคับ ขับเคลื่อน 2 ล้อ Arduino กับ แอพแอนดรอยด์