วันศุกร์ที่ 4 เมษายน พ.ศ. 2557

ตอนที่ 2 : Ketai กับ Fleet Monitoring

ผมคิดอยู่ว่าจะนำเสนอ Ketai ยังไงให้น่าติดตาม ครั้นจะมาเล่าทีละส่วนของมันก็อาจจะทำให้น่าเบื่อ ก็เลยคิดว่าจะนำเสนอในลักษณะเป็นการนำไปใช้งาน น่าจะดีกว่า ก็เลยทดลองเขียนบทความออกมาในลักษณะนี้ครับ

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

สิ่งที่ต้องเรียนรู้เพิ่มเติม คือ การสร้างโปรแกรมบน Server เพื่อรองรับค่าที่ส่งมา บันทึกลงใน mySQL

Ketai ทำหน้าที่อ่านตำแหน่งโดยไม่ใช้ GPS แต่จะอ่านตำแหน่งจากข้อมูลของ WIFI Network Location ซึ่งจะมีความแม่นยำอยู่ในระดับประมาณ 40 - 80 เมตร เทียบกับ GPS ที่ประมาณ 5 เมตร

Arduino อ่านการเปิด-ปิดประตู และระดับน้ำมัน มาประกอบกับข้อมูลตำแหน่ง

แพ็กข้อมูลแล้วก็ส่งไปที่ Server ผ่าน Internet

เริ่มที่ Ketai

ทำการ download Ketai libaray มาไว้ใน Processing ก่อนครับ จากนั้นก็เขียนโปรแกรมแบบ Android
ก่อน Run ให้กำหนดใช้ ACCESS_FINE_LOCATION ในเมนู Android -> Sketch Permission เสียก่อน
การทดสอบกับ Android บางตัวจะไม่มีในส่วนของการอ่านค่า location จาก Network ทำให้ไม่สามารถแสดงค่าตำแหนงได้ สำหรับมือถือส่วนใหญ่ใช้งานได้


import ketai.sensors.*;

KetaiLocation location;                     //   เรียกใช้ location library
double longitude, latitude, altitude;  //   เก็บค่า ตำแหน่ง
float accuracy;                                    //    ความแม่นยำ

void setup(){
  orientation(LANDSCAPE);
  textAlign(CENTER,CENTER);
  textSize(36);
  location = new KetaiLocation(this);   // เปิดการใช้งาน
}

void draw() {
  background(128);
  if(location.getProvider() == "none")
    text("Location data is unavailable. \n" +
      "Please check your location settings.", width/2,height/2);
  else
    text("Latitude: " + latitude + "\n" +
      "Longitude: " + longitude + "\n" +
      "Altitude: " + altitude + "\n" +
      "Accuracy: " + accuracy + "\n" +
      "Provider: " + location.getProvider(), width/2, height/2);
}

void onLocationEvent(double _latitude, double _longitude, double _altitude, float _accuracy){
  longitude = _longitude;
  latitude = _latitude;
  altitude = _altitude;
  accuracy = _accuracy;
  println("lat/lon/alt/acc: " + latitude + "/" + longitude + "/" + altitude + "/" + accuracy);
}

ผลการ Run แสดงตำแหน่ง อันนี้ใช้ WIFI Network


เมื่อเปิด GPS

ต่อไปก็จะเป็นการเตรียมการที่ Server

การติดตั้ง XAMPP สำหรับทำ Server บน PC ให้ดาวน์โหลดไฟล์


ให้เลือก XAMPP for Widows แล้วติดตั้ง ขอให้ระบุตำแหน่งที่ติดตั้งเป็น C:\ เพื่อความสะดวก
เมื่อติดตั้งเสร็จให้ทดสอบโดยพิมพ์ http://localhost ที่ Browser จะเห็นหน้าตาแบบนี้ เป็นอันใช้ได้ ตอนนี้เรามี Server ใช้แล้ว


การบันทึกข้อมูลต้องใช้ PHP ก็เลยต้องทดสอบการทำงานของ PHP กันก่อน ให้กดที่ phpinfo() จะได้หน้าตานี้


ในส่วนของฐานข้อมูล จะยังไม่ใช้ mySQL เพราะจะทำให้เนื้อหายืดยาวเกินไป ก็เลยจะเก็บข้อมูลไว้ใน Text file แทน ซึ่งต้องเขียนไฟล์ PHP ขึ้น ตามนี้ แล้วนำไปวางไว้ที่ C:\xampp\htdocs\xampp 

<?php
// Geolocation Device Locator PHP Script
// Writing to a text file on a web server

if(isset($_GET['get']))
{
 $filename = $_GET['get'].".txt";
 if(file_exists($filename))
{
 $file = file_get_contents($filename);
 echo $file;
} else
echo "ERROR! No location found for " . $_GET['get'];
}
//if the request is an update,we dump the location into a file
// named after the device making the request
 else if(isset($_GET['update']) && isset($_GET['location']))
{
 $fh =fopen($_GET['update'].".txt", "w");
if($fh == FALSE)
{
echo "ERROR. Cannot open file on server.";
return;
}
 if(fwrite($fh, $_GET['location']."\n") == FALSE)
echo "ERROR. Writing to file.";
 if(fclose($fh) == FALSE)
echo "ERROR. Closing file,";
}
?>

ทดลองให้ PHP ทำงาน ด้วยการพิมพ์ http://localhost/xampp/location.php?get=Test ใน Browser จะแสดงข้อความว่า 

ERROR! No location found for Test

ถ้าได้ตามนี้ ก็โอเคครับ ต่อไปจะเพิ่มการบันทึกตำแหน่งไปที่ Server

ให้เพิ่มโค๊ดต่อจากโปรแกรมเดิม จุดที่สำคัญคือ 

serverIP เป็น IP ของเครื่อง PC ที่ติดตั้ง XAMPP นะครับ ถ้าไม่รู้ใหัเข้าที่ Command Prompt ใช้คำสั่ง ipconfig ดูที่ IPv4 Address

myName เป็นการแยกไฟล์เก็บข้อมูลของเครื่อง Android แต่ละเครื่อง ถ้ามี 2 เครื่องขึ้นไปให้ตั้ง myName ที่ไม่ซ้ำกัน 

String myName = "Supot"; // ชื่อของเครื่อง Android ตั้งไม่ให้ซ้ำกัน
String serverMessage = "";
String serverIP = "192.168.1.14";  // IP Address ของ Server อันนี้เป็นของเครื่องผม

void mousePressed(){
  String url = "http://" + serverIP + "/xampp/location.php?update=" + myName +
    "&location="+latitude+","+longitude+","+altitude;
    
  // Update ข้อมูลตำแหน่งของเครื่องไปที่ Server ชื่อเครื่องระบุที่ myName
  
  loadStrings(url);  

  url = "http://" + serverIP + "/xampp/location.php?get=" + myName;
  
  // get ข้อมูลตำแหน่ง ระบุชื่อเครื่องที่ myName
  
  String results[] = loadStrings(url);
  if(results.length > 0)
    serverMessage = results[0];
    
}



