Skip to content
Kai Morich edited this page Jul 5, 2024 · 13 revisions

This is a collection of FAQ-style troubleshooting tips.

Table of Contents

Do I need an OTG adapter?

Android device and serial device with USB-C connector

No OTG adapter is required if the serial device correctly implements USB-C. Typically the the CC pins are connected to GND with 5,1 kΞ© resistors each, to mark the device as USB peripheral.

Other connector combinations

Connecting an USB device with just an USB cable will not work. You have to attach an OTG adapter to your Android device to switch it into host mode. This adapter typically provides an USB A socket, where you attach a standard USB cable.

My USB device is not recognized

Check OTG adapter as discussed in previous question.

Check with an App like Serial Example in this project, if your device is shown. If no USB device is shown, check the web for USB Host Mode aka. USB OTG support for your Android device. Missing support is more likely for older devices.

For an incomplete list of older Android devices with known issues look here.

My USB device is recognized, but no driver is detected

The library includes a list of USB vendor and device ID's (VID/PID) that are typically used by the supported chips. In case of unusual ID's, use a Custom Prober or specify a fixed driver.

Non USB-to-Serial devices like mass-storage, mouse, GPC receivers, ... and rarely used USB-to-Serial chips including Hoitek HE2325U and FTDI FT312D are not supported.

My device is connected, but I receive no / wrong data

A common issue is wrong baud rate.

My device sporadically disconnects with Queueing USB request failed

Typically an issues with the USB device, e.g.

  • USB requests are temporarily not handled in your program
  • power supply insufficient, causing hardware reset

Baud rate 12345 does not work

Not all drivers / devices support non-standard baud rates. See Feature Matrix for details.

I am using an Arduino Uno, Sparkfun Pro Micro, or other Arduino but Serial.write() does not send data

Most Arduinos with CDC-ACM driver use the DTR line to determine serial channel readiness. In your Android code, call setDTR(true);

Can my Android device be powered by the external device?

Typically communication and power supply cannot be combined. Might be possible with USB-C and USB PD implemented in the serial device, but typical USB-C serial serial devices only use simple 5,1 kΞ© resistors to GND.

How do I access /dev/ttyUSB0?

You don't. /dev/tty* is the Linux kernel's driver interface to various serial devices. Certain devices and custom ROMs may expose serial devices this way, but this library does not use this interface.

How to persist USB permission?

After adding vendor-id and device-id to device_filter.xml the USB device connection dialog has a "Always open {app} when {device} is attached" checkbox and the permission dialog has a "Use by default for this USB device" checkbox.

When the checkbox is selected, the permission is retained over USB device re-attach, also after Android reboot.

If the device remains attached during Android reboot, the permission is only retained in some cases:

The issue is identified in Bug 77658221, but set to Won't Fix by Google.

The persisted permission file is only loaded when attaching an USB device, so starting the App after reboot without re-attaching any device, the App again asks for permission. The bug recommends to add android:directBootAware="true" to your activity in AndroidManifest.xml, which improves the situation for some Android devices, e.g.:

USB device attached after boot USB device attached during reboot
Permission retained on App start Permission retained
on App start
App autostart directBootAware Device
🟒 🟒 🟒 - Android 11 / Samsung
🟒 🟒 🟒 true Android 8 / Samsung
🟒 🟒 πŸ”΄ true Android 11 / Lineage OS
🟒 πŸ”΄ πŸ”΄ false Android 11 / Lineage OS
Android 8 / Samsung
... (*)

(*) Some android devices do not show USB devices that were connected during reboot, so you have to reconnect them anyway.

Can I open multiple connections in one application?

You can connect to multiple devices and to multiple ports of a multi-port device in one App.

  • When connecting to different ports in a multi-port device, each connection needs it's own UsbDeviceConnection, else the first close will also close all other ports.
  • Distinguishing identical devices has some challenges:
    • Use devices with different VIDs/PIDs or devices where it can be programmed like FTDI or CP210x .
    • Use UsbDevice.getSerialNumber() and devices that come with a unique serial or devices where it can be programmed like FTDI or CP210x.
      Note: Starting with Android 10, the serial number is only accessible after device access permission has been granted.
    • Using UsbDevice.getDeviceName() or UsbDevice.getDeviceId() is not an option as the numbers increase when re-attaching a device.

When reading a message, why don't all bytes arrive at the same time?

The protocol layer does not guarantee that all bytes will arrive in a single message. In fact the protocol layer doesn't have any knowledge of what your "message" is β€” it is just an interface for sending a serial stream of bytes.

To reduce the number of USB requests, devices usually buffer data until some size or idle time is reached. Therefore request sizes heavily depend on the device type and actual data transfer pattern.

For example, to receive a 100 byte string, you might read 64 bytes, then 36 bytes, instead of a single message of 100 bytes. You need to account for this problem when designing your protocol. Some common techniques:

  • Fixed length messages: If you a message is always going to be 100 bytes, just keep reading until you have all 100.
  • Length-prefixed messages: Prefix every message with a fixed-length size value; your message is complete after you've read size more bytes.
  • Newline-terminated messages: Read until you see a \n (or any other "terminal" character).

I observed read data loss with continuous transfer at high baud rates

