前面寫了一個方法是透過無線藍牙 -- Android Bluetooth 應用之 HelloBTUart(RS-232)
本文延續這個問題,提供了第二個答案就是直接使用有線的USB來做 Virtual COM port (VCP) 。
Android自從Honeycomb (3.1)版本後就開始支援USB hosting機制,也就是說從3.1版後Android提供了android.hardware.usb類別來使用,把早先Android從Accessory的角色變成Host及Accessory(Device)兩種都可以的角色。
至目前為止,Android在USB hosting機制從一開始只支援USB Keyboard、Mouse到現在Jelly Bean陸續已經可以支援到USB儲存裝置、攝影機、網卡、搖桿、....等總類非常繁多。
所以,本篇實作的部分主要是如何在Android上使用USB裝置(UsbDevice API)。
【免責聲明】:本實作有可能會造成Android裝置或是相關USB的損毀,恐危及到相關設備的保固或維修,請評估是否可以自行承擔相關風險,如無把握請勿嘗試將不負任何責任。
實作環境:
ASUS 變形金剛2(TF201)
Google nexus 7
USB to RS-232 (PL-2303)
OTG線
開發環境:
Win 7 SP1
Java 1.6.0_35
Eclipse 3.7.2
Android SDK r20.0.3
前置作業
1.準備OTG線一條:
所謂USB OTG(On-The-Go)簡單來說就是原先當Devices裝置可以變成Host裝置來控制其他USB Devices,至於要詳細認識OTG的話可以參考許永和老師的USB OTG這篇文章。
或者參考USB OTG官方網站。
製作或購買OTG線:
Google nexus 7 所採用的是標準的Micro-USB,2007年1月4日USB實裝論壇(USB-IF)頒布了Micro-USB的插頭標準,目前在歐盟也訂定了充電裝置接頭一律使用Micro-USB。
所以目前除了 nexus 7外目前很多手機、平板的充電接頭都是使用Micro-USB當作充電接頭。
所以如果喜歡DIY的人可以到電子材料行購買以下材料:
Micro-USB 公頭 x1
USB標準USB母頭(A型USB插座) x1
USB線材(紅黑綠白)
然後,按照下圖進行焊接:
從上圖我們可以看出Micro-USB分別+5V、D+、D-、ID、GND等五隻接腳,其中在Android裝置上定義 :
1.ID、GND短路變成OTG線。
2.ID、GND斷路變成充電線。
3.ID、GND接100K歐姆電阻變成OTG/充電線。(目前網路上確認SONY手機可以這樣做,我測試過在nexus 7上這個方式沒作用)
上述詳細資訊請參考:https://sites.google.com/site/sonicboomworld/my-projects/otg-diagrams
另外,比較進階的可以改造USB HUB(如下圖),這樣Android就可以使用更多USB裝置了(在 nexus 7上還是沒有充電的功能)。
不過.........上述只是把一些觀念作解釋,如果懶得DIY的 ....最簡單的方法就是花錢購買現成的就可以了。目前手上購買的USB OTG線是在台中電子街今X電子所購得,價格大約在NT$60左右。
2.基本觀念:
在Android裝置中,USB可以扮演Host 及 Accessory角色。
http://developer.android.com/guide/topics/connectivity/usb/index.html
程式中配合這兩種角色的不同會去呼叫不同的API;
如果Android的供電設備作為USB Host則使用UsbDevice API,例如 :滑鼠、搖桿、攝影機、USB集線器等。
http://developer.android.com/guide/topics/connectivity/usb/host.html
如果USB設備當做USB Host ,那就要使用UsbAccessory API。例如: IOIO、Arduino 之類。
http://developer.android.com/guide/topics/connectivity/usb/accessory.html
簡單來說,哪個裝置可以供電誰就是Host,兩者細節部分請參考官網說明。
在程式撰寫過程中,必須先弄清楚一些USB基本觀念的部分,例如 USB Architectural 、Data Flow Types、USB Communication Flow、VID、PID、Endpoint、....等等。否則不但看不懂程式外,連改程式都會不知如何去著手。礙於篇幅及主題,USB觀念部分除了到Google上搜尋外,另外就打聽一下敏哥何時會在逢甲星期四晚上USB課程,好好的吸收一下(還好我有認真聽敏哥上課!!)。
另外要討論的是需不需要 root 權限的問題,類似與硬體連接在Android下通常會需要 root 權限,主要是更改/dev目錄下裝置的權限,假如你使用的是 "Java Native Interface (JNI) Android實戰篇(使用NDK) -- HelloUart" 這篇的方式,你只要檢查當OTG連線時在有沒有 /dev/ttyUSBx的裝置,如果已有的情況下使用chmod 777 方法直接更改 /dev/ttyUSBx的權限,這時該文章的HelloUart可以達到同樣的功能。
同樣道理,如果你寫的是一個使用 USB 隨身碟,最簡單的方式寫個程式把該裝置掛載(mount)到一個目錄下就行了,這個時候同樣也會更改到 /dev目錄下 USB 隨身碟裝置的權限,所以同樣也需要用root權限。
至於使用root權限的程式撰寫方式,會再另外寫一篇文章來討論。
本篇主要是使用Android提供 USB 標準類別來做,所以執行本文的程式是不需要root的權限。
另外說明一下既然使用的是Android提供 UsbDevice API類別來寫程式,所以這篇文章的內容就跟NDK無關囉!!(不會用到C,不要再問ㄡ....^_^)
3.程式:
本文主要是實作以 nexus 7當作USB Host,以下是程式說明:
(1)獲取與設備通訊的權限
AndroidManifest.xml
在<intent-filter>與</intent-filter>加入
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
在</activity>前面加入
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" />
在專案目錄 res 中建立一個 xml目錄,在xml目錄下建立一個device_filter.xml檔案,其內容定義USB的PID與UID這必須要視你連上的USB裝置而定,相關內容如下:
res/xml/device_filter.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1659" product-id="8963" />
</resources>
上述內容是以PL-2303 (USB to RS-232) 為範例,其中PS-2303的VID是067B轉 換成10進制等於1659;PID是2303轉 換成10進制等於8963。
上述的宣告,主要是使用在當USB插入時,系統偵測到鎖定一的VID及PID,會跳出如下圖的畫面;當選則確定後就會執行程式。
詳細參考:Manifest and resource file examples
(2)主程式部分我是參考 INDI server ( indiserver )所提供的原始碼中 PL2303 USB Serial Converter Driver (PL2303driver.java) ,網址如下:
https://code.google.com/p/indiserver/source/browse/INDIserver/branches/version2/src/de/hallenbeck/indiserver/communication_drivers/PL2303driver.java
主程式撰寫的部分,主要利用上述的程式外,程式內容大致上只要依序做到:
a. Initialization
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
mUsbDevice = (UsbDevice) getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
mUsbIntf = mUsbDevice.getInterface(0);
mEndpointOut = mUsbIntf.getEndpoint(1); // endpoint addr 0x2 = output bulk
mEndpointIn = mUsbIntf.getEndpoint(2); // endpoint addr 0x83 = input bulk
b.open()
protected boolean openDevice() {
try {
mUsbConnection = mUsbManager.openDevice(mUsbDevice);
mUsbConnection.claimInterface(mUsbIntf, true);
Log.d(TAG, "Device opening... ");
return true;
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
mUsbConnection = null;
}
return false;
}
c. Register BroadcastReceiver
d. setup(BaudRate.B9600, DataBits.D8, StopBits.S1, Parity.NONE); -->See PL2303driver.java
e. 用Thread或Timer做顯示 --> getInputStream() -->See PL2303driver.java
f. 用Button.OnClickListener做送出 --> getOutputStream() -->See PL2303driver.java
g. close()
protected boolean closeDevice() {
try {
mUsbConnection.releaseInterface(mUsbIntf);
mUsbConnection.close();
mUsbConnection = null;
mEndpointOut = null;
mEndpointIn = null;
Log.d(TAG, "Device closed. ");
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
return false;
}
至於程式碼部分,由於目前身邊工作太忙了沒時間可以好好的完整寫出,加上實在太多Bug及沒有考慮的因素,所以還在研究當中本文先秀出按照上述a~g個步驟所述方法目前初步的結果並且證明可行。
程式碼部分請參考文章後面所列出的參照說明的所有網頁,修改一下應該就可以寫出下面的結果了;日後待有空將程式寫完整後再慢慢分享程式碼。
4.執行結果:
以下是我做的初步執行結果:
環境:
(1) Nexus 7 <--> PL-2302 (USB to RS232) <--> GPS接收器 (沒座標,單純當作RS232產出訊息使用)
程式開始:
接著開始接收訊息:
執行過程:
後記:
1.以目前實作過程中USB裝置會使用Android裝置上的電力增加耗電量,加上Nexus 7 還找不到可以同時OTG也同時充電的方法,所以如果克服這個部分USB在Android裝置上利用會更加完整,或許未來有出 Nexus 7 Dock座應該就有解了。
[2013/02/01 測試了 ASUS原廠的Nexus 7 Dock座,同樣無法達到在OTG下同時充電]
3. 另外一種Debug方式比較複雜而且需要root還要花錢就是使用QuickSSHd,然後透過SSH client連到Android裝置上執行logcat。
4. Google play上類似的軟體:
OTG UART 超級終端機 Free
Slick USB 2 Serial Demo
5. 通常當遇到使用無線(藍牙)不穩定時,可以參考一下有線的方式連結,或許可以減少一些無謂的干擾。
==============2013/05/20補充=====================
原始碼部分,一直以來沒有時間可以去整理好,這幾天剛好在網路上看到有家廠商有釋出PL-2303原始碼,記得使用別人程式碼要尊重相關著作權以免觸法。
http://www.oneping.com.tw/fileDownload.htm
下面連結是他們測試方法:
http://www.oneping.com.tw/Technical_Articles/t-PL2303-Android.htm
==============2013/11/24補充=====================
Prolific官方也提供了PL-2303的SDK,詳見:
http://www.prolific.com.tw/US/ShowProduct.aspx?p_id=230&pcid=41
==============相關閱讀=====================
Java Native Interface (JNI) Android實戰篇(使用NDK) -- HelloUart
Android Bluetooth 應用之 HelloBTUart(RS-232)
參考:
1. INDI server ( indiserver ) , PL2303driver.java
https://code.google.com/p/indiserver/source/browse/INDIserver/branches/version2/src/de/hallenbeck/indiserver/communication_drivers/PL2303driver.java
2.嘗試在Android的USB主機功能。(日文,看不懂?!沒關係,用Google翻譯看吧!!)
http://blog.livedoor.jp/jun_dime/archives/51720766.html
3.Android USB Host + USB VCP Driver
http://www.ezequielaceto.com.ar/techblog/?cat=44
4.Simple OTG cable diagram
https://sites.google.com/site/sonicboomworld/my-projects/otg-diagrams
5.USB Host and Accessory, Android Developers
http://developer.android.com/guide/topics/connectivity/usb/index.html
6.USB Host
http://developer.android.com/guide/topics/connectivity/usb/host.html
感謝蚊子提供寶貴文章。
回覆刪除最近想讓視訊攝影機透過OTG連結到手機上,請問我該怎麼做?
回覆刪除http://brain.cc.kogakuin.ac.jp/research/usb-e.html
這是我在網路上找到一篇成功的例子,
他說的要在內核配置中開啟一些參數,但那個配置文件是在哪裡啊?
麻煩大大能幫小弟講解一下~~謝謝
這個網頁的程式可以用,不過你要確定:
刪除1.你的cam有支援V4L2(Video4Linux),http://www.ideasonboard.org/uvc/你到這個網站就可以查到了。
2.這個程式使用NDK來寫與本篇不太一樣。
3.NDK程式直接呼叫/dev/video0的裝置,所以你必須要root後執行chmod 777 /dev/video0
3.執行他的程式就可以用了。
不好意思因為剛接觸ANDROID又對LINUX不熟......
回覆刪除大大您的意思是只要執行chmod 777就能夠執行那個程式了??不需要修改內核配置嗎?
還有那個/dev/video0路徑在哪裡?我用手機裡的R.E管理器找不到 = =
1.目前試過ASUS 變形金剛2(TF201) 及 Google nexus 7 這兩台可以不用修改kernel可以抓到/dev/video0, HTC手機不行,手上也沒其他裝置可測試所以其他就不清楚了.
刪除2.chmod 777 主要是權限設定給uid為shell可以使用,權限問題非三言兩語就說清楚這部份如果對Unix/Linux檔案系統不熟悉的話,可能找有關Linux檔案權限的或書局用力K一下了...
您好!
回覆刪除很感謝您的分享!讓我多了解了很多專業知識~
其實我很想寫類似您的或是"OTG UART 超級終端機 Free"這樣的程式~
但是我對於Java 及 Android的涉獵都很有限~
只有看過一本"Android 初學特訓班"
主要學到的都是版面layout及component的功能~
就算您提供了開發步驟但我還是遇到困難~
小弟太愚昧了~所以可以請問步驟a的程式要加在哪裡呢!
是不是在MainActivity.java呢?
因為我可以說是沒有Android的設計經驗~
斗膽請問您是否願意分享source code供參考呢?
謝謝!
大大~我想請問一下
回覆刪除如果Nexus 7<-->OTG<-->USB HUB<-->{PL-2302 (USB to RS232) <-->GPS接收器.RFID讀卡機.等...
這樣的架構目前ANDROID做得到嗎?
可以的,參考本文及連結出去的那個原始碼網站可以完成這樣的需求。
刪除另外需要注意的是:
1.PL-2303比較新版本的我還沒時間測試,目前我還不確定新版的PL-2303 Nexus 7能否使用。
2.USB底層的觀念要弄清楚,不然程式很難寫。
請問,如果是使用FDTI 的FT232R USB UART晶片
回覆刪除在程式上撰寫需要注意的地方??
1.PID,VID
刪除2. /dev目錄下有沒有抓到裝置,如果沒有請確認OTG線(換別家OTG試試),另外就是Kernel有沒有驅動(可以用dmesg檢查)
3.USB底層的觀念要弄清楚,Data Flow Types、USB Communication Flow、Endpoint、....等等要先K書研究一下,其他只要按照我上面寫的a~g項目去做就行了,參考中有一些網頁有你可以逛逛看^^
请教大大个问题:
回覆刪除我用的是UsbDevice API,我想实现的需求是:一个U盘插在我的Android设备上的USB Host口,1.能检测到U盘
2.在我的程序里界面上能显示U盘的内容(有那些文件夹,文件,类似于资源管理器)。
现在确实能检测到U盘,第一条实现了
第2条还没实现,我还没想到怎么遍历U盘里文件或文件夹
UsbDevice API里有两种数据传输方法
1.controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
Performs a control transaction on endpoint zero for this device.
2.bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout)
Performs a bulk transaction on the given endpoint.
这两种方法也只是传输数据,要实现我说的显示U盘的内容(有那些文件夹,文件,类似于资源管理器)怎么实现那?
请大大指教下
谢谢大大的分享
回覆刪除有那位大大知道不?盼解答
回覆刪除請問可以接案嗎?
回覆刪除我需要OTG->傳送接收HEX 範例與控制器溝通
email:wcs975@gmail.com
OK
刪除敏哥您好
回覆刪除之前已經在Android 上實做該USB功能並且也可正常使用
但最近(前段時間) Android 手機或平板更新後, 插上USB都出現無法辨識裝置
想請教一下是否Android sdk 做了什麼修改, 或是我有什麼地方沒有注意到的
如果能解答...萬分感激
檢查一下開發者功能
回覆刪除你好,接觸android沒多久,對於架構還不太熟
回覆刪除我想要做的是
android手機(app) <-->usb OTG<-->FIDI FT311D(usb to uart)<-->sensor module
我想要在app上面做一些操作去控制我的sensor module
然後從sensor module讀回一些binary,經過轉換機制在app上面顯示成圖案
請問這樣做得到嗎?
可否提供些意見,感恩