และแก้ไขในส่วนขอ void draw() ตามนี้

void draw() {
  background(128);
  if(location.getProvider() == "none")
    text("Location data is unavailable. \n" +
      "Please check your location settings.", width/2,height/2);
  else
    text("Latitude: " + latitude + "\n" +
      "Longitude: " + longitude + "\n" +
      "Altitude: " + altitude + "\n" +
      "Accuracy: " + accuracy + "\n" +
      "Provider: " + location.getProvider() + "\n" +
      "Last Servver Message: " + serverMessage, width/2, height/2);
}


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

ไฟล์ Location.php ต้องแก้ไขเล็กน้อย เพื่อให้สามารถเขียนเพิ่มได้ บรรทัดที่ 19

จาก
 $fh =fopen($_GET['update'].".txt", "w");

เป็น
 $fh =fopen($_GET['update'].".txt", "a");

(w = write, a = append)

ส่วนการอ่านข้อมูลจากไฟลที่บันทึกให้พิมพ์ที่ Browser 


เปลี่ยน Supot เป็นชื่อที่ระบุใน myName จากนั้น Server จะตอบกลับมาเป็นตำแหน่งที่บันทึกไว้ โดยใช้ช่องว่างแยกข้อมูลออกจากกัน

13.8921433,100.7591537,0.0 13.8921433,100.7591537,0.0 13.8921433,100.7591537,0.0 13.8921433,100.7591537,0.0

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

ในส่วนของ Server เอง เราจะเข้าถึงจากภายนอกไม่ได้ ถ้ามีความรู้เรื่อง DDNS (Dynamic Domain Name Server) ก็สามารถทำการปรับตั้งเพื่อให้สามารถบันทึกข้อมูลจากภายนอกได้ ของผมใช้ของ no-ip ถ้าจะลองก็ให้ตั้ง serverIP เป็น supotsaeea.no-ip.info:81 ก็จะมาบันทึกข้อมูลไว้ที่เครื่องผมได้ ลองดูนะครับ ได้ไม่ได้ยังไงก็ส่งความคิดเห็นเข้ามาได้ครับ

------------------------------------------
สำหรับใครที่ทดลอง XAMPP แล้วไม่ผ่าน ให้เปิด Control Panel ของ XAMPP อยู่ใน c:\xampp ชื่อไฟล์ xampp-control.exe เปิดขึ้นมาจะได้หน้าต่างนี้


กดปุ่ม Start ที่แถว Apache


ตัวหนังสือ Apache จะคลุมเป็นสีเขียว ถ้าเป็นสีอื่นต้องแก้ไขหมายเลขพอร์ท สำหรับบางเครื่องที่ลง IIS ของ windows ซึ่งใช้พอร์ท 80 แล้ว ให้หลีกเลี่ยงไปใช้พอร์ท 81 แทน ให้กดที่ Config เลือกเมนูแรก Apache (httpd-conf)  

ไฟล์จะเปิดขึ้นด้วย Notepad ให้ไปที่บรรทัดที่ 47 หรือบรรทัดที่เขียนว่า Listen 80 ให้เปลี่ยนเป็น Listen 81 แล้วบันทึก ถ้าไม่สามารถบันทึกได้ให้ดูว่าไฟล์อยู่ที่โฟลเดอร์ไหน ปิดไฟล์จากนั้นเปิด Notepad แบบ Administrator เรียกไฟล์ทำการแก้ไขแล้วบันทึก

ทดลองกด Start อีกครังที่ Apache อาจต้องลองเปลี่ยนพอร์ทเป็น 8080 หรือ 8081 ซึ่งก็สามารถใช้งานได้เช่นกัน ถ้าไม่ได้จริงๆ ลองยังไงก็ไม่ได้ ก็หลังไมค์ได้ครับ

ถ้าใครทำอาชีพขนส่งแล้วอยากรู้ว่ารถของเราอยู่ที่ไหนก็ติดตั้งระบบนี้เข้าไป แต่อยากให้ใช้ GPS จะแม่นยำกว่าครับ เปิดเครื่องที่บริษัทเป็น Server ไม่ต้องไปเช่าใคร ปัจจุบันค่าเนตรายเดือนก็ไม่แพงแล้ว ส่งข้อมูลทั้งวันทั้งคืนก็จ่ายเท่าเดิม





ตอนที่ 10 : แสดงค่าโวลต์แบบหน้าปัทม์และตัวเลขกราฟฟิก

ดูคลิปนี้

http://www.youtube.com/watch?v=NbM7Ni2_eEQ&list=UUyt09UeQcMrj8QUSr5BEmaQ

จะทำการอ่านค่าจากพอร์ท Analog ของ Arduino มาแสดงบนหน้าจอ Android


ตอนนี้จะเรียนรู้การนำภาพมาแสดงบนจอ การวาดเส้นขยับตามค่าโวลต์ที่อ่านจากพอร์ท แสดงตัวเลขแบบกราฟฟิก

ไฟลต่างๆที่ต้องใช้ โหลดมาครับ
1. ไฟล์หน้าปัทม์ https://www.dropbox.com/s/q5ouvwpmr7188sr/voltmeter.JPG


2. ไฟล์ตัวเลขกราฟฟิก https://www.dropbox.com/s/0er5h40u5zoiwo3/flipnumber.png


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

ในรูปตัวเลขที่ให้มาจะมีระยะห่างระหว่างตัวเลขเท่ากับ 128 ดังนั้นถ้าต้องการตัวเลข 4 ก็จะไป crop ที่ X = (128 * (4 - 1)) = 384, Y = 0; ความกว้าง = 128, ความสูง = 174 ส่วนของการนำภาพที่ crop ไปใช้ก็ให้คำนวณค่าก่อน หาตัวเลขที่อยู่ในแต่ละหลัก แล้วก็มาหาตำแหน่งที่ต้องการดึงมาใช้ 

สร้าง Processing Sketch ใหม่ ตั้งขื่อตามใจชอบ บันทึก จากนั้นลากไฟล์รูปภาพทั้งสองวางลงในพื้นที่ของ Editor ตรงไหนก็ได้ครับ โปรแกรมจะทำการสร้างโฟลเดอร์ชื่อ data เพื่อเก็บไฟล์ทั้งสอง ลองเปิดดูในโฟลเดอร์ของ sketch  ดูครับ ทีนี้ถ้าต้องการใช้ไฟล์อะไร ก็ลากไปวางตรงพื้นที่ที่ใช้พิมพ์โค๊ดนั่นแหละ มันก็จะเอาไปเก็บให้เราเอง

ทีนี้เอาไฟล์ AndroidManifest.xml และโฟลเดอร์ res (ย้อนกลับไปดูตอนที่ 7 AndroidSerial Library) ยกมาวางในโฟลเดอร์ของ Sketch อันนี้ลากมาวางในพื้นที่โค๊ดไม่ได้นะครับ เพราะมันจะเอาไปใส่ในโฟลเดอร์ data โปรแกรมจะมองไม่เห็น ให้วางอยู่ในระนายเดียวกับตัว sketch

