วันศุกร์ที่ 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