Skip to content

Commit

Permalink
USB Serial driver patch (#192)
Browse files Browse the repository at this point in the history
* USB driver patch

Refactored SerialInputOutputManager:

- Implemented multiple read buffers to improve USB reading efficiency and reduce latency.
- Used separate threads for reading and writing, enhancing concurrency and performance.
- Removed read timeouts, as they are not necessary for event-driven data reading.

These changes enhance the responsiveness, reliability, and robustness of USB communication.

* fixed merge

* fixed merge

* cleanup.

* Removed unused import
  • Loading branch information
dkaukov authored Jan 26, 2025
1 parent 3c3d7c0 commit a768980
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ kv4p HT (see http://kv4p.com)
import com.google.android.gms.tasks.CancellationTokenSource;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.hoho.android.usbserial.driver.SerialTimeoutException;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;
Expand Down Expand Up @@ -389,9 +388,6 @@ public void setMode(int mode) {
usbIoManager.stop();
break;
default:
if (null != usbIoManager && usbIoManager.getState() == SerialInputOutputManager.State.STOPPED) {
usbIoManager.start();
}
break;
}

Expand Down Expand Up @@ -935,7 +931,8 @@ public void onRunError(Exception e) {
}
});
usbIoManager.setWriteBufferSize(90000); // Must be large enough that ESP32 can take its time accepting our bytes without overrun.
usbIoManager.setReadTimeout(1000); // Must not be 0 (infinite) or it may block on read() until a write() occurs.
usbIoManager.setReadBufferSize(1024/4); // Must not be 0 (infinite) or it may block on read() until a write() occurs.
usbIoManager.setReadBufferCount(16*4);
usbIoManager.start();
checkedFirmwareVersion = false;

Expand Down Expand Up @@ -1221,43 +1218,7 @@ public synchronized void sendBytesToESP32(byte[] newBytes) {
Log.d("DEBUG", "Warning: Attempted to send bytes to ESP32 while in the process of flashing a new firmware.");
return;
}

int usbRetries = 0;
try {
// usbIoManager.writeAsync(newBytes); // On MCUs like the ESP32 S2 this causes USB failures with concurrent USB rx/tx.
int bytesWritten = 0;
int totalBytes = newBytes.length;
final int MAX_BYTES_PER_USB_WRITE = 128;
do {
try {
byte[] arrayPart = Arrays.copyOfRange(newBytes, bytesWritten, Math.min(bytesWritten + MAX_BYTES_PER_USB_WRITE, totalBytes));
serialPort.write(arrayPart, 200);
bytesWritten += MAX_BYTES_PER_USB_WRITE;
usbRetries = 0;
} catch (SerialTimeoutException ste) {
// Do nothing, we'll try again momentarily. ESP32's serial buffer may be full.
usbRetries++;
Log.d("DEBUG", "usbRetries: " + usbRetries);
}
} while (bytesWritten < totalBytes && usbRetries < 10);
// Log.d("DEBUG", "Wrote data: " + Arrays.toString(newBytes));
} catch (Exception e) {
e.printStackTrace();
try {
serialPort.close();
} catch (Exception ex) {
// Ignore. We did our best to close it!
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
// Ignore. This should only happen if the app is paused in this brief moment between USB retries, not a serious issue.
}
findESP32Device(); // Attempt to reconnect after the brief pause above.
}
if (usbRetries == 10) {
Log.d("DEBUG", "sendBytesToESP32: Connected to ESP32 via USB serial, but could not send data after 10 retries.");
}
usbIoManager.writeAsync(newBytes);
}

public static UsbSerialPort getUsbSerialPort() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,6 @@ private int readSpeedInt(int writeSeconds, int readBufferSize, int readTimeout)
writeAhead = 50;

usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START));
usb.ioManager.setReadTimeout(readTimeout);
if(readBufferSize > 0)
usb.ioManager.setReadBufferSize(readBufferSize);
usb.ioManager.start();
Expand Down Expand Up @@ -1383,9 +1382,6 @@ public void IoManager() throws Exception {
usb.ioManager = new SerialInputOutputManager(usb.serialPort, usb);
assertEquals(usb, usb.ioManager.getListener());

assertEquals(0, usb.ioManager.getReadTimeout());
usb.ioManager.setReadTimeout(10);
assertEquals(10, usb.ioManager.getReadTimeout());
assertEquals(0, usb.ioManager.getWriteTimeout());
usb.ioManager.setWriteTimeout(11);
assertEquals(11, usb.ioManager.getWriteTimeout());
Expand All @@ -1399,7 +1395,6 @@ public void IoManager() throws Exception {

usb.ioManager.setReadBufferSize(usb.ioManager.getReadBufferSize());
usb.ioManager.setWriteBufferSize(usb.ioManager.getWriteBufferSize());
usb.ioManager.setReadTimeout(usb.ioManager.getReadTimeout());
usb.ioManager.setWriteTimeout(usb.ioManager.getWriteTimeout());
usb.close();

Expand All @@ -1416,19 +1411,14 @@ public void IoManager() throws Exception {
} catch (IllegalStateException ignored) {
}
try {
usb.ioManager.run();
usb.ioManager.start();
fail("already running error expected");
} catch (IllegalStateException ignored) {
}
try {
usb.ioManager.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
fail("setThreadPriority IllegalStateException expected");
} catch (IllegalStateException ignored) {}
try {
usb.ioManager.setReadTimeout(20);
fail("setReadTimeout IllegalStateException expected");
} catch (IllegalStateException ignored) {}
assertEquals(0, usb.ioManager.getReadTimeout());
usb.ioManager.setWriteTimeout(21);
assertEquals(21, usb.ioManager.getWriteTimeout());
usb.ioManager.setReadBufferSize(22);
Expand Down Expand Up @@ -1484,7 +1474,6 @@ public void IoManager() throws Exception {
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
usb.ioManager.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
Executors.newSingleThreadExecutor().submit(usb.ioManager);
usb.waitForIoManagerStarted();
try {
usb.ioManager.start();
Expand Down Expand Up @@ -1514,15 +1503,13 @@ public void writeAsync() throws Exception {

// with timeout: write after timeout
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START));
usb.ioManager.setReadTimeout(100);
usb.ioManager.start();
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
usb.ioManager.writeAsync(buf);
usb.ioManager.writeAsync(buf);
data = telnet.read(2);
assertEquals(2, data.length);
usb.ioManager.setReadTimeout(200);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
protected final UsbDevice mDevice;
protected final int mPortNumber;

// non-null when open()
@Override
public UsbDeviceConnection getConnection() {
return mConnection;
}

// non-null when open()
protected UsbDeviceConnection mConnection = null;
protected UsbEndpoint mReadEndpoint;
protected UsbEndpoint mWriteEndpoint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,6 @@ enum ControlLine { RTS, CTS, DTR, DSR, CD, RI }
*/
boolean isOpen();

UsbDeviceConnection getConnection();

}
Loading

0 comments on commit a768980

Please sign in to comment.