I am trying to get some data from a USB device connected to my Android phone, which is in host mode. I can send data to it, but reading fails.
I looked through a few examples and tried everything I could, but I have no experience with USB-communication, although I already know a little, and I lingered on it longer than I agree.
I am not very familiar with the configuration of endpoints, but I know that my device uses a CDC-type communication method and registers both the output (from the phone to the device) and the input.
Here, the whole class that manages the USB connection with the only device connected to the phone is not completed in any way, but I would like this part of the reading to work before I go any further.
public class UsbCommunicationManager { static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; UsbManager usbManager; UsbDevice usbDevice; UsbInterface intf = null; UsbEndpoint input, output; UsbDeviceConnection connection; PendingIntent permissionIntent; Context context; byte[] readBytes = new byte[64]; public UsbCommunicationManager(Context context) { this.context = context; usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); // ask permission from user to use the usb device permissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); context.registerReceiver(usbReceiver, filter); } public void connect() { // check if there a connected usb device if(usbManager.getDeviceList().isEmpty()) { Log.d("trebla", "No connected devices"); return; } // get the first (only) connected device usbDevice = usbManager.getDeviceList().values().iterator().next(); // user must approve of connection usbManager.requestPermission(usbDevice, permissionIntent); } public void stop() { context.unregisterReceiver(usbReceiver); } public String send(String data) { if(usbDevice == null) { return "no usb device selected"; } int sentBytes = 0; if(!data.equals("")) { synchronized(this) { // send data to usb device byte[] bytes = data.getBytes(); sentBytes = connection.bulkTransfer(output, bytes, bytes.length, 1000); } } return Integer.toString(sentBytes); } public String read() { // reinitialize read value byte array Arrays.fill(readBytes, (byte) 0); // wait for some data from the mcu int recvBytes = connection.bulkTransfer(input, readBytes, readBytes.length, 3000); if(recvBytes > 0) { Log.d("trebla", "Got some data: " + new String(readBytes)); } else { Log.d("trebla", "Did not get any data: " + recvBytes); } return Integer.toString(recvBytes); } public String listUsbDevices() { HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); if(deviceList.size() == 0) { return "no usb devices found"; } Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); String returnValue = ""; UsbInterface usbInterface; while(deviceIterator.hasNext()) { UsbDevice device = deviceIterator.next(); returnValue += "Name: " + device.getDeviceName(); returnValue += "\nID: " + device.getDeviceId(); returnValue += "\nProtocol: " + device.getDeviceProtocol(); returnValue += "\nClass: " + device.getDeviceClass(); returnValue += "\nSubclass: " + device.getDeviceSubclass(); returnValue += "\nProduct ID: " + device.getProductId(); returnValue += "\nVendor ID: " + device.getVendorId(); returnValue += "\nInterface count: " + device.getInterfaceCount(); for(int i = 0; i < device.getInterfaceCount(); i++) { usbInterface = device.getInterface(i); returnValue += "\n Interface " + i; returnValue += "\n\tInterface ID: " + usbInterface.getId(); returnValue += "\n\tClass: " + usbInterface.getInterfaceClass(); returnValue += "\n\tProtocol: " + usbInterface.getInterfaceProtocol(); returnValue += "\n\tSubclass: " + usbInterface.getInterfaceSubclass(); returnValue += "\n\tEndpoint count: " + usbInterface.getEndpointCount(); for(int j = 0; j < usbInterface.getEndpointCount(); j++) { returnValue += "\n\t Endpoint " + j; returnValue += "\n\t\tAddress: " + usbInterface.getEndpoint(j).getAddress(); returnValue += "\n\t\tAttributes: " + usbInterface.getEndpoint(j).getAttributes(); returnValue += "\n\t\tDirection: " + usbInterface.getEndpoint(j).getDirection(); returnValue += "\n\t\tNumber: " + usbInterface.getEndpoint(j).getEndpointNumber(); returnValue += "\n\t\tInterval: " + usbInterface.getEndpoint(j).getInterval(); returnValue += "\n\t\tType: " + usbInterface.getEndpoint(j).getType(); returnValue += "\n\t\tMax packet size: " + usbInterface.getEndpoint(j).getMaxPacketSize(); } } } return returnValue; } private void setupConnection() { // find the right interface for(int i = 0; i < usbDevice.getInterfaceCount(); i++) { // communications device class (CDC) type device if(usbDevice.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { intf = usbDevice.getInterface(i); // find the endpoints for(int j = 0; j < intf.getEndpointCount(); j++) { if(intf.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_OUT && intf.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { // from android to device output = intf.getEndpoint(j); } if(intf.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_IN && intf.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { // from device to android input = intf.getEndpoint(j); } } } } } private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(ACTION_USB_PERMISSION.equals(action)) { // broadcast is like an interrupt and works asynchronously with the class, it must be synced just in case synchronized(this) { if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { setupConnection(); connection = usbManager.openDevice(usbDevice); connection.claimInterface(intf, true); // set flow control to 8N1 at 9600 baud int baudRate = 9600; byte stopBitsByte = 1; byte parityBitesByte = 0; byte dataBits = 8; byte[] msg = { (byte) (baudRate & 0xff), (byte) ((baudRate >> 8) & 0xff), (byte) ((baudRate >> 16) & 0xff), (byte) ((baudRate >> 24) & 0xff), stopBitsByte, parityBitesByte, (byte) dataBits }; connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | 0x01, 0x20, 0, 0, msg, msg.length, 5000); } else { Log.d("trebla", "Permission denied for USB device"); } } } else if(UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { Log.d("trebla", "USB device detached"); } } }; }
I keep getting -1 from the read() method, which points to some kind of error, it always expires. Perhaps the problem is with the connection configuration, I tried it several times (read: trial version and error), and no one worked, and I suddenly do not need any configuration to send data to the device.
Edit
It should also be noted that the cable I use is micro-USB to micro-USB, and it works in only one way, that is, my device only powers up my phone when the A plug B is connected to the phone and plugged in. to the device, and not vice versa ... it seems very strange. The fact that I can send data and not receive when connected correctly remains.
EDIT 2
I found that someone else had the same problem , but it seems he could not solve it.
EDIT 3
I finally found a solution on this page :
Another major oversight is that the host does not have a mechanism to notify the device that there is a data receiver on the host side that is ready to receive data. This means that the device may try to send data while the host is not listening, which causes long blocking timeouts in the transfer procedures. Therefore, it is highly recommended that you use the virtual serial line DTR (Data Terminal Ready) signal, where possible, to determine if the host application is ready for data.
So, the DTR signal was mandatory, and all I had to do was add this to the interface configuration:
connection.controlTransfer(0x21, 0x22, 0x1, 0, null, 0, 0);
EDIT 4
If anyone is interested, I finished the project with open source and published it to my GitHub account . It is unstable all the time (see notes ), and I no longer plan to work on it, but it works. Feel free to use it for your projects.