基于usb4java实现的java下的usb通信
项目地址:点击打开
使用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,里面的代码不知道怎么写,如果有这方便的高手还请帮我一下,谢谢。FQ看了下有的人用的是同步传输出现的问题,相关的解决办法,这里我就完全粘贴、复制了: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(); } } }