安卓USB主机从设备读取

61

我正在尝试从连接到我的Android手机的USB设备中获取一些数据,该设备处于主机模式。我可以向其发送数据,但读取失败。

我查看了几个示例,并尝试了所有我所知道的,但我没有任何USB通信方面的经验,虽然现在我已经知道一点点了,但我陷入了这个问题中,这比我愿意承认的时间还要长。

我对端点配置不是很熟悉,但我知道我的设备使用CDC类型的通信方法,并且输出(从手机到设备)和输入都被注册了。

这是管理与连接到手机的唯一设备的USB连接的整个类,它还没有完成,但是在继续之前,我想让那个读取部分工作起来。

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");
            }
        }
    };
}

我一直从read()方法中得到-1,这表明出现了某种错误,它总是超时。也许问题来自连接配置,我尝试了几个(即:反复试验),但都没有成功,令人惊讶的是,我不需要任何配置就能向设备发送数据。

编辑

还必须注意的是,我使用的电缆是micro-USB到micro-USB的,只有一种连接方式有效,即插头A连接到手机,插头B连接到设备时,我的设备才由手机供电,反之则无效... 这似乎非常奇怪。事实上,当以“正确”的方式插入时,我能够发送数据而无法接收。


编辑2

我发现其他人也遇到了同样的问题,但似乎他解决不了。


编辑3

最终我在这个页面上找到了解决方案:

另一个主要的疏忽是,没有机制可以通知设备主机端已准备好接受数据。这意味着设备可能在主机没有监听时尝试发送数据,导致传输例程中的长时间阻塞超时。因此,强烈建议在可能的情况下使用虚拟串行线路DTR(数据终端就绪)信号来确定主机应用程序是否准备好接收数据。

因此,DTR信号是必需的,我所要做的就是将其添加到接口配置中:

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

编辑4

如果有人感兴趣,我已经完成了这个项目,并且开源并发布在我的GitHub账户上。尽管它不总是稳定的(请参见注释),我也不打算再继续开发它,但它可以工作。欢迎将其用于您自己的项目中。


1
我使用的是ATmega32U4,本质上是一个注册为CDC设备的Arduino Leonardo。您的CP2112EK似乎是一个需要其他配置的HID设备。 - Solenoid
@Solenoid 这行代码应该放在哪里?controlTransfer(0x21, 0x22, 0x1, 0, null, 0, 0) ?? - Palak Darji
@Karoly S:我已经有一段时间没有接触这个项目了,所以我不太记得了。但是请随意浏览此处并获取您需要的任何其他部分:https://github.com/Nurgak/Smartphone-sensor-data-collection-application - Solenoid
你尝试过使用usb-serial-for-android吗?我正在使用它,它非常好用。我同时使用多个设备,它可以帮助你很多,简化你的事情。顺便说一下,如果你能回答你的问题并标记它为答案,那就更好了。 - MRodrigues
嘿,@Solenoid,你成功实现了完全通信吗?我正在做一个非常相似的项目。 - Machado
显示剩余2条评论
1个回答

1
你可以使用来自https://github.com/mik3y/usb-serial-for-android的UsbSerial库。
我的示例代码:
    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());
                }

1
如何将该库添加到工作室中? - Gaju Kollur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接