Android OTG-HID的连接与通讯
文章目錄
- 前言
- 一、HID是什么?
- 二、HID設(shè)備的連接過程
- 1.配置Manifest
- 2.HID設(shè)備的枚舉與連接
- 1.設(shè)備枚舉
- 2.查找接口
- 3.指定通訊端點(diǎn)
- 4.連接設(shè)備
- 3.數(shù)據(jù)的發(fā)送與接收
- 1.發(fā)送
- 2.接收
- 總結(jié)
前言
正在開發(fā)的項(xiàng)目中多有涉及到Android通過OTG-HID與設(shè)備終端通訊的需求,所以寫下本篇博客做個(gè)筆記。
如何進(jìn)行USB接口的HID進(jìn)行開發(fā)?網(wǎng)站上很多文章并不完善,這方便的也介紹的不多,我看了很多資料,借助網(wǎng)上的一些代碼,整理了以下信息,希望能給大家提供便捷。
一、HID是什么?
USB HID類是USB設(shè)備的一個(gè)標(biāo)準(zhǔn)設(shè)備類,包括的設(shè)備非常多。HID類設(shè)備定義它屬于人機(jī)交互操作的設(shè)備,用于控制計(jì)算機(jī)操作的一些方面,如USB鼠標(biāo)、USB鍵盤、USB游戲操縱桿等。但HID設(shè)備類不一定要有人機(jī)接口,只要符合HID類別規(guī)范的設(shè)備都是HID設(shè)備。
二、HID設(shè)備的連接過程
1.配置Manifest
在Manifest中加入以下代碼
<uses-feature android:name="android.hardware.usb.host"/> <uses-permission android:name="android.hardware.usb.host" />uses-feature是用來聲明一個(gè)app在運(yùn)行時(shí)所依賴的外部的硬件或軟件特征(feature),此處是聲明Android設(shè)備必須能作為Host。uses-permission聲明Host權(quán)限。
2.HID設(shè)備的枚舉與連接
1.設(shè)備枚舉
HID設(shè)備一般通過Pid和Vid來枚舉。Pid和Vid由硬件廠商提供,也可以插在PC端通過PC端的設(shè)備管理器查看。
mUsbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); HashMap<String,UsbDevice> usbDevices = mUsbManager.getDeviceList();Log.e(TAG,"usbDevices" + usbDevices.size()) ;for (UsbDevice usbDevice : usbDevices.values()) {if( (usbDevice.getProductId() == mPid && usbDevice.getVendorId() == mVid) ){mUsbDevice = usbDevice;hadEnum = true;Log.d(TAG, "find Device");break;}}UsbManager是在android.hardware.usb包下的USB設(shè)備管理類,通過這個(gè)類的getDeviceList()方法來枚舉連接在android設(shè)備上USB設(shè)備。然后我們通過UsbDevice的getProductId()和getVendorId()方法獲取設(shè)備的Pid和Vid來找到我們的設(shè)備。
2.查找接口
private void findInterface() {if(mUsbDevice != null) {Log.d(TAG,"interfaceCounts = " + mUsbDevice.getInterfaceCount());for(int i = 0; i < mUsbDevice.getInterfaceCount() ; i++) {final UsbInterface intf = mUsbDevice.getInterface(i);Log.e(TAG,"usbClass = " + intf.getInterfaceClass());Log.e(TAG,"usbSubClass = " + intf.getInterfaceSubclass());Log.e(TAG,"usbProtocol = " + intf.getInterfaceProtocol());if(UsbConstants.USB_CLASS_HID == intf.getInterfaceClass()&& 0 ==intf.getInterfaceSubclass()&& 0 == intf.getInterfaceProtocol()) {mInterface = intf;break;}}}一個(gè)USB設(shè)備下可能存在多個(gè)Interface,我們需要通過getInterfaceClass()方法獲取接口類型來判斷是否有支持HID通訊的接口,UsbConstants.USB_CLASS_HID同樣是android.hardware.usb包的一個(gè)全局變量。表示HID設(shè)備。Hid設(shè)備的InterfaceSubclass和InterfaceProtocol一般都為0。
/*** USB class for human interface devices (for example, mice and keyboards).*/public static final int USB_CLASS_HID = 3;3.指定通訊端點(diǎn)
private void assignEndPoint() {if(mInterface != null ){for(int i = 0; i < mInterface.getEndpointCount() ; i++) {final UsbEndpoint ep = mInterface.getEndpoint(i);Log.d(TAG,"ep.getType() =" + ep.getType() );if(ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT){if(ep.getDirection() == UsbConstants.USB_DIR_IN){Log.d(TAG,"find mEpIn :MaxPacketSize = " + ep.getMaxPacketSize());mEpIn = ep;}else if(ep.getDirection() == UsbConstants.USB_DIR_OUT){Log.d(TAG,"find mEpOut :MaxPacketSize = " + ep.getMaxPacketSize());mEpOut = ep;}}}}}同樣一個(gè)接口下可能多有點(diǎn)端點(diǎn),我們也需要找到通訊端點(diǎn),首先通過getType()方法獲取端點(diǎn)類型,我們這里只需要中斷類型(UsbConstants.USB_ENDPOINT_XFER_INT),然后通過getDirection() == UsbConstants.USB_DIR_IN來獲取輸入端點(diǎn),通過ep.getDirection() == UsbConstants.USB_DIR_OUT獲取輸出端點(diǎn)。同時(shí)我們可以通過getMaxPacketSize獲取端點(diǎn)支持的最大包長。
4.連接設(shè)備
在獲取完接口和端點(diǎn)后就可以開始連接了,但是在連接之前還需要做一些權(quán)限的準(zhǔn)備工作,因?yàn)锳ndroid的限制,在使用Usb設(shè)備之前需要用戶授權(quán)。
首先我們定義一個(gè)申請(qǐng)權(quán)限的Action:
申請(qǐng)權(quán)限的結(jié)果是通過廣播返回的,所以我們需要定義一個(gè)廣播接收器并注冊(cè)。
private class USBReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 這里可以拿到插入的USB設(shè)備對(duì)象UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);switch(intent.getAction()) {case UsbManager.ACTION_USB_DEVICE_ATTACHED: // 插入U(xiǎn)SB設(shè)備Log.e(TAG, "USB插入");break;case UsbManager.ACTION_USB_DEVICE_DETACHED: // 拔出USB設(shè)備Log.e(TAG, "USB拔出");isUsbConnect = false;stopConnect();break;case ACTION_USB_PERMISSION:Log.e(TAG, "申請(qǐng)授權(quán)");if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {//user choose YES for your previously popup window asking for grant perssion // for this usb devicesynchronized(syncObj) {syncObj.notifyAll();}} else {//user choose NO for your previously popup window asking for grant perssion // for this usb devicesynchronized(syncObj) {syncObj.notifyAll();}}break;default:break;}}}private void registerUSBReceiver(Context mContext) {IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);mUsbReceiver = new USBReceiver();mContext.registerReceiver(mUsbReceiver, filter);}由于項(xiàng)目需要,所以我貼的代碼還對(duì)設(shè)備的插入和拔出做了處理,如果你不需要可以不用監(jiān)聽。
好了準(zhǔn)備好之后我們就可以開始連接設(shè)備了。
private int openDevice() {if (mUsbDevice == null || mInterface == null) {return -16;}// 在open前判斷是否有連接權(quán)限;對(duì)于連接權(quán)限可以靜態(tài)分配,也可以動(dòng)態(tài)分配權(quán)限,可以查閱相關(guān)資料PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);try {if (!mUsbManager.hasPermission(mUsbDevice)) {mUsbManager.requestPermission(mUsbDevice, pi);synchronized(syncObj){syncObj.wait();}if (!mUsbManager.hasPermission(mUsbDevice)) {return -17;}}UsbDeviceConnection conn = mUsbManager.openDevice(mUsbDevice);if(conn == null){return -18;}if(conn.claimInterface(mInterface,true)){mConnection = conn;return 0;}else {conn.close();return -18;}} catch (Exception e) {e.printStackTrace();return -18;}}在連接前我們先確認(rèn)一下是否有權(quán)限,如果沒有就通過requestPermission方法來申請(qǐng)權(quán)限,并在用戶授權(quán)結(jié)束后再次判斷一下是否已有權(quán)限,因?yàn)橛脩羧绻c(diǎn)了拒絕,設(shè)備還是沒有權(quán)限。然后通過UsbManager.openDevice()來連接設(shè)備,最后通過UsbDeviceConnection .claimInterface()方法確認(rèn)連接,至此我們的HID設(shè)備就正式連接完成了。
3.數(shù)據(jù)的發(fā)送與接收
設(shè)備連接完成之后,我們就可以通過先前獲取的發(fā)送、接收端點(diǎn)來進(jìn)行通訊了。
1.發(fā)送
int transferred = mConnection.bulkTransfer(mEpOut, data, data.length, 1000);四個(gè)參數(shù)分別代表:發(fā)送端點(diǎn)mEpOut、待發(fā)送數(shù)據(jù)、待發(fā)送數(shù)據(jù)長度、超時(shí)時(shí)間(ms),返回值為實(shí)際發(fā)送數(shù)據(jù)長度(失敗為-1)。
上文我們提到過端點(diǎn)有最大包長限制,實(shí)際使用中,超過最大包長android底層依然會(huì)給你做分包,但是數(shù)據(jù)超出過大,會(huì)存在數(shù)據(jù)丟失問題,所以建議大數(shù)據(jù)做分包發(fā)送處理。
2.接收
接收數(shù)據(jù)我們通過開啟一個(gè)線程循環(huán)接收。
private class RecvThread extends Thread {byte[] recv_buff = new byte[MaxPackSize];int recv_len;@Overridepublic void run() {super.run();while(true) {if(!isUsbConnect) {return;}try {recv_len = mConnection.bulkTransfer(mEpIn, recv_buff, recv_buff.length, 1000);if(recv_len > 0) {String hex = Util.byte2HexStr(recv_buff, 0, recv_len);for(int i = 0; i < recv_len; i++) {queue.offer(recv_buff[i]);}}} catch (Exception e) {e.printStackTrace();}}}}四個(gè)參數(shù)分別代表:接收端點(diǎn)mEpIn、欲接收數(shù)據(jù)、欲接收數(shù)據(jù)長度、超時(shí)時(shí)間(ms),返回值為實(shí)際接收數(shù)據(jù)長度(失敗為-1)。我們這里按最大包長來接收。原因和發(fā)送一樣。
總結(jié)
以上就是今天要講的內(nèi)容,本文僅僅介紹了Android設(shè)備通過OTG-HID協(xié)議和HID設(shè)備的簡單通訊,希望能對(duì)你有所幫助。總結(jié)
以上是生活随笔為你收集整理的Android OTG-HID的连接与通讯的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQLsever数据库实例是啥子
- 下一篇: android打印功能,Android通