Android is not a real time OS. The thread responsible for receiving data might not be scheduled by the OS or garbage collection might be ongoing or … Therefore data loss can happen for continues read at high baud rates. If data is lost, typically some smaller fragments in the middle of the data are missing.

This effect is more likely for slower Android device and USB devices with smaller buffer size and typically starts with higher baud rates like 115k2 baud, but is hardly predictable. For low baud rates or non-continuous transfers this issue was not observed as the USB device will not run into a read buffer overflow before the next USB read transfer.

Using a higher thread priority (default for SerialInputOutputManager since usb-serial-for-android v3.1.0) and offloading work from onNewData to other threads can mitigates the issue.

It is recommended to use some sort of flow control like X-Modem protocol.

Data loss can also occur with inappropriate buffer size, as shown in next Question.

How to size the read buffer?

USB data transfers have a fixed packet size which is 64 for most devices. You can get the respective size with port.getReadEndpoint().getMaxPacketSize(), and it's recommended to use it as buffer size.

A smaller buffer size is ok, as long as the received messages fits into the buffer. If it doesn't fit into the buffer, read returns an empty result. Due to poor error propagation in Android USB code, this cannot be distinguished from timeout or real communication errors.

A larger buffer size can make your app lagging. In particular FTDI devices tend to queue data until the buffer is completely filled on continuous transfer at higher baud rates. This can be mitigated by choosing appropriate buffer size and setting the latency timer as also recommended by FTDI.

When using smaller buffer sizes with FTDI devices, you have to take into account that each response starts with 2 header bytes. The header bytes will be stripped from the final result.

How to set timeout in read/write methods?

read timeout should not be shorter then 200 to 500 milliseconds, if possible use infinite timeout.

  • read with infinite timeout blocks until data is available, so it should be avoided in the UI thread, instead use SerialInputOutputManager to be notified on new data.
  • read with timeout is typically used in the UI thread, if you send a request and know that there is a timely response and the blocking time is acceptable.
write timeout should typically be significantly larger than the required send time.
  • write with short timeout is ok, if you use port.getReadEndpoint().getMaxPacketSize() as buffer size and resend the remaining data after getting a SerialTimeoutException. With larger buffer size, the packeting is done in the Linux kernel which returns -1 instead of the written size, so there is no chance to resume writing.
Android is not a realtime OS, e.g. garbage collection can easily take 100 msec.

Is write synchronous?

It's typically not.

Write consists of 2 phases:

  1. from android to usb device
  2. from usb device to serial port
If the send buffer on the usb device is not full, the write call returns after phase 1. If the buffer is full, e.g. your write is larger or older data is still to be send, the write call returns when enough data has been send over the serial port and the rest is in the buffer.

Waiting for write completion is typically done with application specific protocols, like request-response pattern.

How to use flow control with control lines RTS/CTS or DTR/DSR

When getCTS() or getDSR() return false you can continue writing until the send buffer is full, then write() will return a SerialTimeoutException, but typically it's better to immediately stop writing.

If your App is not reading data fast enough, the RTS or DTR lines are reset when the receive buffer is over 50~80% full.

How to use flow control with XON/XOFF characters

Some devices return XON/XOFF state with getXON() method, others return XON/XOFF characters inline in received data. The supported variant is reflected by different FlowControl modes. The inline data can be filtered out with XonXoffFilter helper class, which provides a getXON() method similar to the UsbSerialPort classes.

When the appropriate getXON() method returns false you can continue writing until the send buffer is full, then write() will return a SerialTimeoutException, but typically it's better to immediately stop writing.

If your App is not reading data fast enough, CHAR_XOFF is send when the receive buffer gets 50~80% full, and CHAR_XON when below that threshold.

How to use flow control with unsupported devices

Some devices do not have hardware support for flow control. You can check the control line or XON/XOFF characters yourself and can stop writing, but this could still cause data loss if not done sufficiently early.

Does this library work with Unity/Ionic/Cordova/Xamarin/App Inventor/... development framework?

If your framework can import Java .jar files, look here for instructions about creating a .jar file.

Feature Matrix

Basic Features are supported by all drivers / devices

Method / Feature Supported
open, close 🟒
setParameters 🟒
read, write 🟒
event based read with SerialInputOutputManager 🟒

Some Features are not supported by all drivers / devices

Feature FTDI CH34x CP210x PL2303 CDC-ACM
setParameters with non-standard baud rates 🟒 🟒 🟒 for CP2105
πŸ”΄ for CP2102. fallback to unknown baud rate without exception
🟒 🟒
set BREAK condition 🟒 🟒 🟒 for CP2102, CP2105 first port
πŸ”΄ for CP2105 second port. resends last byte without exception
🟒 🟒
set/get flow control lines RTS, CTS, DTR, DSR, CD, RI 🟒 🟒 🟒 🟒 🟑 only output lines RTS, DTR
flow control with control lines 🟒 RTS/CTS, DTR/DSR n.a. 🟒 RTS/CTS, DTR/DSR 🟒 RTS/CTS n.a.
flow control with XON/XOFF characters 🟒 n.a. 🟒 🟒 n.a.
purgeHwBuffers 🟒 πŸ”΄ 🟒 🟒 🟒
multi port devices 🟒 n.a. 🟒 n.a. 🟒
device specific methods get/setLatencyTimer - - - -