Basic Flow of Android USB to Serial Communication Development

Posted by Bauer418 on Fri, 12 Jul 2019 00:25:06 +0200

I haven't written an article for a long time. A new project started by the company a year ago is related to the serial port communication of usb. The demand is to communicate with several peripherals through the USB connection of Android tablet. I have been busy until recently, then I slowly idle down. Taking advantage of this weekend's not busy, I record the basic process of the development of serial port communication of usb.

We developed and used the usb host mode, that is, Android tablet as the host and usb peripheral as the slave for data communication. The whole development process can be summarized as follows:

1. Discovery equipment

UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
Map<String, UsbDevice> usbList = usbManager.getDeviceList();

Through the class provided by UsbManager system, we can enumerate all the usb devices currently connected. What we need is the UsbDevice object. As for the UsbDevice class, the official annotation is as follows:

This class represents a USB device attached to the android device with the android device
 acting as the USB host.

Yes, this class represents the usb device that android connects to.

2. Turn on the device

Next, we need to open the usb device we just searched. We can imagine the connection between the flat panel and the usb peripherals as a channel. Only when the door of the channel is opened, can the two sides communicate.

Generally speaking, when accessing usb devices for the first time on a non-customized android device, we default that we do not have access rights, so we first need to determine whether we have access rights to the currently open usbDevice:

if (!usbManager.hasPermission(usbDevice)) {
       usbPermissionReceiver = new UsbPermissionReceiver();
       //Application authority
       Intent intent = new Intent(ACTION_DEVICE_PERMISSION);
       PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
       IntentFilter permissionFilter = new IntentFilter(ACTION_DEVICE_PERMISSION);
       context.registerReceiver(usbPermissionReceiver, permissionFilter);
       usbManager.requestPermission(usbDevice, mPermissionIntent);
        }

Here we declare a broadcasting UsbPermission Receiver, which does some other processing after accepting authorized successful broadcasting:

  private class UsbPermissionReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_DEVICE_PERMISSION.equals(action)) {
                synchronized (this) {
                    UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (device.getDeviceName().equals(usbDevice.getDeviceName()) {
                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                          //Authorization succeeded. Open the device here
                        } else {
                          //privilege grant failed
                        }
                    }
                }
            }
        }
    }

Next, we need to find the interface UsbInterface with data transmission function, from which we can find the data input and output ports UsbEndpoint. Usually, a usbDevice has multiple UsbInterfaces. We need the first UsbInterface, so:

usbInterface=usbDevice.getInterface(0);

Similarly, a usbInterface has multiple UsbEndpoint s, control ports, data ports, etc., so we need to find the data input and output ports we need according to the type and data flow:

for (int index = 0; index < usbInterface.getEndpointCount(); index++) {
                UsbEndpoint point = usbInterface.getEndpoint(index);
                if (point.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                    if (point.getDirection() == UsbConstants.USB_DIR_IN) {
                        usbEndpointIn = point;
                    } else if (point.getDirection() == UsbConstants.USB_DIR_OUT) {
                        usbEndpointOut = point;
                    }
                }
            }

Finally, to really turn on the usb device, we need to build a UsbDevice Connection with the usb peripheral. Its annotations give a very brief description of its purpose:

This class is used for sending and receiving data and control messages to a USB device.

Its acquisition is also very simple, just a code:

usbDeviceConnection = usbManager.openDevice(usbDevice);

So far, theoretically, the connection between the flat panel and the usb peripherals has been established, and the data can be sent out. However, in most cases, we need to configure the usb serial port, such as baud rate, stop bit, data control, etc. Otherwise, the data received will be scrambled if the configuration of the two sides is different. How to configure it depends on what kind of serial chip you use. At present, there are pl2303,ch340 and so on. Because of the space problem, friends who need to configure the serial code need to send it to you personally.

3. Data transmission

At this point, we have been able to transmit data with the usb peripherals. First, let's see how to send data to the usb devices.

 1. Send data to usb peripherals

In the second step, we have obtained the output port of the data, usbEndpointIn, through which we send data to the peripherals. See how to use:

int ret = usbDeviceConnection.bulkTransfer(usbEndpointOut, data, data.length, DEFAULT_TIMEOUT);

The bulkTransfer function is used for data transmission on a given port. The first parameter is the port of this transmission. Here we use the output port. The second parameter is the data to be sent. The type is an array of bytes. The third parameter represents the length of the data to be sent. The last parameter is timeout and return value. Represents the number of bytes that were sent successfully, and if - 1 was returned, the sending failed.

2. Accept data sent by usb peripherals

Similarly, we have found the data input port usbEndpointIn, because the data input is not timed, so we can open a separate thread to receive data specially. The code for receiving data is as follows:

int inMax = inEndpoint.getMaxPacketSize(); 
ByteBuffer byteBuffer = ByteBuffer.allocate(inMax); 
UsbRequest usbRequest = new UsbRequest(); 
usbRequest.initialize(connection, inEndpoint); 
usbRequest.queue(byteBuffer, inMax); 
if(connection.requestWait() == usbRequest){ 
    byte[] retData = byteBuffer.array(); 
    for(Byte byte1 : retData){ 
        System.err.println(byte1); 
    } 
}

Above, is the basic process of serial communication from usb. Some places are not very comprehensive. For example, there should be other ways to receive data from USB peripherals. Welcome to correct the shortcomings.

Topics: Android