Arduino โค๊ด

void setup(){
  pinMode(11,OUTPUT);
  digitalWrite(11,LOW);
  Serial.begin(9600);
}
char c;
String str = "";

boolean active = false;

void loop(){
  //delay(200);
  if(Serial.available()){
    while(Serial.available() > 0){
      c = Serial.read();
      if(c == '\n'){
        if(str[0] == 'B')
        {
          digitalWrite(11, !digitalRead(11));
          active = true;
        }
        if(str[0] == 'S')
          active = false;
       
        str = "";
      }else{
        str += c;
      }
    }
  }
  else
  {
    if(active)
    {
      Serial.println(map(analogRead(A0),0,1023,0,255));
      active = false;
    }
  }
}

ตัวโค๊ดจะอ่านตัวอักษรมาตรวจสอบว่ามีตัว newline (\n) หรือไม่ ถ้ามีก็ให้ดูว่าตัวคำสั่งคืออะไร ถ้าเป็นตัวอักษร B ก็จะสั่งให้ LED ติด-ดับ และเปิดให้ส่งข้อมูลของ A0 กลับไป สังเกตุว่าเมื่อส่งค่าคืนไปแล้วก็จะปิดการส่ง ด้วยคำสั่ง active = false; การทำแบบนี้ก็เพื่อไม่ให้ส่งข้อมูลกลับมากเกินไป ซึ่งจะทำให้ตัวรับต้องมาตอบสนองข้อมูลจนไม่สามารถไปทำงานอื่นได้ เมื่อส่งข้อมูลกลับแล้วก็จะมารอรับคำสั่งต่อไป วิธีการนี้สามารถประยุกต์ไปใช้ได้กับงานอื่นๆได้นะครับ

โค๊ดสำหรับ Processing

import com.yourinventit.processing.android.serial.*;
import java.net.*;

PImage pimage;
PImage nimage;

Serial port;
String voltRead="0"; // เก็บค่าโวลต์

void setup(){
  size(400,342);
  originX = 205;
  originY = 245;
  frameRate(10); // กำหนดความเร็วในการวาดภาพ ตอนนี้เป็น 10 ครั้งต่อวินาที
  pimage = loadImage("voltmeter.JPG"); // โหลดรูปหน้าปัทม์
  nimage = loadImage("flipnumber.png"); // โหลดรูปตัวเลข

  println(Serial.list(this));
  port = new Serial(this,Serial.list(this)[0],9600);
  port.clear();
  port.bufferUntil('\n');

  port.write("B\n"); // ส่งคำสั่งไปที่ Arduino สังเกตุว่าต้องมี \n ตามไปด้วย
}

int originX, originY;
float measureVolt;
long lastMillis = millis();
int digit = 0;
int count = 0;
boolean portReady = true;

void draw(){
  try{ // ใส่คำสั่ง try ไว้เผื่อว่าเกิดข้อผิดพลาดใดๆ ตอนทำงาน โปรแกรมจะได้ไม่หยุดทำงาน
    background(0);
    image(pimage,0,0); // วางรูปหน้าปัทม์
    needle(originX, originY, measureVolt); // วาดรูปเข็มตามค่าที่รับมาจาก  Arduno
 
   //
   // ส่วนนี้เป็นการ Crop ตัวเลขมาจากรูปตัวเลข
   //
    int c = int(measureVolt) % 1000;  // ดึงค่าตัวเลขที่น้อยกว่า 1000 มาใช้งาน

   // ส่วนนี้เป็นหลักร้อย โดยที่ c/100 จะดึงตัวเลขที่หลักร้อยมาใช้งาน เพื่อหาว่าจะนำช่องไหนของรูปตัวเลขมาแสดง

  // นี้คือตำแหน่งที่ของตัวเลข
    int iStart = ((c / 100) * 128);
 
  // คำสั่ง copy จะ crop รูปตัวเลข nimage ที่ตำแหน่ง x = iStart, y = 0, กว้าง 128 สูง 174 จากนั้นเอาไป
  //วาดไว้ที่ตำแหน่ง x = 152, y = 250 ย่อขนาดกว้างลงมาเป็น 32 และสูง 45

    copy(nimage,iStart,0,128,174,152,250,32,45);

   // หลักสิบ
    c = c % 100;
    int iStart1 = (( c / 10) * 128);
    copy(nimage,iStart1,0,128,174,184,250,32,45);
 
   // หลักหน่วย
    c = c % 10;
    int iStart2 = (c * 128);
    copy(nimage,iStart2,0,128,174,216,250,32,45);
 
   // ส่วนนี้ให้รอ 300 ms แล้วค่อยส่งคำสั่งไปอีกครั้ง
    if((millis() - lastMillis) > 300){
      lastMillis = millis();
      port.write("B\n");
    }
  }
  catch(Exception e){
   // ส่วนนี้เพื่อให้รู้ว่าตอนทำงานพบปัญหา เพื่อให้สามารถ debug เพื่อแก้ไข
    println("Something happen: " + (++count));
    lastMillis = millis();
  }
}

 //
 // ส่วนนี้จะทำการวาดเข็มให้เคลื่อนไปในตำแหน่งที่ได้รับมาจาก Arduino
 //
void needle(int originX, int originY, float measureVolt){
  int minToMaxAngle = 90;  // มุมที่วัดจากตำแหน่งค่าเท่ากับ 0 ไปถึงค่าสูงสุดในที่นี้คอที่ตำแหน่ง 50
  int maxVolt = 50;  // ค่าโวลต์สูงสุด
  int startAngle = 45;   //มุมที่วัดจากตำแหน่ง 0 องศา (ซ้ายมือ) มาถึงตำแหน่ง 50 โวลต์

  // มุมที่เข็มเคลื่อนที่
  float angle = (PI/180) * ((minToMaxAngle * (maxVolt - measureVolt) / maxVolt) + startAngle);

  // ความยาวของเข็ม
  float needleLength = 200;

  strokeWeight(2); // ขนาดความหนาของเข็ม
  stroke(255,0,0); // สีของเข็ม (สีแดง)

  // วาดเส้น
  line(originX, originY, originX+cos(angle)*needleLength, originY-sin(angle)*needleLength);

  fill(0);
  noStroke();

  // วาดรูปวงกลมปิดเข็มในส่วนที่ไม่ต้องการ
  ellipse(originX,originY,100,100);
}

// ส่วนการรับข้อมูลและแปลงค่าที่อ่านได้เป็นขนาดโวลต์

void serialEvent(Serial port){
  // อ่านค่าที่ส่งมาจาก Arduino เมื่อพบตัว \n ให้เก็บค่าไว้ที่ voltRead
  voltRead = port.readStringUntil('\n');
  port.clear();
  voltRead = trim(voltRead);

  // นำ voltRead มาแปลงค่าเป็น measureVolt เพื่อใช้ในการวาดเข็มและแสดงตัวเลข
  measureVolt = float(voltRead) * 50 / 255;
}

