2014年1月22日 星期三

Android socket via mini USB

Socket

簡單來講socket是基於TCP/IP通訊協定所產生出來的一個傳輸介面,源自於UNIX,現在已經被廣泛的應用在通訊界面上

Socket pair

Socket的定義就是一個IP加上一個TCP/UPD的port,例如:127.0.0.1:11554
而一組連線必須要有一個Source socket及Destination socket,我們稱之為Socket pair

Socket通訊協定的原理

Socket通訊

在這裡我們使用Android當作Server端,電腦當作Client端(使用C#),透過mini USB連線

Android socket server (Java)

要在Android端建立socket server,我們可以用java內建的socket server api來實做,另外透過DataInputStream及DataOutputStream來接收及傳送資料(在這裡我們使用自定義的port:15144):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
String line;
Double testdata = 10.5;
try {
         //建立socket server, port number:15144
         ServerSocket = new ServerSocket(15144);
         while (true) {
              //等待client端連線
              Socket client = ServerSocket.accept();
              //利用DataStream傳送及接收資料
              DataInputStream in = new DataInputStream(client.getInputStream());
              DataOutputStream out = new DataOutputStream(client.getOutputStream());
//接收一行資料
              line = in.readLine();
//Delay 5
              Thread.sleep(5000);
//傳送一個Double大小的資料
              out.writeDouble(testdata);
//關閉socket
              client.close();
} catch (IOException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
}
※socket server一定要包在try catch裡面做,因為有可能會丟出exception
※建議要在另外一個thread做,因為socket server會停在ServerSocket.accept()等待client連線,若是在主thread執行的話,很容易會被Android系統kill掉,而產生ANR

電腦端socket client (C#)

在這裡利用C#來建立socket client來與Android的socket server做連接,可以利用C#內建的client api來實做(在這裡我們使用自定義的port:15134):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
byte[] rd = new byte[8];
byte data = 10;
double result;
//建立一個TCP Socket協定
Socket mSocket = new Socket(AddressFamily.InterNetwork,
 SocketType.Stream,
 ProtocolType.Tcp);
//連線至Socket server,因為是透過USB連線,所以IP位置就是127.0.0.1
mSocket.Connect(IPAddress.Parse("127.0.0.1"),int.Parse("15134"));
//傳送資料至socket server
mSocket.Send(data);
//socket server接收資料
mSocket.Receive(rd,8,0);
/**因為JavaDataOutputStream回傳回來的Double剛好與C#的相反,所以轉置陣列**/
byte[] temp = new byte[8];
temp[0] = rd[7];
temp[1] = rd[6];
temp[2] = rd[5];
temp[3] = rd[4];
temp[4] = rd[3];
temp[5] = rd[2];
temp[6] = rd[1];
temp[7] = rd[0];
//將接收到的陣列轉換回Double型態
result = BitConverter.ToDouble(temp, 0);
//關閉socket
mSocket.Close();

ADB設定

另外在連線之前,必須要設定Android的port,所以要先用透過adb來開通port:
$adb forward tcp:15134 tcp:15144
                             ↑               ↑
                       client port  server port

結語

透過以上的程式碼,就可以達到Android device與電腦溝通傳送資料,且傳輸速度又很快,所以就能夠透過USB傳輸大量的資料到電腦端,若是Android device有連到網路上的話,也可以透過IP位置及port number來直接使用網路與電腦做資料傳輸,所以socket已經漸漸的成為一個標準的傳輸協定了。

2013年12月9日 星期一

TSLIB on Android gingerbread

TSLIB,顧名思義就是Touch Screen Library
聽說原本是拿來做滑鼠校正的,衍生出來觸控螢幕校正

基本上他的原理並不難懂,他是使用五點校正來算校正值,公式如下:
x = (Origin.x * xscale) + (Origin.y * xymix) + xoffset;
y = (Origin.x * yxmix) + (Origin.y * yscale) + yoffset;
如果對於校正的理論有興趣的話,可以參考這份文件:
http://www.tij.co.jp/jp/lit/an/slyt277/slyt277.pdf

------------------------------------------------------------------------------------------------------------
首先呢,我們要先下載for Android的tslib,網路上有很多地方可以下載
我下載的是下面這個連結的code:(裡面也有說明,可以參考)
https://github.com/etmatrix/tslib

Linux下的下載步驟:
1、首先,先切換到Android source code的目錄下的externel資料夾:
#cd <Android source>/external/

2、再來下載tslib source code:
#git clone https://github.com/etmatrix/tslib.git

3、再來切換到tslib的source code目錄下,對tslib做設定:
#cd tslib
#autoreconf -ivs
#./configure

4、設定完之後,切換回Android source code的目錄:
#cd <Android source>

5、最後,編譯tslib:
#make libts
#make ts/plugins/input
#make ts/plugins/pthres
#make ts/plugins/dejitter
#make ts/plugins/linear
#make ts/plugins/variance
#make ts_calibrate
#make ts_test

------------------------------------------------------------------------------------------------------------
編譯完tslib之後,接下來就是要開始把編譯完的tslib丟到Android系統裡了
由於我們要透過adb(Android Debug Bridge)來把資料丟到系統裡
若是您沒有adb的話,可以在google下載SDK,在platform-tools裡就會有了

SDK下載連結:
https://developer.android.com/sdk/index.html

再來我們要開始把編譯好的tslib丟進Android系統裡:
#adb push out/target/product/generic/system/lib/libts.so /system/lib/libts.so
#adb push out/target/product/generic/system/lib/ts/plugins/dejitter.so /system/lib/ts/plugins/dejitter.so
#adb push out/target/product/generic/system/lib/ts/plugins/input.so /system/lib/ts/plugins/input.so
#adb push out/target/product/generic/system/lib/ts/plugins/linear.so /system/lib/ts/plugins/linear.so
#adb push out/target/product/generic/system/lib/ts/plugins/pthres.so /system/lib/ts/plugins/pthres.so
#adb push out/target/product/generic/system/lib/ts/plugins/variance.so /system/lib/ts/plugins/variance.so
#adb push out/target/product/generic/system/bin/ts_calibrate /system/ bin/ts_calibrate
#adb push out/target/product/generic/system/bin/ts_test /system/bin/ts_test

丟進去之後,透過ADB連進Android的shell裡:
#adb shell

進到Android的shell裡之後,我們要先查觸控螢幕的event編號,可以用以下指令查:







在這裡我的觸控螢幕是event0,所以底下的環境變數就要設定/dev/input/event0

連進去之後,要來做環境變數設定:
首先,我們必須先停掉Android系統,不然沒辦法寫校正參數進去:
#stop zygote

停掉Android系統之後,再來輸入環境變數:
#export TSLIB_TSDEVICE=/dev/input/event0
#export TSLIB_CALIBFILE=/system/etc/pointercal
#export TSLIB_CONFFILE=/system/etc/ts.conf 
#export TSLIB_PLUGINDIR=/system/lib/ts/plugins 
#export TSLIB_FBDEVICE=/dev/graphics/fb0 
#export TSLIB_CONSOLEDEVICE=/dev/tty

設定完環境變數之後,就可以開始做校正:
#ts_calibrate

校正畫面:





















校正完之後,會把校正檔案寫在/etc/pointercal,校正參數的格式如下:
xscale  xymix  xoffset  yxmix  yscale  yoffset  divider  x  y

校正完之後,我們可以利用tslib內建的測試畫面來測試剛剛校正的位置:
#ts_test

測試畫面:

當然 底下畫得很醜的圖案是我畫的XD

到這裡基本上就已經校正完成了,再來就是要在Android系統上套用校正參數
我的作法是修改Android系統的source code,只修改一個檔案就可以套用校正參數:
在/framework/base/libs/ui/InputReader.cpp下:

首先先定義校正參數的struct
struct PointTouchCalibrate
{
           float xscale;
           float xymix;
           float xoffset;
           float yxmix;
           float yscale;
           float yoffset;
           bool HaveTouchCalibration;
}mPointTouchCalibrate;

定義完結構之後,加入讀取校正參數的code:
@void TouchInputMapper::parseCalibration()
//Load touchscreen calibration data
           int t_xscale;
           int t_xymix;
           int t_xoffset;
           int t_yxmix;
           int t_yscale;
           int t_yoffset;
           int divider
    FILE *file = fopen("/system/etc/pointercal","r");
   if(FILE)
   {        
          fscanf(file,"%d%d%d%d%d%d%d",
                    &t_xscale,&t_xymix,&t_xoffset,&t_yxmix,&t_yscale,&t_yoffset,&divider);
          fclose(file);
          mPointTouchCalibrate.xscale = (float)t_xscale / divider;
          mPointTouchCalibrate.xymix = (float)t_xymix / divider;
          mPointTouchCalibrate.xoffset = (float)t_xoffset / divider;
          mPointTouchCalibrate.yxmix = (float)t_yxmix / divider;
          mPointTouchCalibrate.yscale = (float)t_yscale / divider;
          mPointTouchCalibrate.yoffset = (float)t_yoffset / divider;
          mPointTouchCalibrate.HaveTouchCalibration = true;
   }else
   {
          mPointTouchCalibrate.HaveTouchCalibration = false;
    }

之後就要來修改Android坐標軸的算法了:
@void TouchInputMapper::dispatchTouch
//X and Y
//float x = float(in.x - mLocked.xOrigin) * mLocked.xScale;
//float y = float(in.y - mLocked.yOrigin) * mLocked.yScale;
float x,y,x_temp,y_temp;
if(mPointTouchCalibrate.HaveTouchCalibration)
{
        x_temp = float(in.x - mLocked.xOrigin);
        y_temp = float(in.y - mLocked.yOrigin);

        x = (x_temp * mPointTouchCalibrate.xscale) +
              (y_temp * mPointTouchCalibrate.xymix) + mPointTouchCalibrate.xoffset;

        y = (x_temp * mPointTouchCalibrate.yxmix) +
              (y_temp * mPointTouchCalibrate.yscale) + mPointTouchCalibrate.yoffset;
}else
{
        x = float(in.x - mLocked.xOrigin) * mLocked.xScale;
        y = float(in.y - mLocked.yOrigin) * mLocked.yScale;
}

修改完source code之後,重新compile就可以把tslib的校正參數套用進去了!

Reference:

Android Gingerbread Touchscreen Calibration