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

ตอนที่ 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 ได้นะครับ






ไม่มีความคิดเห็น:

แสดงความคิดเห็น