บทความตอนนี้ จะเน้นในการนำค่าที่ได้มาแสดงให้เกิดความน่าสนใจ โดยที่อาจทำการสร้างหน้าปัทม์ขึ้นเองด้วยโปรแกรม Graphic Editor ต่างๆ เช่น Photoshop, Paint แล้วก็บันทึกเป็นไฟล์ jpg, gif หรือ png นำมาวางเป็นพิ้นหลัง จากนั้นก็สร้างการเคลื่อนที่ของเข็ม การแสดงตัวเลข การติดดับของหลอดไฟ แบบที่แสดงในห้องควบคุมในเครื่องบิน

หวังว่าจะทำให้เกิดไอเดีย ไปทำงานอื่นๆนะครับ

ส่วนของการเขียนบทความ อาจไม่สม่ำเสมอ ยังไงก็ follow ได้นะครับ






วันจันทร์ที่ 31 มีนาคม พ.ศ. 2557

ตอนที่ 9 : ต่อ Android กับ PC แบบไม่ต้องใช้สาย


มีเคล็ดลับในการต่อ Android เข้ากับ Processing แบบต่อผ่าน WIFI แทนกัน สิ่งที่ต้องมี ตามนี้ครับ

1. หาโปรแกรม adb.exe ซึ่งจะอยู่ในชุด ADK ที่เราโหลดมา  

อันนี้ในเครื่องผม ทีนี้ต้องเข้าไปที่นี่ด้วย Command Prompt สังเกตุที่ Path นะครับ


จากนั้นให้เสียบสาย USB จาก Android มาที่ PC ก่อน จะมีเสียงติ๊งตึ่ง ซึ่งแสดงว่ามีการรับรู้ว่ามีการเชื่อมต่อแล้ว ให้พิมพ์คำสั่งตมนี้

adb devices -l   (ตัวอักษร แอล)


ผลการทำคำสั่ง จะมีรายการแสดงหมายเลขของ Android พร้อมคำอธิบาย ถ้าไม่ขึ้นตามนี้ให้ลองดึงสาย แล้วเสียบใหม่ จากนั้นให้ลองพิมพ์ adb shell ls หน้าจอจะแสดงรายการไฟล์ที่มีอยู่ใน Android ส่วนนี้ไม่เกี่ยวนะครับ เพียงให้รู้ว่าต่อเข้า Android ได้แล้ว

จากนั้นพิมพ์ adb tcpip 5555 เครื่องตอบกลับตามรูป



ทีนี้ให้ดึงสาย USB ออก แล้วป้อนคำสั่ง adb connect 192.168.1.3 หมายเลข IP หาได้จากเครื่องในส่วนของการตั้งค่า WIFI ให้แท๊บที่ Access point ที่เราเชื่อมต่อใน Android ดู IP แล้วมาใส่ตามคำสั่ง


ผลแสดงเชื่อมต่อกับ Android ถ้าได้ตามนี้ เมื่อทดลอง Processing มันจะคอมไฟล์ลงเครื่องของเราเอง ทีนี้ก็ไม่ต้องต่อสาย USB แล้ว

วันเสาร์ที่ 29 มีนาคม พ.ศ. 2557

ตอนที่ 1 : Ketai เกริ่นนำ

Ketai เป็น Library แบบ Open source เพื่อดึงเอาความสามารถของเซนเซอร์ที่มีใน Android มาใช้งานผ่านโปรแกรม Processing ซึ่งอาจเกี่ยวกันกับ Arduino แบบบางๆ คืออาจเชื่อมต่อหรือไม่เชื่อมต่อกับ Arduino ก็ได้

จากที่ Android เองมีเซนเซอร์อยู่มากมาย อย่างน้อยก็ WiFi, Accelerometer, GSM เพื่อส่ง SMS ซึ่งถ้าเราต้องการให้ Arduino ทำอะไร ปกติก็ต้องไปจัดหา Shield ตัวนั้นๆ มาใช้ แต่ถ้ามี Android แล้วเราก็เขียนคำสั่งผ่าน Ketai ไปบอกให้เซนเซอร์ตัวนั้นๆ ทำงาน

ยกตัวอย่างการทำ Remote Sensing ที่ต้องการให้ส่งข้อมูลไปยัง Control Center ด้วยการส่งเป็น SMS ก็สามารถเปิด Library ในส่วน SMS จัดข้อมูลแล้วก็ส่งได้เลย ซึ่งปกติเราก็ต้องเขียนโปรแกรมบน Arduino ทั้งหมดบน GSM Shield หรือไม่ก็อาจส่งข้อมูลเข้า Server ของ Control Center ผ่านความสามารถในการเชื่อต่อ internet ได้อีกทางหนึ่ง

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


ตอนที่ 8 : ทดลองกับ Android กับ Arduino (จบครับ)

สิ่งที่ต้องมี
1. Android mobile หรือ Tablet
2. สาย USB OTG
3. บอร์ด Arduino

โปรแกรมที่ต้องลงในบอร์ด Arduino


void setup(){
  pinMode(11,OUTPUT);
  digitalWrite(11,LOW);
  Serial.begin(9600);
}

void loop(){
  if(Serial.available() > 0){
    while(Serial.available()){
      if(Serial.read() == 'B')
        digitalWrite(11, !digitalRead(11));
    }
  }
  else
 {

  Serial.println(random(255));
  delay(100);

 }
}

ทดสอบการทำงานด้วย Serial monitor เห็นตัวเลขวิ่ง และเมื่อป้อนอักษร B กดปุ่ม Send หลอด LED ติด ป้อน B กดปุ่ม Send หลอด LED ดับ เป็นโอเค

เปืดโปรแกม Processing เลือก Android mode เรียก Sketch ที่ทดลองกับ PC แต่ต้องปรับโปรแกรมเล็กน้อย ตามนี้

ให้ทำการ import library -> Android Serial Library for Processing


import com.yourinventit.processing.android.serial.*;
import controlP5.*;

//import processing.serial.*; ลบบรรทัดนี้ทิ้งได้เลยครับ หรือใส่ // ไว้ด้านหน้าก็ได้

ControlP5 cp5;
Serial port;

String inData="";

void setup(){
  size(600,600);

  println(Serial.list());
  port = new Serial(this, Serial.list()[1], 9600);
  port.clear();
  port.bufferUntil('\n');

  cp5 = new ControlP5(this);

  cp5.addTextfield("text1")
     .setPosition(100,100)
     .setSize(100,20)
     .setCaptionLabel("Data")
     .setFont(createFont("Verdana", 16))
     ;

  cp5.addButton("button1")
     .setValue(0)
     .setPosition(250, 100)
     ;
   
  cp5.addSlider("slider1")
     .setPosition(150,200)
     .setValue(100)
     .setMax(255)
     .setMin(0)
     ;
   
  cp5.addKnob("knob1").setSize(100,200)
     .setPosition(150,250)
     .setMax(255)
     .setMin(0)
     .setValue(100)
     ;
}

