項目地址:點擊打開
使用java開發的好處就是跨平台,基本上java的開發的程序在linux、mac、MS上都可以運行,對應這java的那句經典名言:一次編寫,到處運行。這個項目里面有兩種包選擇,一個是low-level(libus)一個是high-level(javax-usb),相關的優缺點在官方網站上已經說明了,我這里就不翻譯了,不過前者好像基於libusb已經好久不更新了,所以還是選擇后者。
配置:你需要在你包的根目錄下新建一個名為:javax.usb.properties的文件,里面的內容是這樣的:
javax.usb.services = org.usb4java.javax.Services
查找usb設備,其實通過usb通信流程大體上都是一致,之前我做過android與arduino通過usb通信,然后java通信走了一遍之后發現是一樣的。USB 設備在一棵樹上進行管理。這樹的根是所有物理根集線器連接到一個虛擬的 USB集線器。更多的集線器可以連接到這些根集線器和任何集線器可以有大量的連接的 USB設備。
通常,您需要在使用它之前搜索特定設備,下面的是一個例子如何掃描與一個特定的供應商和產品 id 的第一個設備的設備:
public UsbDevice findDevice(UsbHub hub, short vendorId, short productId) { for (UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices()) { UsbDeviceDescriptor desc = device.getUsbDeviceDescriptor(); if (desc.idVendor() == vendorId && desc.idProduct() == productId) return device; if (device.isUsbHub()) { device = findDevice((UsbHub) device, vendorId, productId); if (device != null) return device; } } return null; }
接口
當你想要與一個接口或者這個接口的端點進行通信時,那么你在使用它之前必須要claim它,並且當你結束時你必須釋放它。比如:下面的代碼:
UsbConfiguration configuration = device.getActiveUsbConfiguration(); UsbInterface iface = configuration.getUsbInterface((byte) 1); iface.claim(); try { ... Communicate with the interface or endpoints ... } finally { iface.release(); }
可能出現的一種情況是你想要通信的接口已經被內核驅動使用,在這種情況下你可能需要通過傳遞一個接口策略到claim方法以此嘗試強制claim:
iface.claim(new UsbInterfacePolicy() { @Override public boolean forceClaim(UsbInterface usbInterface) { return true; } });
需要注意的是,接口策略只是為了實現基礎USB的一個提示.接口策略在MS-Windows上將被忽略,因為libusb在windows上不支持分派驅動。
同步 I/O
這個example發送8個字節到端點0x03:
UsbEndpoint endpoint = iface.getUsbEndpoint(0x03); UsbPipe pipe = endpoint.getUsbPipe(); pipe.open(); try { int sent = pipe.syncSubmit(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }); System.out.println(sent + " bytes sent"); } finally { pipe.close(); }
這個example是從端點0x83讀取8個字節:
UsbEndpoint endpoint = iface.getUsbEndpoint((byte) 0x83); UsbPipe pipe = endpoint.getUsbPipe(); pipe.open(); try { byte[] data = new byte[8]; int received = pipe.syncSubmit(data); System.out.println(received + " bytes received"); } finally { pipe.close(); }
異步 I/O
同步I/O操作與異步I/O操作類似,僅僅是使用asyncSubmint方法取代syncSubmit方法。syncSubmit方法將會阻塞直到請求完成,而asyncSubmit不會阻塞並且立即返回,為了接受請求的返回,你必須為Pipe添加一個監聽器,像下面這樣:
pipe.addUsbPipeListener(new UsbPipeListener() { @Override public void errorEventOccurred(UsbPipeErrorEvent event) { UsbException error = event.getUsbException(); ... Handle error ... } @Override public void dataEventOccurred(UsbPipeDataEvent event) { byte[] data = event.getData(); ... Process received data ... } });
遇到的問題:在Linux上不能打開設備Device,其實是權限的問題導致的,你應該知道在Linux下權限分配是一門很重要的學問,其實該問題在官方網站的FAQ一欄已經提供了解決的辦法,地址:點擊打開
在Linux環境下,你需要給你想要通信的usb設備寫一個許可文件。在此之前,你可以以root用戶運行你的程序檢查設備是否可以獲取,如果有上述操作有作用,那么我推薦配置下udev以便當設備連接上后你的用戶擁有對設備寫的權限
SUBSYSTEM=="usb",ATTR{idVendor}=="89ab",ATTR{idProduct}=="4567",MODE="0660",GROUP="wheel"
需要該更改的就是PID、VID、GROUP,關於PID和VID信息以及查看相關的接口、端口信息的查看,linux下可以使用一個叫usbview的軟件,軟件安裝很簡單,僅僅一條命令:
sudo apt-get insall usbview
注意啟動的時候需要權限,而且還需要以圖形界面顯示(sudo usbview在我的Linux Mint沒有反應的):
sudo gksu usbview
參考官方寫的一段簡單的代碼:
package nir.desktop.demo; import java.util.List; import javax.usb.UsbConfiguration; import javax.usb.UsbConst; import javax.usb.UsbControlIrp; import javax.usb.UsbDevice; import javax.usb.UsbDeviceDescriptor; import javax.usb.UsbEndpoint; import javax.usb.UsbException; import javax.usb.UsbHostManager; import javax.usb.UsbHub; import javax.usb.UsbInterface; import javax.usb.UsbInterfacePolicy; import javax.usb.UsbPipe; import javax.usb.event.UsbPipeDataEvent; import javax.usb.event.UsbPipeErrorEvent; import javax.usb.event.UsbPipeListener; public class UsbConn { private static final short VENDOR_ID = 0x2341; /** The product ID of the missile launcher. */ private static final short PRODUCT_ID = 0x43; // private static final short VENDOR_ID = 0x10c4; // // /** The product ID of the missile launcher. */ // private static final short PRODUCT_ID = -5536; private static UsbPipe pipe81, pipe01; /** * 依據VID和PID找到設備device * * @param hub * @return */ @SuppressWarnings("unchecked") public static UsbDevice findMissileLauncher(UsbHub hub) { UsbDevice launcher = null; for (UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices()) { if (device.isUsbHub()) { launcher = findMissileLauncher((UsbHub) device); if (launcher != null) return launcher; } else { UsbDeviceDescriptor desc = device.getUsbDeviceDescriptor(); if (desc.idVendor() == VENDOR_ID && desc.idProduct() == PRODUCT_ID) { System.out.println("找到設備:" + device); return device; } } } return null; } public static void sendMessage(UsbDevice device, byte[] message) throws UsbException { UsbControlIrp irp = device .createUsbControlIrp( (byte) (UsbConst.REQUESTTYPE_TYPE_CLASS | UsbConst.REQUESTTYPE_RECIPIENT_INTERFACE), (byte) 0x09, (short) 2, (short) 1); irp.setData(message); device.syncSubmit(irp); } /** * 注意權限的配置問題,在linux下可能無法打開device,解決辦法參考官方的FAQ * * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub UsbDevice device; try { device = findMissileLauncher(UsbHostManager.getUsbServices() .getRootUsbHub()); if (device == null) { System.out.println("Missile launcher not found."); System.exit(1); return; } UsbConfiguration configuration = device.getActiveUsbConfiguration();//獲取配置信息 UsbInterface iface = configuration.getUsbInterface((byte) 1);//接口 iface.claim(new UsbInterfacePolicy() { @Override public boolean forceClaim(UsbInterface arg0) { // TODO Auto-generated method stub return true; } }); // for (UsbEndpoint endpoints : (List<UsbEndpoint>) iface // .getUsbEndpoints()) { // System.out.println("--->"+endpoints.getUsbEndpointDescriptor()); // } UsbEndpoint endpoint81 = iface.getUsbEndpoint((byte) 0x83);//接受數據地址 UsbEndpoint endpoint01 = iface.getUsbEndpoint((byte)0x04);//發送數據地址 pipe81 = endpoint81.getUsbPipe(); pipe81.open(); pipe01 = endpoint01.getUsbPipe(); pipe01.open(); byte[] dataSend = { (byte) 0x00 };//需要發送的數據 pipe01.asyncSubmit(dataSend); pipe81.addUsbPipeListener(new UsbPipeListener() { @Override public void errorEventOccurred(UsbPipeErrorEvent arg0) { // TODO Auto-generated method stub System.out.println(arg0); } @Override public void dataEventOccurred(UsbPipeDataEvent arg0) { // TODO Auto-generated method stub System.out.println(new String(arg0.getData())); } }); // pipe81.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // } } }
其中的一些接口、端口都是依據usbview寫的,數據是發成功了(RX燈亮了),但是因為我使用的是arduino,里面的代碼不知道怎么寫,如果有這方便的高手還請幫我一下,謝謝。翻牆看了下有的人用的是同步傳輸出現的問題,相關的解決辦法,這里我就完全粘貼、復制了:Here is an abbreviated example. Note that I have removed from this, allchecks, error handling, and such.It is just the logic of creating two pipes for two endpoints. One IN andone OUT. Then using them to do a write and a read.It assumes that endpoint 2 is an OUT and endpoint 4 is an IN.Note also that the name of an IN endpoint is OR'd with 0x80:

import javax.usb.*; public class Example { public static void main(String[] args) throws Exception { UsbDevice usbDevice = ...getUsbDevice by matching vID, pID, knowing the path... or whatever works for you... usbDevice.getUsbConfigurations(); UsbConfiguration config = usbDevice.getActiveUsbConfiguration(); UsbInterface xface = config.getUsbInterface((byte)0); xface.claim(); UsbEndpoint endPt2 = xface.getUsbEndpoint((byte)2); //OUT endpoint UsbEndpoint endPt4 = xface.getUsbEndpoint((byte)(4 | 0x80) ); //IN endpoint UsbPipe pipe2 = endPt2.getUsbPipe(); pipe2.open(); UsbPipe pipe4 = endPt4.getUsbPipe(); pipe4.open(); //Use the pipes: byte[] bytesToSend = new byte[] {1,2,3}; //Going to send out these 3 bytes UsbIrp irpSend = pipe2.createUsbIrp(); irpSend.setData(bytesToSend); pipe2.asyncSubmit(irpSend); //Send out some bytes irpSend.waitUntilComplete(1000); //Wait up to 1 second byte[] bytesToRead = new byte[3]; //Going to read 3 bytes into here UsbIrp irpRead = pipe2.createUsbIrp(); irpRead.setData(bytesToRead); pipe4.asyncSubmit(irpRead); //Read some bytes irpRead.waitUntilComplete(1000); //Wait up to 1 second } }
下面是我監聽數據返回點完整代碼:
UsbConn.java:

package DemoMath; import java.util.List; import javax.usb.UsbConfiguration; import javax.usb.UsbConst; import javax.usb.UsbControlIrp; import javax.usb.UsbDevice; import javax.usb.UsbDeviceDescriptor; import javax.usb.UsbEndpoint; import javax.usb.UsbException; import javax.usb.UsbHostManager; import javax.usb.UsbHub; import javax.usb.UsbInterface; import javax.usb.UsbInterfacePolicy; import javax.usb.UsbPipe; import javax.usb.util.UsbUtil; public class UsbConn { private static final short VENDOR_ID = 0x04b4; private static final short PRODUCT_ID = 0x1004; private static final byte INTERFACE_AD= 0x00; private static final byte ENDPOINT_OUT= 0x02; private static final byte ENDPOINT_IN= (byte) 0x86; private static final byte[] COMMAND = {0x01,0x00}; @SuppressWarnings("unchecked") public static UsbDevice findMissileLauncher(UsbHub hub) { UsbDevice launcher = null; for (UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices()) { if (device.isUsbHub()) { launcher = findMissileLauncher((UsbHub) device); if (launcher != null) return launcher; } else { UsbDeviceDescriptor desc = device.getUsbDeviceDescriptor(); if (desc.idVendor() == VENDOR_ID && desc.idProduct() == PRODUCT_ID) { System.out.println("發現設備" + device); return device; } } } return null; } //command for controlTransfer public static void sendMessage(UsbDevice device, byte[] message) throws UsbException { UsbControlIrp irp = device .createUsbControlIrp( (byte) (UsbConst.REQUESTTYPE_TYPE_CLASS | UsbConst.REQUESTTYPE_RECIPIENT_INTERFACE), (byte) 0x09, (short) 2, (short) 1); irp.setData(message); device.syncSubmit(irp); } /** * Class to listen in a dedicated Thread for data coming events. * This really could be used for any HID device. */ public static class HidMouseRunnable implements Runnable { /* This pipe must be the HID interface's interrupt-type in-direction endpoint's pipe. */ public HidMouseRunnable(UsbPipe pipe) { usbPipe = pipe; } public void run() { byte[] buffer = new byte[UsbUtil.unsignedInt(usbPipe.getUsbEndpoint().getUsbEndpointDescriptor().wMaxPacketSize())]; @SuppressWarnings("unused") int length = 0; while (running) { try { length = usbPipe.syncSubmit(buffer); } catch ( UsbException uE ) { if (running) { System.out.println("Unable to submit data buffer to HID mouse : " + uE.getMessage()); break; } } if (running) { // System.out.print("Got " + length + " bytes of data from HID mouse :"); // for (int i=0; i<length; i++) // System.out.print(" 0x" + UsbUtil.toHexString(buffer[i])); try { String result = DataFix.getHexString(buffer); System.out.println(result); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * Stop/abort listening for data events. */ public void stop() { running = false; usbPipe.abortAllSubmissions(); } public boolean running = true; public UsbPipe usbPipe = null; } /** * get the correct Interface for USB * @param device * @return * @throws UsbException */ public static UsbInterface readInit() throws UsbException{ UsbDevice device = findMissileLauncher(UsbHostManager.getUsbServices() .getRootUsbHub()); if (device == null) { System.out.println("Missile launcher not found."); System.exit(1); } UsbConfiguration configuration = device.getActiveUsbConfiguration(); UsbInterface iface = configuration.getUsbInterface(INTERFACE_AD);//Interface Alternate Number //if you using the MS os,may be you need judge,because MS do not need follow code,by tong iface.claim(new UsbInterfacePolicy() { @Override public boolean forceClaim(UsbInterface arg0) { // TODO Auto-generated method stub return true; } }); return iface; } /** * 異步bulk傳輸,by tong * @param usbInterface * @param data */ public static void syncSend(UsbInterface usbInterface,byte[] data) { UsbEndpoint endpoint = usbInterface.getUsbEndpoint(ENDPOINT_OUT); UsbPipe outPipe = endpoint.getUsbPipe(); try { outPipe.open(); outPipe.syncSubmit(data); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { outPipe.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static HidMouseRunnable listenData(UsbInterface usbInterface) { UsbEndpoint endpoint = usbInterface.getUsbEndpoint(ENDPOINT_IN); UsbPipe inPipe = endpoint.getUsbPipe(); HidMouseRunnable hmR = null; try { inPipe.open(); hmR = new HidMouseRunnable(inPipe); Thread t = new Thread(hmR); t.start(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return hmR; } /** * 主程序入口 * * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub UsbInterface iface; try { iface = readInit(); listenData(iface); syncSend(iface, COMMAND); } catch (UsbException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }