I tried to communicate between a commercially available Android tablet and a device with a serial interface. The communication protocol was successful with both plain serial communication and modbus.
The configuration is almost the same as the configuration diagram of Link. This time, my knowledge of serial communication started from scratch, so I looked at this site to death and studied. It was very easy to understand and it was helpful. It probably wouldn't be possible without this site.
usb-serial-for-android v2.2.2(https://github.com/mik3y/usb-serial-for-android/releases) It's about April 2020, so I'm sorry that the version is old.
At that time, the content written in the ReadMe about how to use the library was not very substantial, so I wanted to write an article. I was at a loss because it is a little fulfilling now, but I would like to give a supplementary explanation.
However, as mentioned above, serial communication itself was a zero start, so there are many parts to talk about in the image. I would appreciate it if you could point out any mistakes.
First of all, let's talk about the prerequisite knowledge about serial connection. Basically, commercially available Android tablets do not have a serial interface. Therefore, the physical configuration will be a connection like [Android tablet → USB cable → serial USB conversion cable → device].
The Android framework provides an API for USB operation, but not for serial communication. Therefore, it is necessary to realize the image of encapsulating serial communication with USB communication (it looks like a simple USB operation, not serial communication from the application).
The trouble here is that the content of serial communication depends on the "serial USB conversion cable", and the content varies depending on the vendor and product.
You can't implement that, so you need a library.
I will add comments by excerpting from the ReadMe as of January 2021. By the way, it is a sample of multi-thread premise to start a thread for communication. I'm wondering if I should stop the UI thread (or is it impossible due to Android restrictions?), So normally it should be multithreaded.
// Find all available drivers from attached devices.
//Create a USB service manager using the standard Android API
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
//The following is usb-serial-for-android API. Get a driver for USB serial communication that can be used.
//Prober, which will be mentioned later, is a mapping table between the vendor ID and product ID of the cable used.
//AvailableDrivers is populated when there is a USB serial conversion cable registered with Prover and connected
List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
if (availableDrivers.isEmpty()) {
return;
}
// Open a connection to the first available driver.
//Below, in the ReadMe sample, get(0)I have selected the first driver for the time being.
UsbSerialDriver driver = availableDrivers.get(0);
//When multiple cables are connected, or because I wanted to fix the available cables,
//I implemented it like this
// for(UsbSerialDriver driver: availableDrivers) {
// //Extract the ones that match the vendor ID and product ID
// String vendorId = Integer.toString(driver.getDevice().getVendorId());
// String productId = Integer.toString(driver.getDevice().getProductId());
// if(If the vendor ID and product ID match what you intended) {
// return driver;
// }
// }
//The following is the standard Android API. As the name implies, it opens the device.
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
if (connection == null) {
// add UsbManager.requestPermission(driver.getDevice(), ..) handling here
//Here is the handling process when you do not have access to the USB device.
//I think I'll probably use the Broadcast Receiver mechanism
return;
}
//The following is usb-serial-for-android API. Get here too(0)I'm doing
//I don't know what kind of situation there are multiple patterns.
UsbSerialPort port = driver.getPorts().get(0); // Most devices have just one port (port 0)
//The serial port is open
port.open(connection);
//After opening, set the parameters according to the serial communication standard. The reference link is described in "Serial setting contents"
port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
//I think I usually use the "event processing" described later, so I probably won't use this one.
//Send
//request is byte[]Set the content you want to write with the type
port.write(request, WRITE_WAIT_MILLIS);
//Receive
//response is byte[]Set the variable whose contents you want to read by type
//len contains the received size
len = port.read(response, READ_WAIT_MILLIS);
I think this is not enough to explain the method, so I will post the JavaDoc of UsbSerialPort.
/**
* Reads as many bytes as possible into the destination buffer.
*
* @param dest the destination byte buffer
* @param timeout the timeout for reading in milliseconds, 0 is infinite
* @return the actual number of bytes read
* @throws IOException if an error occurred during reading
*/
public int read(final byte[] dest, final int timeout) throws IOException;
/**
* Writes as many bytes as possible from the source buffer.
*
* @param src the source byte buffer
* @param timeout the timeout for writing in milliseconds, 0 is infinite
* @return the actual number of bytes written
* @throws IOException if an error occurred during writing
*/
public int write(final byte[] src, final int timeout) throws IOException;
//The following is usb-serial-for-Send / receive manager class provided by android
//Basically, I think it's better to use this.
//However, it is not a very good sample, so I will add it separately in "My implementation example".
usbIoManager = new SerialInputOutputManager(usbSerialPort, this);
//Below, I am using SingleThreadExecutor, but anything that can be multithreaded is fine.
Executors.newSingleThreadExecutor().submit(usbIoManager);
...
port.write("hello".getBytes(), WRITE_WAIT_MILLIS);
//This is a callback function that is called when the communication content is received. But the method is SerialInputOutputManager.Implement Listener
//I need it, so if I write this as it is, it will not work
@Override
public void onNewData(byte[] data) {
runOnUiThread(() -> { textView.append(new String(data)); });
}
Actually, I was designing various classes, so it is not the same as the following, but only the important points are excerpted. Also, the following implementation was based on Sample code in usbSerialExamples package at that time. I still have sample code, but it seems that the implementation has changed considerably, so It may be better to look at the sample code directly.
//Any class is fine, but you need a mechanism that can call back to the UI thread.
public class SampleActivity extends AppCompatActivity {
private SerialInputOutputManager mSerialIoManager;
private SerialInputOutputManager.Listener mListener;
private Handler mHandler;
private UsbSerialPort mPort;
//Any method. Probably it will be implemented in the life cycle such as onCreate.
@Override
protected void onCreate(Bundle savedInstanceState) {
//The processing equivalent to "initial processing" and the processing required for onCreate are omitted.
//Serial port creation process
mPort = driver.getPorts().get(0); // Most devices have just one port (port 0)
//Generate a handler for UI thread
mHandler = new Handler(Looper.getMainLooper());
//Generate listener for callback
mListener = new Listener {
//Callback method called when receiving data
@Override
public void onNewData(byte[] data) {
//Call back the process to the UI thread
mainLooper.post(() -> {
//What you want to do
});
}
//Callback method called when some error is detected
//For example, the cable came off
@Override
public void onRunError(Exception e) {
//Call back the process to the UI thread
mHandler.post(() -> {
//What you want to do
});
}
}
this.startSerial();
}
//Any method. It will probably be implemented in a life cycle such as on Destroy.
@Override
protected void onDestroy() {
//Omit the processing required for onDestroy
if (mSerialManager != null) {
this.stopSerial();
}
}
//Method for starting serial communication
private void startSerial() {
if (mPort != null) {
//Associate the serial communication manager with the serial port and callback when receiving an event
mSerialIoManager = new SerialInputOutputManager(mPort, mListener);
//Anything can be multithreaded
new Thread(mSerialIoManager).start();
} else {
//Appropriate error handling
}
}
//Method for stopping serial communication
private void stopSerial() {
if (mSerialIoManager != null) {
//Stop serial communication manager
mSerialIoManager.stop();
mSerialIoManager = null;
}
if (mPort != null) {
try {
//Close the serial port
mPort.close()
} catch (IOException e) {
//Appropriate error handling
}
}
}
By the way, you can also write with the serial communication manager
You can also write
byte[] data = //Data you want to send
mSerialIoManager.writeAsync(data);
[See wikipedia](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB%E3%83%9D%E3% 83% BC% E3% 83% 88 #% E8% A8% AD% E5% AE% 9A )
I'm not very familiar with serial communication, but I was able to easily realize communication. I think it's a very useful library.
However, since the amount of information is not so much, error handling and various open / close processing may make you cry a little ...
Also, I feel that this is a limitation of serial communication, but the device seen from the Android application is not the device of [Android tablet → USB cable → serial USB conversion cable → device] as mentioned above, but serial USB conversion It is a cable.
So, it seems that the library doesn't have a mechanism to detect who the real device is. I think it is important to consider what to do with it from a security perspective.
Recommended Posts