void draw(){
  Textfield t = (Textfield) cp5.getController("text1");
  t.setText(inData);
  cp5.getController("slider1").setValue(int(inData));
  cp5.getController("knob1").setValue(int(inData));
}

void button1(int value)
{
  println("Button pressed");
  port.write("B");
}

void serialEvent(Serial port){
  inData = port.readStringUntil('\n');
  inData = trim(inData);
}

เสียบสาย USB OTG เข้ากับเครื่อง Android ปลายอีกด้านนำสาย USB จากบอร์ด Android เสียบเข้า

ปิดหน้าต่าง Serial monitor เสียก่อน กดปุ่ม Run ของ Processing เครื่องทำการลงโปรแกรมไปที่ Android และเริ่มทำงาน ถ้าเกิดเออเร่อร์ เหตุที่เป็นไปได้คือ ตัเลขในวงเล็บระบุหมายเลข COM Port ไม่ถูกต้องให้เลือนบรรทัดที่แสดงเออเร่อร์ขึ้นไปหาตัวเลขเพื่อนำไปป้อนในโปรแกรม ส่วนใหญ่แล้วจะเป็น [0] ครับ

กดปุ่ม Run อีกครั้ง ตอนนี้เอง Android จะแสดงป๊อบอัพ ขึ้นมาถามว่าจะใช้โปรแกรมไหนเมื่อมีการเสียบสายเข้ากับ Arduino ให้เลือกชื่อโปรแกรมที่เราเพิ่งติดตั้งไป จะเห็นหน้าตาโปรแกรมเหมือนกับที่ทดสอบบน PC ทดลองกดปุ่ม สังเกตุหลอด LED

ปัญหาที่น่าจะพบคือ Android ไม่แสดงป๊อบอัพ นั่นก็หมายถึงไฟล์ AndroidMaifest.xml ไม่ได้แก้ไข ตามที่ได้แนะนำ ให้ทำการแก้ไขไฟล์ AndroidManifest.xml จากตอนที่แล้ว

ทั้งหมดที่เขียนมา หวังว่าพอจะจุดประกายไอเดีย ในการนำไปใช้งาน

สำหรับผมเองมองว่าตัว Android จะมี Sensor อยู่ภายในตัวมันเองเยอะมาก แทบจะเรียกได้ว่าใช้แทน Arduino shield ได้เป็นอย่างดี เช่น Camera, GPS, Bluetooth, Accelerometer, Compass, NFC, Clock, SD Card, WiFi หน้าจอ LCD ขนาดใหญ่ พร้อม Touch screen (ถ้าเป็น Tablet ต่อออก HDMI เหมือนที่ผมทำคลิปตัวอย่าง อันนี้ยิ่งไอเดียบรรเจิด)  รวมๆแล้ว ถ้าซื้อ Shield ทั้งหมดก็หลายตังค์ แต่ซื้อ Android ตัวเดียว ประมาณ 3 - 4 พัน มีครบเลย ถ้ามีไอเดียดีๆ งานที่ออกมาจะดูทันสมัย ขายได้ราคาเลยหละ

ถ้าใครได้แนวคิดแล้วทำเป็นเรื่องเป็นราว สละเวลามาแบ่งปันกันบ้างก๊ดีนะครับ

ตอนต่อไปผมจะแนะนำ Library อีกตัว ชื่อ Ketai ตัวนี้จะนำเอา Sensor ทั้งหมดที่มีใน Android มาประยุกต์ใช้งาน เขียนบน Processing เชื่อมเข้า Arduino เพื่อขับ Hardware รอติดตามนะครับ

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


AndroidSerial Library

วันศุกร์ที่ 28 มีนาคม พ.ศ. 2557

ตอนที่ 7 : AndroidSerial library


AndroidSerial libaray ไม่อยู่ในรายการ Import Library ต้องติดตั้งเอง ก่อนอื่น download ก่อนครับ


ยังไม่ต้องแตกไฟลนะครับ ต้องหาตำแหน่งที่จะไปเก็บก่อน วางมั่วๆโปรแกรมหาไม่เจอ จะใช้งานไม่ได้ครับ


ขั้นแรกให้เปิดโปรแกรม Processing ก่อน เข้า Menu -> File - Preference
ดู Path ที่แสดงในช่อง Sketchbook Location



เมื่อรู้ตำแหน่งเก็บ Sketchbook แล้ว เปิดเข้าไปจะพบโฟลเดอร์ libraries ให้เอาโฟลเดอร์ AndroidSerial ใส่ไปในโฟลเดอร์นี้

วางเรียบร้อยก็ปิดโปรแกรม แล้วเปิดขึ้นใหม่ เข้าไปที่ Sketch -> Import Library จะเห็น Android Serial Library for Processing เพิ่มเข้ามา เป็นอันเรียบร้อย ให้คลิ๊กที่ Android Serial Library for Processing โปรแกรมจะ import library มาที่พื้นที่เขียนโปรแกรม


อันนี้ต้องทำทุกครั้งที่สร้าง Sketch ใหม่

ยังมีอีกส่วนหนึ่งที่ต้องเพิ่มเติมเข้าไปในโฟลเดอร์ของ Sketch ปกติแล้วตอนที่เราทำการบันทึก Sketch โปรแกรมจะทำการสร้างโฟลเดอร์ใหม่ให้เรา ตั้งชื่อตามที่เราระบุ ถ้าตามที่ผมเก็บโฟลเดอร์ใหม่จะเก็บอยู่ในโฟลเดอร์ Works ในโฟลเดอร์ที่สร้างใหม่ ถ้าเราเลือก Android mode โปรแกรมจะสร้างไฟล์ชื่อ AndroidManifest.xml ซึ่งจะขาดส่วนสำคัญในการเชื่อมต่อกับ USB อยู่

อันนี้โปรแกรมสร้างให้ในไฟล์ AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package=""
          android:versionCode="1"
          android:versionName="1.0">
  <uses-sdk android:minSdkVersion="10" />
  <application android:label=""
               android:icon="@drawable/icon"
               android:debuggable="true">
    <activity android:name="">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>
</manifest>


แต่ที่ต้องการเป็นตามนี้ 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
               android:versionCode="1" 
               android:versionName="1.0" 
               package="">
      <uses-feature android:name="android.hardware.usb.host"/>
      <uses-sdk android:minSdkVersion="10"/>
      <application android:icon="@drawable/icon" android:label="">
      <activity android:name="">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
      <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
      </intent-filter>
      <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"        android:resource="@xml/device_filter"/>
    </activity>
  </application>
</manifest>

ใช้เฉพาะถ้าเลือก Android mode และใช้ AndroidSerial 

โอเคส่วนสุดท้ายครับ ใน Sketch โฟลเดอร์ให้สร้างโฟลเดอร์ res จากนั้นเข้าไปที่โฟลเดอร์ res สร้างโฟลเดอร xml ตามรูป แล้วสร้างไฟล์ device_filter.xml อาจใช้ Notepad ช่วยครับ


