diff --git a/README.md b/README.md index 5037cc6..4b2b32b 100644 --- a/README.md +++ b/README.md @@ -33,66 +33,67 @@ dependencies { } ``` -**2.** Copy [device_filter.xml](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples/src/main/res/xml/device_filter.xml) to your project's `res/xml/` directory. - -**3.** Configure your `AndroidManifest.xml` to notify your app when a device is attached (see [Android USB Host documentation](http://developer.android.com/guide/topics/connectivity/usb/host.html#discovering-d) for help). +**2.** If the app should be notified when a device is attached, add +[device_filter.xml](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples/src/main/res/xml/device_filter.xml) +to your project's `res/xml/` directory and configure in your `AndroidManifest.xml`. ```xml - - - - + + + + ``` -**4.** Use it! Example code snippet: +**3.** Use it! Example code snippet: ```java -// Find all available drivers from attached devices. -UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); -List availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager); -if (availableDrivers.isEmpty()) { - return; -} + // Find all available drivers from attached devices. + UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); + List availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager); + if (availableDrivers.isEmpty()) { + return; + } -// Open a connection to the first available driver. -UsbSerialDriver driver = availableDrivers.get(0); -UsbDeviceConnection connection = manager.openDevice(driver.getDevice()); -if (connection == null) { - // You probably need to call UsbManager.requestPermission(driver.getDevice(), ..) - return; -} + // Open a connection to the first available driver. + UsbSerialDriver driver = availableDrivers.get(0); + UsbDeviceConnection connection = manager.openDevice(driver.getDevice()); + if (connection == null) { + // add UsbManager.requestPermission(driver.getDevice(), ..) handling here + return; + } -// Read some data! Most have just one port (port 0). -UsbSerialPort port = driver.getPorts().get(0); -try { - port.open(connection); - port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); - - byte buffer[] = new byte[16]; - int numBytesRead = port.read(buffer, 1000); - Log.d(TAG, "Read " + numBytesRead + " bytes."); -} catch (IOException e) { - // Deal with error. -} finally { - port.close(); + UsbSerialPort port = driver.getPorts().get(0); // Most devices have just one port (port 0) + port.open(connection); + port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); + usbIoManager = new SerialInputOutputManager(usbSerialPort, this); + Executors.newSingleThreadExecutor().submit(usbIoManager); +``` +```java + port.write("hello".getBytes(), WRITE_WAIT_MILLIS); +``` +```java +@Override +public void onNewData(byte[] data) { + runOnUiThread(() -> { textView.append(new String(data)); }); } ``` +```java + port.close(); +``` -For a simple example, see the -[UsbSerialExamples project](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples) -in git, which is a simple application for reading and showing serial data. + +For a simple example, see +[UsbSerialExamples](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples) +folder in this project. For a more complete example, see separate github project -[SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal) - -A [simple Arduino application](https://github.com/mik3y/usb-serial-for-android/blob/master/arduino) -is also available which can be used for testing. +[SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal). ## Probing for Unrecognized Devices diff --git a/test/serial_test/serial_test.ino b/test/serial_test/serial_test.ino deleted file mode 100644 index 6964001..0000000 --- a/test/serial_test/serial_test.ino +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright 2012 Google Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * Project home page: http://code.google.com/p/usb-serial-for-android/ - */ - -// Sample Arduino sketch for use with usb-serial-for-android. -// Prints an ever-increasing counter, and writes back anything -// it receives. - -static int counter = 0; -void setup() { - Serial.begin(115200); -} - -void loop() { - Serial.print("Tick #"); - Serial.print(counter++, DEC); - Serial.print("\n"); - - if (Serial.peek() != -1) { - Serial.print("Read: "); - do { - Serial.print((char) Serial.read()); - } while (Serial.peek() != -1); - Serial.print("\n"); - } - delay(1000); -} diff --git a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java index 0ac13b9..4d92811 100644 --- a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java +++ b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java @@ -194,22 +194,7 @@ public class DeviceTest implements SerialInputOutputManager.Listener { try { telnetRead(0); } catch (Exception ignored) {} - - try { - usbIoManager.setListener(null); - usbIoManager.stop(); - } catch (Exception ignored) {} - try { - usbSerialPort.setDTR(false); - usbSerialPort.setRTS(false); - usbSerialPort.close(); - } catch (Exception ignored) {} - try { - usbDeviceConnection.close(); - } catch (Exception ignored) {} - usbIoManager = null; - usbSerialPort = null; - usbDeviceConnection = null; + usbClose(); usbSerialDriver = null; } @@ -261,10 +246,15 @@ public class DeviceTest implements SerialInputOutputManager.Listener { private void usbClose() { if (usbIoManager != null) { + usbIoManager.setListener(null); usbIoManager.stop(); - usbIoManager = null; } if (usbSerialPort != null) { + try { + usbSerialPort.setDTR(false); + usbSerialPort.setRTS(false); + } catch (Exception ignored) { + } try { usbSerialPort.close(); } catch (IOException ignored) { @@ -274,6 +264,18 @@ public class DeviceTest implements SerialInputOutputManager.Listener { if(usbDeviceConnection != null) usbDeviceConnection.close(); usbDeviceConnection = null; + if(usbIoManager != null) { + for(int i=0; i<2000; i++) { + if(SerialInputOutputManager.State.STOPPED == usbIoManager.getState()) break; + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + assertEquals(SerialInputOutputManager.State.STOPPED, usbIoManager.getState()); + usbIoManager = null; + } } private void usbOpen(boolean withIoManager) throws Exception { @@ -520,6 +522,11 @@ public class DeviceTest implements SerialInputOutputManager.Listener { usbSerialPort.close(); } catch (IOException ignored) { } + + if (usbSerialDriver instanceof Cp21xxSerialDriver) { // why needed? + usbIoManager.stop(); + usbIoManager = null; + } // full re-open supported usbClose(); usbOpen(true); @@ -640,8 +647,8 @@ public class DeviceTest implements SerialInputOutputManager.Listener { assertNotEquals(data1, buf1); assertNotEquals(data2, buf2); } - assertThat("42000/8N1", data1, equalTo(buf1)); } else { + assertThat("42000/8N1", data1, equalTo(buf1)); assertThat("42000/8N1", data2, equalTo(buf2)); } } @@ -1109,4 +1116,85 @@ public class DeviceTest implements SerialInputOutputManager.Listener { // todo: purge receive buffer } + + @Test + // WriteAsync rarely makes sense, as data is not written until something is read + public void writeAsync() throws Exception { + if (usbSerialDriver instanceof FtdiSerialDriver) + return; // periodically sends status messages, so does not block here + usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + + SerialInputOutputManager ioManager; + ioManager = new SerialInputOutputManager(null); + assertEquals(null, ioManager.getListener()); + ioManager.setListener(this); + assertEquals(this, ioManager.getListener()); + ioManager = new SerialInputOutputManager(null, this); + assertEquals(this, ioManager.getListener()); + + byte[] data, buf = new byte[]{1}; + int len; + usbIoManager.writeAsync(buf); + usbIoManager.writeAsync(buf); + data = telnetRead(1); + assertEquals(0, data.length); + telnetWrite(buf); + data = usbRead(1); + assertEquals(1, data.length); + data = telnetRead(2); + assertEquals(2, data.length); + } + + @Test + // Blocking read should be avoided in the UI thread, as it makes the app unresponsive. + // You better use the SerialInputOutputManager. + // + // With the change from bulkTransfer to queued requests, the read timeout has no effect + // and the call blocks until close() if no data is available! + // The change from bulkTransfer to queued requests was necessary to prevent data loss. + public void readSync() throws Exception { + if (usbSerialDriver instanceof FtdiSerialDriver) + return; // periodically sends status messages, so does not block here + + Runnable closeThread = new Runnable() { + @Override + public void run() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + usbClose(); + } + }; + + usbClose(); + usbOpen(false); + usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + + byte[] buf = new byte[]{1}; + int len; + long time; + telnetWrite(buf); + len = usbSerialPort.read(buf, 0); // not blocking because data is available + assertEquals(1, len); + + time = System.currentTimeMillis(); + Executors.newSingleThreadExecutor().submit(closeThread); + len = usbSerialPort.read(buf, 0); // blocking until close() + assertEquals(0, len); + assertTrue(System.currentTimeMillis()-time >= 100); + + usbOpen(false); + usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + + time = System.currentTimeMillis(); + Executors.newSingleThreadExecutor().submit(closeThread); + len = usbSerialPort.read(buf, 10); // timeout not used any more -> blocking until close() + assertEquals(0, len); + assertTrue(System.currentTimeMillis()-time >= 100); + } } diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java index 78bcd0b..1ab3edf 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java @@ -50,7 +50,7 @@ public class SerialInputOutputManager implements Runnable { // Synchronized by 'mWriteBuffer' private final ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ); - private enum State { + public enum State { STOPPED, RUNNING, STOPPING @@ -111,7 +111,7 @@ public class SerialInputOutputManager implements Runnable { } } - private synchronized State getState() { + public synchronized State getState() { return mState; }