Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android USB host read from device

I'm trying to get some data out of a USB device connected to my Android phone that is on host mode. I'm able to send data to it, but reading fails.

I've looked at several examples and tried all I could but I don't have any experience in USB communication, although by now I know a little, and I've been stuck on this longer that I care to admit.

I'm not very familiar with the endpoint configuration, but I know is that my device uses a CDC type communication method and both the output (from phone to device) and input are registered.

Here's the whole class that manages the USB connection with the only device that is connected to the phone, it's not finished by any means, but I'd like to get that reading part 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's 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 indicates some kind of error, it always times out. Maybe the problem comes from the connection configuration, I've tried several (read: trial and error) and none worked, surprisingly I don't need any configuration to send data to the device.


Edit

It must also be noted that the cable I'm using is micro-USB to micro-USB and it only works in one way, that is my device is powered by my phone only when the plug A connected to phone and plug B connected to device, not the other way around... it seems very strange. The fact that I'm able to send data and not receive when plugged the right way remains.


EDIT 2

I found that somebody else had the same problem but it seems he wasn't able to solve it.


EDIT 3

I finally found the solution on this page:

Another major oversight is that there is no mechanism for the host to notify the device that there is a data sink on the host side ready to accept data. This means that the device may try to send data while the host isn't listening, causing lengthy blocking timeouts in the transmission routines. It is thus highly recommended that the virtual serial line DTR (Data Terminal Ready) signal be used where possible to determine if a host application is ready for data.

So the DTR signal was mandatory and all I had to do was to add this to the interface configuration:

connection.controlTransfer(0x21, 0x22, 0x1, 0, null, 0, 0); 

EDIT 4

If anybody is interested I finished the project and it's open source and published on my GitHub account. It's not stable all the time though (see the notes) and I don't plan working on it anymore, but it works. Feel free to use it for your own projects.

like image 643
Solenoid Avatar asked Nov 01 '13 22:11

Solenoid


People also ask

What is USB host in Android?

In USB host mode, the Android-powered device acts as the host. Examples of devices include digital cameras, keyboards, mice, and game controllers. USB devices that are designed for a wide range of applications and environments can still interact with Android applications that can correctly communicate with the device.

How does USB host detect device?

A USB host detects the speed of a USB slave device by sensing the status of the two USB data lines. The two data lines are commonly referred to as the D+ and D- lines. The USB master has very weak (15K) pull-downs on both the D+ and D- line.


1 Answers

You can use UsbSerial Lib of from https://github.com/mik3y/usb-serial-for-android

My example code:

    UsbManager usbManager = null;     UsbDeviceConnection connection = null;     UsbSerialDriver driver = null;     UsbSerialPort port = null;      usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);      List<UsbSerialDriver> availableDrivers =  UsbSerialProber.getDefaultProber().findAllDrivers(manager);     // Open a connection to the first available driver.      for (UsbSerialDriver usd : availableDrivers) {         UsbDevice udv = usd.getDevice();          if (udv.getVendorId()==0x067B || udv.getProductId()==2303){             driver = usd;             break;         }     }         connection = usbManager.openDevice(driver.getDevice());          port = driver.getPorts().get(0);          driver.getDevice().      }          if (connection == null) return;          try{              port.open(connection);             port.setParameters(4800, UsbSerialPort.DATABITS_8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);                  try{                      byte buffer[] = new byte[250];                     //Log.d("GPS_REQ", "->");                     int numBytesRead = port.read(buffer, 500); //5000;                   }catch (Exception e) {                     Log.d("GPS_ERR", e.getLocalizedMessage());                 } 
like image 161
GiapLee Avatar answered Sep 19 '22 07:09

GiapLee