เนื้อหาในไฟล์ครับ

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 0x0403 / 0x6001: FTDI FT232R UART -->
    <usb-device vendor-id="1027" product-id="24577" />

    <!-- 0x2341 / Arduino -->
    <usb-device vendor-id="9025" />

    <!-- 0x16C0 / 0x0483: Teensyduino  -->
    <usb-device vendor-id="5824" product-id="1155" />

    <!-- 0x10C4 / 0xEA60: CP210x UART Bridge -->
    <usb-device vender-id="4292" product-id="60000" />
 
</resources>

พิมพ์เองคงยากให้ Copy แล้ว Paste จากนั้นบันทึก ข้อควรระวังในการใช้ Notepad ตอนบันทึกมันจะใล่ .txt ต่อท้ายชื่อไฟล์ ต้องดูให้ดีว่าไฟล์นั้นต้องชื่อ device_fileter.xml

เพิ่มเติมครับ ในไฟล์นี้จะเป็นการเลือกข้อมูลของ Chip USB ที่อยู่บนอร์ด Arduino ถ้าทดลองแล้วไม่ทำงาน อาจจะเป็นส่วนนี้ ซึ่งเราสามารถหาค่าของ vender-id และ product-id โดยดูจาก Properties ของบอร์ด


ตัวอย่างบอร์ดของผมจะเป็น 0403 และ 6001 เป็นเลขฐาน 16 ซึ่งจะเท่ากับ 1027 และ 24577 เมื่อแปลงเป็นเลขฐานสิบ ยังไงถ้าไม่ได้ก็เอาตัวเลขของบอร์ดมาใส่แทนได้ครับ ไม่น่าจะงงนะครับ

เอาหละตอนนี้ก็ใช้พลังสมองเยอะหน่อย ก็จะไม่ยาวไปกว่านี้ เอาไว้ทดลองกันตอนต่อไปดีกว่า


เตรียมการเข้าสู่ Android mode

วันพฤหัสบดีที่ 27 มีนาคม พ.ศ. 2557

ตอนที่ 6 : เตรียมการเข้าสู่ Android mode

ตอนนี้ผมว่าเป็นตอนที่ต้องทำความเข้าใจให้ดี ไม่งั้นจะหลงทางได้ง่าย ๆ

การพัฒนาโปรแกรมบน Android  ใช้การพัฒนาในลักษณะต่อยอดจากที่มีอยู่ ซึ่งเค้าเรียกว่า Development Kit ซึ่งจะเห็นและได้ยินกันอยู่บ่อย บางค่ายก็จะเรียกว่า Framework

Android Development Kit (ADK) ก็เป็นส่วนสำคัญขาดไม่ได้ ในการพัฒนาโปรแกรม ซึ่งเราจำเป็นต้องติดตั้งชุดนี้ลงในเครื่อง PC ก่อนเป็นอย่างแรก ง่ายๆครับ download กันเลย


ไม่มีการติดตั้งใดๆ แตกไฟล์ออกวางในตำแหน่งที่สามารถเข้าถึงได้ง่ายๆ



ผมเองจะทำโครงสร้างโฟลเดอร์ลักษณะนี้เพื่อจะได้หาง่าย ชื่อโฟลเดอร์ยาวมาก ตอนจะเรียกใช้งานใน command prompt พิมพ์กันเมื่อยเลย ใครจะ rename ก็ไม่ว่ากัน ผมทิ้งไว้แบบนี้จะได้ไม่ทำให้สับสน เปิดเข้าไปดูก็จะมีโฟลเดอร์อีก ทีนี้ต้องเริ่มงานสำคัญกันแล้ว เปิดโปรแกรม SDK Manager.exe


จะเห็นหน้าต่างนี้

ที่เห็นนี่ได้ติดตั้งในส่วนของ Android 2.3.3 (API 10) แล้ว ถ้าเปิดแล้วพบว่าเป็น Not Installed ให้ติ้กที่ช่องหน้า Android 2.3.3 (API 10) ที่ปุ่ม Install ... package ให้กดปุ่มเพื่อเริ่มติดตั้ง 


กดเลือก Accept License ตามด้วย Install รอจนติดตั้งสำเร็จ และเห็นว่าเปลี่ยนเป็น Installed เป็นอันใช้ได้

มีอีก Package หนึ่งที่ต้องติดตั้งด้วยคือ Build-tools ตัวนี้ถ้าไม่มีตอนคอมไฟล์ใน Processing จะแจ้งเออเร่อร์


จะเห็นว่ามี Build-tools หลายตัว ผมเลือกตัวล่าสุด ใช้ได้ก็เลยไม่ได้ลองตัวอื่น
เรื่องของ ADK ก็เป็นอันเรียบร้อย จะรู้ว่าใช้ได้หรือไม่ก็ต้องลองเขียนโปรแกรมเลย

ทีนี้เราจะใช้ Processing ใน Android mode ก็ต้องติดตั้งกัน เหมือนเดิมครับ ถ้าจำได้ตอนติดตั้ง PDE X ก็แบบเดียวกัน Add mode


หา Android mode แล้วก็ติดตั้ง เสร็จเรียบร้อย ปิด เปิด โปรแกรม แล้วก็เลือก Android mode

ลองเขียนโปรแกรมง่ายสักตัว เอาจากตอนแรกๆ ก็ได้ ยังไม่ต้องใช้ ControlP5 ประมาณนี้

void setup() {
  size(500,500);
}

void draw(){
  background(128);
  rect(100,100,100,150,5);
}

ทีนี้ก็เสียบสาย USB จาก Android มายัง PC ถ้ายังไม่เคยใช้ก็อาจมีการติดตั้ง driver กันเล็กน้อย แต่ส่วนสำคัญที่จะต้องมีคือ Android Phone


ถ้ายังไม่มีตัวนี้จะไม่สามารถส่งโปรแกรมไปที่ Android ได้ เครื่องผมเป็น Samsung ก็จะเป็นประมาณนี้

เมื่อทดลอง run โปรแกรม จะสังเกตุเห็นการส่งข้อมูลไปยัง Android ตัวเลข 92862519 นี่แต่ละเครื่องจะไม่เหมือนกันครับ




ผลแสดงบนหน้าจอ Android 


ลองเพิ่มส่วน ControlP5 เข้าไปจากตอนที่แล้ว แต่จะมาใช้ทั้งหมดไม่ได้นะครับ ต้องตัดส่วนที่เกี่ยวกับ Serial ออกก่อน ประมาณนี้

//import com.yourinventit.processing.android.serial.*;
import controlP5.*;
//import processing.serial.*;

ControlP5 cp5;
//Serial port;

String inData="";

void setup(){
  size(600,600);
  
  //println(Serial.list(this));
  //port = new Serial(this, Serial.list(this)[0], 9600);
  //port.clear();
  //port.bufferUntil('\n');
  
  cp5 = new ControlP5(this);
  
  cp5.addTextfield("text1")
     .setPosition(100,100)
     .setSize(100,20)
     .setCaptionLabel("Data")
     .setFont(createFont("Verdana", 16))
     ;
  
  cp5.addButton("button1")
     .setValue(0)
     .setPosition(250, 100)
     ;
     
  cp5.addSlider("slider1")
     .setPosition(150,200)
     .setValue(100)
     .setMax(255)
     .setMin(0)
     ;
     
  cp5.addKnob("knob1").setSize(100,200)
     .setPosition(150,250)
     .setMax(255)
     .setMin(0)
     .setValue(100)
     ;
}

void draw(){
  //Textfield t = (Textfield) cp5.getController("text1");
  //t.setText(inData);
  //cp5.getController("slider1").setValue(int(inData));
  //cp5.getController("knob1").setValue(int(inData));
}

void button1(int value)
{
  println("Button pressed");
  //port.write("B");
}

//void serialEvent(Serial port){
//  inData = port.readStringUntil('\n');
//  inData = trim(inData);
//}


ทดลอง Run ครับ

มาถึงตรงนี้ เราสามารถใช้ Processing ได้ ทั้งบน PC, PC + Arduino และ Android เหลืออีกจุดหนึ่งก็คือให้ Android เชื่อมต่อกับ Arduino โดยตรงผ่านสาย USB OTG ก็ต้องเป็นตอนต่อไปครับ


ตอนที่ 5 : ControlP5

ผมมีแก้ไขเนืือหาตอนที่ 4 เป็นส่วนสำคัญมากๆ 
ลองย้อนกลับไปดูครับ
---------------------------------------------------------------------------------

จากตอนที่แล้ว มีหลายอย่างที่ยังไม่ได้อธิบาย ในส่วนของ Serial เองเป็น library ที่ช่วยในการใช้ Serial port ก็จะเหมือนกับ Serial ใน Arduino ก็คือต้องระบุว่าจะเชื่อมต่อกับ COM ช่องไหน และ Baudrate  ซึ่งในโค๊ดใช้คำสั่งประกอบกัน 2 ส่วนคือ

Serial port;

เป็นการประกาศตัวแปรแบบ Serial ตั้งชื่อว่า port

ในส่วนของการเตรียมการ setup() ก็นำตัวแปรไปใช้

void setup() {
.
.
  println(Serial.list()); // แสดงรายการ COM port

  port = new Serial(this, Serial.list()[1], 9600);

  port.clear() // ล้างข้อมูลใน Buffer

  port.bufferUntil('\n'); กำหนดว่าเมื่อพบ \n ก็หยุดการล้างข้อมูล  
.
.
}

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

ส่วนของ this เอง คือระบุว่าจะใช้กับ PApplet ตัวไหน ก็ให้ใส่ไว้แบบนี้ เพราะคงจะน้อยครั้งที่เราจะมี PApplet มากกว่า 1 ตัว

เมื่อกำหนดทั้งหมดแล้ว โปรแกรมก็พร้อมที่จะรับ-ส่ง ข้อมูลแล้วหละ

ที่จะพูดต่อก็คือ ในส่วนของ ControlP5

ControlP5 เป็น Library ที่ใช้ในการวาง Control ต่างๆ ได้แก่ Button, Textfield, Slider, Knob ที่เราเห็นในตัวอย่างจากตอนที่แล้ว และอีกมากมายที่เห็นในรูป




หลายคนเคยเขียน Java หรือภาษาแบบ OOP จะคุ้นกับการใช้ Class แต่ถ้าไม่คุ้นก็ค่อยติดตามไป ไม่ยากครับ

ขอเรียกทั้งหลายที่อยู่ในรูปว่าเป็น Controller การเรียกใช้งานก็ผ่านการประกาศตัวแปร เช่นถ้าต้องการจะใช้ Button ก็ตามนี้

Button button;
Textfield textfield;

ประมาณนี้ครับ

แต่ใน Processing จะมีการเพิ่ม Control ด้วยคำสั่งลักษณะที่ต้องทำความเข้าใจนิดนึง ซึ่งก่อนอื่นต้องสร้างกล่องเก็บ Control ก่อน ตามนี้

ControlP5 cp5;

ใน setup() ก็ใช้ cp5 ด้วย

cp5 = new ControlP5();

ตอนนี้ก็มีกล่องเก็บ Control แล้ว พร้อมจะเก็บ Control ทั้งหลาย สมมุติจะใช้ Button จะทำตามนี้

button = cp5.addButton("button");

ซึ่งอาจไม่ต้องเอาตัวแปร button มารับค่าก็ได้ โดยตัด button = ด้านหน้าออก ก็สามารถทำงานได้ จากตัวอย่างในตอนที่แล้ว มีการกำหนดค่าด้วย .setValue และระบุตำแหน่งด้วย .setPosition ด้วย เอาเป็นว่าใช้วิธีการเขียนตามที่แนะนำไปก่อน เดี๋ยวจะสับสน

ผมว่าอ่านโค๊ดกันก็น่าจะเข้าใจได้ไม่ยาก สำหรับการสร้าง Control ตัวอื่นๆ

ในส่วนของการรับข้อมูลจาก Serial


void serialEvent(Serial port){
  inData = port.readStringUntil('\n');
  inData = trim(inData);
}

serialEvent เป็นคำเฉพาะเพื่ออ่านข้อมูลเข้า ไม่แน่ใจว่าสามารถเขียนแบบอื่นได้หรือปล่าว ยังไม่ได้ลอง ส่วนสำคัญอีกจุดหนึ่งคือ การอ่านค่า ด้วยคำสั่ง port.readStringUtil('\n'); เป็นการอ่านข้อมูลที่เข้ามาจนพบตัว \n (new line) แล้วก็เก็บค่าไว้ในตัวแปร inData ประกาศเป็นแบบ Global ซึ่งทำให้นำเอาไปใช้ในส่วนอื่นได้

ในส่วนของ draw() ก็จะนำเอา inData ไปใช้กับทุก Control

มีอีกส่วนหนึ่งคือการใช้งาน Button


void button1(int value)
{
  println("Button pressed");
  port.write("B");
}


ข้อสังเกตุคือชื่อของฟังค์ชั่นจะเป็นตัวเดียวกับชื่อของ Control คือ button1 ตัวฟังค์ชั่นเองจะคอยรับเหตุการณ์ที่เกิดกับ button ในที่นี้ก็คือการกดปุ่ม โปรแกรมก็จะทำคำสั่งภายใน ก็คือการส่งตัวอักษร B ไปให้ Arduino นั่นคือ สั่งปิด เปิด LED นั่นเอง ส่วนของคำสั่ง println จะแสดงค่าในหน้าต่างด้านล่างของ Processing เพื่อให้สามารถ debug โปรแกรมได้

ยังไงอ่านแล้วยังไม่เข้าใจ ส่งความคิดเห็นมาได้ครับ

ตอนต่อไป จะเป็นการเตรียมเชื่อต่อกับ Android ซึ่งมีเรื่องที่ต้องทำเยอะมาก จะพยายามค่อยๆ อธิบาย คอยติดตามนะครับ

พูดคุยกับ Arduino บน PC กันก่อนดีกว่า

วันพุธที่ 26 มีนาคม พ.ศ. 2557

ตอนที่ 4 : พูดคุยกับ Arduino บน PC กันก่อนดีกว่า

ตามที่ได้เกริ่นไว้แต่แรกว่า เราจะทำการเชื่อมต่อไปยัง Arduino ด้วยสาย USB แบบ OTG ส่วนการจะเชื่อมต่อด้วยวิธีอื่นๆ ผมเองไม่มีตัว Module อื่นๆ อยู่ แต่ถ้าจะมีใครใจดีช่วยจัดหาให้ ก็ยินดีที่จะทดลองการเชื่อมต่อแบบอื่นๆ

บทความตอนนี้จะทดลองเชื่อมต่อผ่านทาง PC เพื่อให้เห็นการเตรียมการต่างๆ ก่อน จากนั้นก็จะสามารถนำไปใช้ตอนที่จะเชื่อมต่อกับ Android ได้

เริ่มกันเลย โดยเปิดโปรแกรม Arduino ก่อน เพื่อสร้างโปรแกรมสำหรับโต้ตอบกับ Processing ได้ พิมพ์โปรแกรมตามนี้


void setup(){
  pinMode(11,OUTPUT);
  digitalWrite(11,LOW);
  Serial.begin(9600);
}

void loop(){
  Serial.println(random(255));
  delay(100);
  if(Serial.available()){
    while(Serial.available()){
      if(Serial.read() == 'B')
        digitalWrite(11, !digitalRead(11));
    }
  }
}

ที่บอร์ดต่อ LED ที่ Pin 11 กับ Gnd จากนั้นทดลอง Run โปรแกรม และเปิด Serial Monitor สังเกตุให้ค่า Baudrate เท่ากับ 9600 นะครับ

ตอนนี้จะเห็นหน้าจอแสดงตัวเลขแบบแรนดอม ที่ออกจากคำสั่ง Serial.println(random(255));

ให้พิมพ์ตัว B ลงในช่องรับข้อมูล ตามด้วยการเคาะ Enter จะเห็นว่า LED ที่ต่อไว้ติดสว่าง พิมพ์ B ตามด้วย Enter อีกครั้ง จะเห็นว่า LED ดับลง ตามคำสั่งเปรียบเทียบการอ่านค่าด้วย Serial.read ว่าเท่ากับตัวอักษร B หรือไม่ ถ้าเงื่อนไขถูกต้องก็ไปทำคำสั่ง 


digitalWrite(11, !digitalRead(11));

ก็คือถ้า LED ติดอยู่ก็ให้ดับ ถ้าดับอยู่ก็ให้ติด สลับไปมา

ถ้าได้ตามนั้นแล้วก็ทำในส่วนของ Processing ต่อไป ถ้าไม่ได้ก็ลองดูว่า มีตรงไหนที่พิมพ์ผิดไปบ้าง

เปิดโปรแกรม Processing ตามสะดวกนะครับ ว่าจะใช้ Mode ไหน ใช้ได้ทั้ง Java และ PDE X ครับ พิมพ์โค๊ดตามนี้ครับ


import controlP5.*;
import processing.serial.*;

ControlP5 cp5;
Serial port;

String inData="";

void setup(){
  size(600,600);
  
  println(Serial.list());
  port = new Serial(this, Serial.list()[1], 9600);
  port.clear();
  port.bufferUntil('\n');
  
  cp5 = new ControlP5(this);
  
  cp5.addTextfield("text1")
     .setPosition(100,100)
     .setSize(100,20)
     .setCaptionLabel("Data")
     .setFont(createFont("Verdana", 16))
     ;
  
  cp5.addButton("button1")
     .setValue(0)
     .setPosition(250, 100)
     ;
     
  cp5.addSlider("slider1")
     .setPosition(150,200)
     .setValue(100)
     .setMax(255)
     .setMin(0)
     ;
     
  cp5.addKnob("knob1").setSize(100,200)
     .setPosition(150,250)
     .setMax(255)
     .setMin(0)
     .setValue(100)
     ;
}

void draw(){
  Textfield t = (Textfield) cp5.getController("text1");
  t.setText(inData);
  cp5.getController("slider1").setValue(int(inData));
  cp5.getController("knob1").setValue(int(inData));
}

void button1(int value)
{
  println("Button pressed");
  port.write("B");
}

void serialEvent(Serial port){
  inData = port.readStringUntil('\n');
  inData = trim(inData);
}

ทดลอง Run เลยครับ ก่อน Run ให้ปิดจอ Serial monitor ก่อน ไม่งั้นจะเกิด Error ได้




ให้ดูในช่องด้านล่างจะเห็นรายการ COM ในภาพเห็นอยู่ 2 บรรทัด ของผมเลือก [1] เพราะ Arduino ต่ออยู่กับ COM4 ถ้าของใครต่อกับคอมอื่น ก็ให้ดูว่าตัวเลขในวงเล็บเป็นหมายเลขใดก็เอาหมายเลขนั้นไปใส่ในโปแกรม ตรงส่วนนี้ครับ


port = new Serial(this, Serial.list()[1], 9600);

ตัวอย่างถ้าตัวเลขในวงเล็บหน้า COM ที่ต่ออเป็น 10 ก็เปลี่ยน [1] เป็น [10]


                   port = new Serial(this, Serial.list()[10], 9600);

ตามนี้ครับ 

กด Stop หรือ ปิดหน้าต่าง แล้วทดลอง Run อีกครั้ง จะได้ผลการทดสอบตามนี้



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

คราวนี้ทดลองเลื่อนเม้าส์ไปที่ BUTTON แล้วกดปุ่มที่เม้าส์ จะเห็นว่า LED ที่ Arduino จะติด-ดับ เหมือนตอนป้อนตัวอักษร B ใน Serial monitor

ผมเดาว่าตอนนี้ถ้าได้อ่านใน Source code ก็พอจะเดาได้ว่า ส่วนไหนทำอะไร

เพื่อไม่ให้หน้านี้ยาวเกินไป ขอยกยอดไปพูดถึงรายละเอียดในหน้าต่อไป

อ้อ คราวนี้ เราได้เพิ่มการใช้งาน library 2 ตัว คือ ControlP5 และ Serial สังเกตุจากคำสั่ง import ด้านบนสุดของโค๊ด

//-----------------------------------------------------------------

เพิ่มเติมครับ ต้องขอโทษผมลืมส่วนสำคัญมากๆ คือการเอา ControlP5 มาใช้งาน ไม่งั้นก็จะคอมไฟล์ไม่ผ่าน 

การนำ ControlP5 เข้ามา ให้เข้าที่
Sketch -> Import Library... -> Add Library แล้วเลือก ControlP5 กดปุ่ม Install เมื่อติดตั้งเสร็จก็เปิดปิดโปรแกรมครั้งหนึ่ง

//------------------------------------------------------------------