mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-31 02:17:23 +00:00 
			
		
		
		
	build tools update; instrumented device test
This commit is contained in:
		
							parent
							
								
									c89ca2c96a
								
							
						
					
					
						commit
						adb22f718e
					
				
							
								
								
									
										37
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										37
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							| @ -1,19 +1,30 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="EntryPointsManager"> | ||||
|     <entry_points version="2.0" /> | ||||
|   <component name="NullableNotNullManager"> | ||||
|     <option name="myDefaultNullable" value="android.support.annotation.Nullable" /> | ||||
|     <option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> | ||||
|     <option name="myNullables"> | ||||
|       <value> | ||||
|         <list size="4"> | ||||
|           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> | ||||
|           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> | ||||
|           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> | ||||
|           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> | ||||
|         </list> | ||||
|       </value> | ||||
|     </option> | ||||
|     <option name="myNotNulls"> | ||||
|       <value> | ||||
|         <list size="4"> | ||||
|           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> | ||||
|           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> | ||||
|           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> | ||||
|           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> | ||||
|         </list> | ||||
|       </value> | ||||
|     </option> | ||||
|   </component> | ||||
|   <component name="ProjectLevelVcsManager" settingsEditedManually="false"> | ||||
|     <OptionsSetting value="true" id="Add" /> | ||||
|     <OptionsSetting value="true" id="Remove" /> | ||||
|     <OptionsSetting value="true" id="Checkout" /> | ||||
|     <OptionsSetting value="true" id="Update" /> | ||||
|     <OptionsSetting value="true" id="Status" /> | ||||
|     <OptionsSetting value="true" id="Edit" /> | ||||
|     <ConfirmationsSetting value="0" id="Add" /> | ||||
|     <ConfirmationsSetting value="0" id="Remove" /> | ||||
|   </component> | ||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> | ||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK"> | ||||
|     <output url="file://$PROJECT_DIR$/build/classes" /> | ||||
|   </component> | ||||
|   <component name="ProjectType"> | ||||
|  | ||||
							
								
								
									
										12
									
								
								.idea/runConfigurations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.idea/runConfigurations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="RunConfigurationProducerService"> | ||||
|     <option name="ignoredProducers"> | ||||
|       <set> | ||||
|         <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> | ||||
|         <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> | ||||
|         <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> | ||||
|       </set> | ||||
|     </option> | ||||
|   </component> | ||||
| </project> | ||||
| @ -2,15 +2,17 @@ | ||||
| 
 | ||||
| buildscript { | ||||
|     repositories { | ||||
|         mavenCentral() | ||||
|         jcenter() | ||||
|         google() | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:1.2.3' | ||||
|         classpath 'com.android.tools.build:gradle:3.0.1' | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| allprojects { | ||||
|     repositories { | ||||
|         mavenCentral() | ||||
|         jcenter() | ||||
|         google() | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| #Tue Jun 23 00:11:28 EDT 2015 | ||||
| #Thu Oct 26 20:06:55 CEST 2017 | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip | ||||
|  | ||||
							
								
								
									
										64
									
								
								test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| /*
 | ||||
|   bridge USB-serial to hardware-serial | ||||
| 
 | ||||
|   for Arduinos based on ATmega32u4 (Leonardo and compatible Pro Micro, Micro) | ||||
|   hardware serial is configured with baud-rate, databits, stopbits, parity as send over USB | ||||
| 
 | ||||
|   see https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino 
 | ||||
|   -> CDC.cpp|HardwareSerial.cpp for serial implementation details | ||||
| 
 | ||||
|   this sketch is mainly for demonstration / test of CDC communication | ||||
|   performance as real usb-serial bridge would be inacceptable as each byte is send in separate USB packet | ||||
| */ | ||||
| 
 | ||||
| uint32_t baud = 9600; | ||||
| uint8_t databits = 8; | ||||
| uint8_t stopbits = 1; | ||||
| uint8_t parity = 0; | ||||
| 
 | ||||
| void setup() { | ||||
|   Serial.begin(baud); // USB
 | ||||
|   Serial1.begin(baud, SERIAL_8N1); | ||||
| } | ||||
| 
 | ||||
| void loop() { | ||||
|   // show USB connected state
 | ||||
|   if (Serial) TXLED1; | ||||
|   else        TXLED0; | ||||
| 
 | ||||
|   // configure hardware serial
 | ||||
|   if (Serial.baud() != baud || | ||||
|       Serial.numbits() != databits || | ||||
|       Serial.stopbits() != stopbits || | ||||
|       Serial.paritytype() != parity) { | ||||
|     baud = Serial.baud(); | ||||
|     databits = Serial.numbits(); | ||||
|     stopbits = Serial.stopbits(); | ||||
|     parity = Serial.paritytype(); | ||||
|     uint8_t config = 0; // ucsrc register
 | ||||
|     switch (databits) { | ||||
|       case 5: break; | ||||
|       case 6: config |= 2; break; | ||||
|       case 7: config |= 4; break; | ||||
|       case 8: config |= 6; break; | ||||
|       default: config |= 6; | ||||
|     } | ||||
|     switch (stopbits) { | ||||
|       case 2: config |= 8; | ||||
|         // 1.5 stopbits not supported
 | ||||
|     } | ||||
|     switch (parity) { | ||||
|       case 1: config |= 0x30; break; // odd
 | ||||
|       case 2: config |= 0x20; break; // even
 | ||||
|         // mark, space not supported
 | ||||
|     } | ||||
|     Serial1.end(); | ||||
|     Serial1.begin(baud, config); | ||||
|   } | ||||
| 
 | ||||
|   // bridge
 | ||||
|   if (Serial.available() > 0) | ||||
|     Serial1.write(Serial.read()); | ||||
|   if (Serial1.available() > 0) | ||||
|     Serial.write(Serial1.read()); | ||||
| } | ||||
							
								
								
									
										42
									
								
								test/rfc2217_server.diff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								test/rfc2217_server.diff
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| *** /n/archiv/python/rfc2217_server.py	2018-03-10 09:02:07.613771600 +0100 | ||||
| --- rfc2217_server.py	2018-03-09 20:57:44.933717100 +0100
 | ||||
| *************** | ||||
| *** 26,31 **** | ||||
| --- 26,32 ----
 | ||||
|               self, | ||||
|               logger=logging.getLogger('rfc2217.server') if debug else None) | ||||
|           self.log = logging.getLogger('redirector') | ||||
| +         self.dlog = logging.getLogger('data')
 | ||||
|    | ||||
|       def statusline_poller(self): | ||||
|           self.log.debug('status line poll thread started') | ||||
| *************** | ||||
| *** 55,60 **** | ||||
| --- 56,62 ----
 | ||||
|               try: | ||||
|                   data = self.serial.read(self.serial.in_waiting or 1) | ||||
|                   if data: | ||||
| +                     self.dlog.debug("serial read: "+data.encode('hex'))
 | ||||
|                       # escape outgoing data when needed (Telnet IAC (0xff) character) | ||||
|                       self.write(b''.join(self.rfc2217.escape(data))) | ||||
|               except socket.error as msg: | ||||
| *************** | ||||
| *** 76,81 **** | ||||
| --- 78,84 ----
 | ||||
|                   data = self.socket.recv(1024) | ||||
|                   if not data: | ||||
|                       break | ||||
| +                 self.dlog.debug("socket read: "+data.encode('hex'))
 | ||||
|                   self.serial.write(b''.join(self.rfc2217.filter(data))) | ||||
|               except socket.error as msg: | ||||
|                   self.log.error('{}'.format(msg)) | ||||
| *************** | ||||
| *** 132,137 **** | ||||
| --- 135,141 ----
 | ||||
|       logging.basicConfig(level=logging.INFO) | ||||
|       #~ logging.getLogger('root').setLevel(logging.INFO) | ||||
|       logging.getLogger('rfc2217').setLevel(level) | ||||
| +     logging.getLogger('data').setLevel(level)
 | ||||
|    | ||||
|       # connect to serial port | ||||
|       ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True) | ||||
| @ -1,14 +1,13 @@ | ||||
| apply plugin: 'com.android.application' | ||||
| 
 | ||||
| android { | ||||
|     compileSdkVersion 22 | ||||
|     buildToolsVersion "22.0.1" | ||||
|     compileSdkVersion 26 | ||||
|     buildToolsVersion "26.0.2" | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         minSdkVersion 14 | ||||
|         targetSdkVersion 22 | ||||
|         targetSdkVersion 26 | ||||
| 
 | ||||
|         testApplicationId "com.hoho.android.usbserial.examples" | ||||
|         testInstrumentationRunner "android.test.InstrumentationTestRunner" | ||||
|     } | ||||
| 
 | ||||
| @ -20,5 +19,5 @@ android { | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     compile project(':usbSerialForAndroid') | ||||
|     implementation project(':usbSerialForAndroid') | ||||
| } | ||||
|  | ||||
| @ -3,12 +3,13 @@ apply plugin: 'maven' | ||||
| apply plugin: 'signing' | ||||
| 
 | ||||
| android { | ||||
|     compileSdkVersion 19 | ||||
|     buildToolsVersion "19.1" | ||||
|     compileSdkVersion 26 | ||||
|     buildToolsVersion "26.0.2" | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         minSdkVersion 12 | ||||
|         targetSdkVersion 19 | ||||
|         minSdkVersion 14 | ||||
|         targetSdkVersion 26 | ||||
|         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | ||||
|     } | ||||
| 
 | ||||
|     buildTypes { | ||||
| @ -19,6 +20,14 @@ android { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     testImplementation 'junit:junit:4.12' | ||||
|     androidTestImplementation 'com.android.support:support-annotations:27.1.0' | ||||
|     androidTestImplementation 'com.android.support.test:runner:1.0.1' | ||||
|     androidTestImplementation 'commons-net:commons-net:3.6' | ||||
|     androidTestImplementation 'org.apache.commons:commons-lang3:3.7' | ||||
| } | ||||
| 
 | ||||
| group = "com.hoho.android" | ||||
| version = "0.2.0-SNAPSHOT" | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										7
									
								
								usbSerialForAndroid/src/androidTest/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usbSerialForAndroid/src/androidTest/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|       package="com.hoho.android.usbserial" | ||||
|       android:versionCode="1" | ||||
|       android:versionName="1.0"> | ||||
|     <uses-permission android:name="android.permission.INTERNET"/> | ||||
| </manifest> | ||||
| @ -0,0 +1,855 @@ | ||||
| /* | ||||
|  * test setup | ||||
|  * - android device with ADB over Wi-Fi | ||||
|  *      - to set up ADB over Wi-Fi with custom roms you typically can do it from: Android settings -> Developer options | ||||
|  *      - for other devices you first have to manually connect over USB and enable Wi-Fi as shown here: | ||||
|  *         https://developer.android.com/studio/command-line/adb.html | ||||
|  * - windows/linux machine running rfc2217_server.py | ||||
|  *      python + pyserial + https://github.com/pyserial/pyserial/blob/master/examples/rfc2217_server.py | ||||
|  *      for developing this test it was essential to see all data (see test/rfc2217_server.diff, run python script with '-v -v' option) | ||||
|  * - all suppported usb <-> serial converter | ||||
|  *      as CDC test device use an arduino leonardo / pro mini programmed with arduino_leonardo_bridge.ino | ||||
|  * | ||||
|  * restrictions | ||||
|  *  - as real hardware is used, timing might need tuning. see: | ||||
|  *      - Thread.sleep(...) | ||||
|  *      - obj.wait(...) | ||||
|  *  - some tests fail sporadically. typical workarounds are: | ||||
|  *      - reconnect device | ||||
|  *      - run test individually | ||||
|  *      - increase sleep? | ||||
|  *  - missing functionality on certain devices, see: | ||||
|  *      - if(rfc2217_server_nonstandard_baudrates) | ||||
|  *      - if(usbSerialDriver instanceof ...) | ||||
|  * | ||||
|  */ | ||||
| package com.hoho.android.usbserial; | ||||
| 
 | ||||
| import android.app.PendingIntent; | ||||
| import android.content.BroadcastReceiver; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.IntentFilter; | ||||
| import android.hardware.usb.UsbDevice; | ||||
| import android.hardware.usb.UsbDeviceConnection; | ||||
| import android.hardware.usb.UsbManager; | ||||
| import android.support.test.InstrumentationRegistry; | ||||
| import android.support.test.runner.AndroidJUnit4; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import com.hoho.android.usbserial.driver.CdcAcmSerialDriver; | ||||
| import com.hoho.android.usbserial.driver.Ch34xSerialDriver; | ||||
| import com.hoho.android.usbserial.driver.Cp21xxSerialDriver; | ||||
| import com.hoho.android.usbserial.driver.FtdiSerialDriver; | ||||
| import com.hoho.android.usbserial.driver.ProbeTable; | ||||
| import com.hoho.android.usbserial.driver.ProlificSerialDriver; | ||||
| import com.hoho.android.usbserial.driver.UsbSerialDriver; | ||||
| import com.hoho.android.usbserial.driver.UsbSerialPort; | ||||
| import com.hoho.android.usbserial.driver.UsbSerialProber; | ||||
| import com.hoho.android.usbserial.util.SerialInputOutputManager; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.apache.commons.net.telnet.InvalidTelnetOptionException; | ||||
| import org.apache.commons.net.telnet.TelnetClient; | ||||
| import org.apache.commons.net.telnet.TelnetCommand; | ||||
| import org.apache.commons.net.telnet.TelnetOptionHandler; | ||||
| import org.junit.After; | ||||
| import org.junit.AfterClass; | ||||
| import org.junit.Before; | ||||
| import org.junit.BeforeClass; | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.util.Deque; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.Executors; | ||||
| 
 | ||||
| import static org.hamcrest.CoreMatchers.equalTo; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNotEquals; | ||||
| import static org.junit.Assert.assertThat; | ||||
| import static org.junit.Assert.assertTrue; | ||||
| import static org.junit.Assert.fail; | ||||
| 
 | ||||
| @RunWith(AndroidJUnit4.class) | ||||
| public class DeviceTest implements SerialInputOutputManager.Listener { | ||||
| 
 | ||||
|     private final static String  rfc2217_server_host = "192.168.0.171"; | ||||
|     private final static int     rfc2217_server_port = 2217; | ||||
|     private final static boolean rfc2217_server_nonstandard_baudrates = false; // false on Windows | ||||
| 
 | ||||
|     private final static int     TELNET_READ_WAIT = 500; | ||||
|     private final static int     USB_READ_WAIT    = 500; | ||||
|     private final static int     USB_WRITE_WAIT   = 500; | ||||
| 
 | ||||
|     private final static String  TAG = "DeviceTest"; | ||||
|     private final static byte    RFC2217_COM_PORT_OPTION = 0x2c; | ||||
|     private final static byte    RFC2217_SET_BAUDRATE = 1; | ||||
|     private final static byte    RFC2217_SET_DATASIZE = 2; | ||||
|     private final static byte    RFC2217_SET_PARITY   = 3; | ||||
|     private final static byte    RFC2217_SET_STOPSIZE = 4; | ||||
| 
 | ||||
|     private Context context; | ||||
|     private UsbSerialDriver usbSerialDriver; | ||||
|     private UsbDeviceConnection usbDeviceConnection; | ||||
|     private UsbSerialPort usbSerialPort; | ||||
|     private SerialInputOutputManager usbIoManager; | ||||
|     private final Deque<byte[]> usbReadBuffer = new LinkedList<>(); | ||||
|     private boolean usbReadBlock = false; | ||||
| 
 | ||||
|     private static TelnetClient telnetClient; | ||||
|     private static InputStream telnetReadStream; | ||||
|     private static OutputStream telnetWriteStream; | ||||
|     private static Integer[] telnetComPortOptionCounter = {0}; | ||||
|     private int telnetWriteDelay = 0; | ||||
| 
 | ||||
|     @BeforeClass | ||||
|     public static void setUpFixture() throws Exception { | ||||
|         telnetClient = null; | ||||
|         // postpone fixture setup to first test, because exceptions are not reported for @BeforeClass | ||||
|         // and test terminates with missleading 'Empty test suite' | ||||
|     } | ||||
| 
 | ||||
|     public static void setUpFixtureInt() throws Exception { | ||||
|         if(telnetClient != null) | ||||
|             return; | ||||
|         telnetClient = new TelnetClient(); | ||||
|         telnetClient.addOptionHandler(new TelnetOptionHandler(RFC2217_COM_PORT_OPTION, false, false, false, false) { | ||||
|             @Override | ||||
|             public int[] answerSubnegotiation(int[] suboptionData, int suboptionLength) { | ||||
|                 telnetComPortOptionCounter[0] += 1; | ||||
|                 return super.answerSubnegotiation(suboptionData, suboptionLength); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         telnetClient.setConnectTimeout(2000); | ||||
|         telnetClient.connect(rfc2217_server_host, rfc2217_server_port); | ||||
|         telnetClient.setTcpNoDelay(true); | ||||
|         telnetWriteStream = telnetClient.getOutputStream(); | ||||
|         telnetReadStream = telnetClient.getInputStream(); | ||||
|     } | ||||
| 
 | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         setUpFixtureInt(); | ||||
|         telnetClient.sendAYT(1000); // not corrctly handled by rfc2217_server.py, but WARNING output "ignoring Telnet command: '\xf6'" is a nice separator between tests | ||||
|         telnetComPortOptionCounter[0] = 0; | ||||
|         telnetWriteDelay = 0; | ||||
| 
 | ||||
|         context = InstrumentationRegistry.getContext(); | ||||
|         final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); | ||||
|         List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager); | ||||
|         assertEquals("no usb device found", 1, availableDrivers.size()); | ||||
|         usbSerialDriver = availableDrivers.get(0); | ||||
|         assertEquals(1, usbSerialDriver.getPorts().size()); | ||||
|         usbSerialPort = usbSerialDriver.getPorts().get(0); | ||||
|         Log.i(TAG, "Using USB device "+ usbSerialDriver.getClass().getSimpleName()); | ||||
| 
 | ||||
| 
 | ||||
|         if (!usbManager.hasPermission(usbSerialPort.getDriver().getDevice())) { | ||||
|             final Boolean[] granted = {Boolean.FALSE}; | ||||
|             BroadcastReceiver usbReceiver = new BroadcastReceiver() { | ||||
|                 @Override | ||||
|                 public void onReceive(Context context, Intent intent) { | ||||
|                     granted[0] = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); | ||||
|                     synchronized (granted) { | ||||
|                         granted.notify(); | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|             PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0, new Intent("com.android.example.USB_PERMISSION"), 0); | ||||
|             IntentFilter filter = new IntentFilter("com.android.example.USB_PERMISSION"); | ||||
|             context.registerReceiver(usbReceiver, filter); | ||||
|             usbManager.requestPermission(usbSerialDriver.getDevice(), permissionIntent); | ||||
|             synchronized (granted) { | ||||
|                 granted.wait(5000); | ||||
|             } | ||||
|             assertTrue("USB permission dialog not confirmed", granted[0]); | ||||
|         } | ||||
|         usbDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice()); | ||||
|         usbSerialPort.open(usbDeviceConnection); | ||||
|         usbSerialPort.setDTR(true); | ||||
|         usbSerialPort.setRTS(true); | ||||
|         usbIoManager = new SerialInputOutputManager(usbSerialPort, this); | ||||
|         Executors.newSingleThreadExecutor().submit(usbIoManager); | ||||
| 
 | ||||
|         synchronized (usbReadBuffer) { | ||||
|             usbReadBuffer.clear(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @After | ||||
|     public void tearDown() throws IOException { | ||||
|         try { | ||||
|             usbRead(0); | ||||
|         } catch (Exception ignored) {} | ||||
|         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; | ||||
|         usbSerialDriver = null; | ||||
|     } | ||||
| 
 | ||||
|     @AfterClass | ||||
|     public static void tearDownFixture() throws Exception { | ||||
|         try { | ||||
|             telnetClient.disconnect(); | ||||
|         } catch (Exception ignored) {} | ||||
|         telnetReadStream = null; | ||||
|         telnetWriteStream = null; | ||||
|         telnetClient = null; | ||||
|     } | ||||
| 
 | ||||
|     // wait full time | ||||
|     private byte[] telnetRead() throws Exception { | ||||
|         return telnetRead(-1); | ||||
|     } | ||||
| 
 | ||||
|     private byte[] telnetRead(int expectedLength) throws Exception { | ||||
|         long end = System.currentTimeMillis() + TELNET_READ_WAIT; | ||||
|         ByteBuffer buf = ByteBuffer.allocate(4096); | ||||
|         while(System.currentTimeMillis() < end) { | ||||
|             if(telnetReadStream.available() > 0) { | ||||
|                 buf.put((byte) telnetReadStream.read()); | ||||
|             } else { | ||||
|                 if (expectedLength >= 0 && buf.position() >= expectedLength) | ||||
|                     break; | ||||
|                 Thread.sleep(1); | ||||
|             } | ||||
|         } | ||||
|         byte[] data = new byte[buf.position()]; | ||||
|         buf.flip(); | ||||
|         buf.get(data); | ||||
|         return data; | ||||
|     } | ||||
| 
 | ||||
|     private void telnetWrite(byte[] data) throws Exception{ | ||||
|         if(telnetWriteDelay != 0) { | ||||
|             for(byte b : data) { | ||||
|                 telnetWriteStream.write(b); | ||||
|                 telnetWriteStream.flush(); | ||||
|                 Thread.sleep(telnetWriteDelay); | ||||
|             } | ||||
|         } else { | ||||
|             telnetWriteStream.write(data); | ||||
|             telnetWriteStream.flush(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // wait full time | ||||
|     private byte[] usbRead() throws Exception { | ||||
|         return usbRead(-1); | ||||
|     } | ||||
| 
 | ||||
|     private byte[] usbRead(int expectedLength) throws Exception { | ||||
|         long end = System.currentTimeMillis() + USB_READ_WAIT; | ||||
|         ByteBuffer buf = ByteBuffer.allocate(4096); | ||||
|         if(usbIoManager != null) { | ||||
|             while (System.currentTimeMillis() < end) { | ||||
|                 synchronized (usbReadBuffer) { | ||||
|                     while(usbReadBuffer.peek() != null) | ||||
|                         buf.put(usbReadBuffer.remove()); | ||||
|                 } | ||||
|                 if (expectedLength >= 0 && buf.position() >= expectedLength) | ||||
|                     break; | ||||
|                 Thread.sleep(1); | ||||
|             } | ||||
| 
 | ||||
|         } else { | ||||
|             byte[] b1 = new byte[256]; | ||||
|             while (System.currentTimeMillis() < end) { | ||||
|                 int len = usbSerialPort.read(b1, USB_READ_WAIT / 10); | ||||
|                 if (len > 0) { | ||||
|                     buf.put(b1, 0, len); | ||||
|                 } else { | ||||
|                     if (expectedLength >= 0 && buf.position() >= expectedLength) | ||||
|                         break; | ||||
|                     Thread.sleep(1); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         byte[] data = new byte[buf.position()]; | ||||
|         buf.flip(); | ||||
|         buf.get(data); | ||||
|         return data; | ||||
|     } | ||||
| 
 | ||||
|     private void usbWrite(byte[] data) throws IOException { | ||||
|         usbSerialPort.write(data, USB_WRITE_WAIT); | ||||
|     } | ||||
| 
 | ||||
|     private void usbParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException, InterruptedException { | ||||
|         usbSerialPort.setParameters(baudRate, dataBits, stopBits, parity); | ||||
|         if(usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|             Thread.sleep(10); // arduino_leonardeo_bridge.ini needs some time | ||||
|         else | ||||
|             Thread.sleep(1); | ||||
|     } | ||||
| 
 | ||||
|     private void telnetParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException, InterruptedException, InvalidTelnetOptionException { | ||||
|         telnetComPortOptionCounter[0] = 0; | ||||
| 
 | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||
|         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_BAUDRATE, (byte)(baudRate>>24), (byte)(baudRate>>16), (byte)(baudRate>>8), (byte)baudRate}); | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||
| 
 | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||
|         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_DATASIZE, (byte)dataBits}); | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||
| 
 | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||
|         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_STOPSIZE, (byte)stopBits}); | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||
| 
 | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||
|         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_PARITY, (byte)(parity+1)}); | ||||
|         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||
| 
 | ||||
|         // windows does not like nonstandard baudrates. rfc2217_server.py terminates w/o response | ||||
|         for(int i=0; i<2000; i++) { | ||||
|             if(telnetComPortOptionCounter[0] == 4) break; | ||||
|             Thread.sleep(1); | ||||
|         } | ||||
|         assertEquals("telnet connection lost", 4, telnetComPortOptionCounter[0].intValue()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onNewData(byte[] data) { | ||||
|         while(usbReadBlock) | ||||
|             try { | ||||
|                 Thread.sleep(1); | ||||
|             } catch (InterruptedException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|         synchronized (usbReadBuffer) { | ||||
|             usbReadBuffer.add(data); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onRunError(Exception e) { | ||||
|         assertTrue("usb connection lost", false); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Test | ||||
|     public void baudRate() throws Exception { | ||||
|         byte[] data; | ||||
| 
 | ||||
|         if (false) { // default baud rate | ||||
|             // CP2102: only works if first connection after attaching device | ||||
|             // PL2303, FTDI: it's not 9600 | ||||
|             telnetParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
| 
 | ||||
|             telnetWrite("net2usb".getBytes()); | ||||
|             data = usbRead(7); | ||||
|             assertThat(data, equalTo("net2usb".getBytes())); // includes array content in output | ||||
|             //assertArrayEquals("net2usb".getBytes(), data); // only includes array length in output | ||||
|             usbWrite("usb2net".getBytes()); | ||||
|             data = telnetRead(7); | ||||
|             assertThat(data, equalTo("usb2net".getBytes())); | ||||
|         } | ||||
| 
 | ||||
|         // invalid values | ||||
|         try { | ||||
|             usbParameters(-1, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|             if (usbSerialDriver instanceof Ch34xSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else if (usbSerialDriver instanceof FtdiSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else if (usbSerialDriver instanceof ProlificSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else | ||||
|                 fail("invalid baudrate 0"); | ||||
|         } catch (java.lang.IllegalArgumentException e) { | ||||
|         } | ||||
|         try { | ||||
|             usbParameters(0, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|             if (usbSerialDriver instanceof ProlificSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|                 ; // todo: add range check in driver | ||||
|             else | ||||
|                 fail("invalid baudrate 0"); | ||||
|         } catch (java.lang.ArithmeticException e) { // ch340 | ||||
|         } catch (java.lang.IllegalArgumentException e) { | ||||
|         } | ||||
|         try { | ||||
|             usbParameters(1, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|             if (usbSerialDriver instanceof FtdiSerialDriver) | ||||
|                 ; | ||||
|             else if (usbSerialDriver instanceof ProlificSerialDriver) | ||||
|                 ; | ||||
|             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||
|                 ; | ||||
|             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|                 ; | ||||
|             else | ||||
|                 fail("invalid baudrate 0"); | ||||
|         } catch (java.io.IOException e) { // ch340 | ||||
|         } catch (java.lang.IllegalArgumentException e) { | ||||
|         } | ||||
|         try { | ||||
|             usbParameters(2<<31, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|             if (usbSerialDriver instanceof ProlificSerialDriver) | ||||
|                 ; | ||||
|             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||
|                 ; | ||||
|             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|                 ; | ||||
|             else | ||||
|                 fail("invalid baudrate 2^31"); | ||||
|         } catch (java.lang.ArithmeticException e) { // ch340 | ||||
|         } catch (java.lang.IllegalArgumentException e) { | ||||
|         } | ||||
| 
 | ||||
|         for(int baudRate : new int[] {2400, 19200, 42000, 115200} ) { | ||||
|             if(baudRate == 42000 && !rfc2217_server_nonstandard_baudrates) | ||||
|                 continue; // rfc2217_server.py would terminate | ||||
|             telnetParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|             usbParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
| 
 | ||||
|             telnetWrite("net2usb".getBytes()); | ||||
|             data = usbRead(7); | ||||
|             assertThat(String.valueOf(baudRate)+"/8N1", data, equalTo("net2usb".getBytes())); | ||||
|             usbWrite("usb2net".getBytes()); | ||||
|             data = telnetRead(7); | ||||
|             assertThat(String.valueOf(baudRate)+"/8N1", data, equalTo("usb2net".getBytes())); | ||||
|         } | ||||
|         { // non matching baud rate | ||||
|             telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|             usbParameters(2400, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
| 
 | ||||
|             telnetWrite("net2usb".getBytes()); | ||||
|             data = usbRead(); | ||||
|             assertNotEquals(7, data.length); | ||||
|             usbWrite("usb2net".getBytes()); | ||||
|             data = telnetRead(); | ||||
|             assertNotEquals(7, data.length); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void dataBits() throws Exception { | ||||
|         byte[] data; | ||||
| 
 | ||||
|         for(int i: new int[] {0, 4, 9}) { | ||||
|             try { | ||||
|                 usbParameters(19200, i, 1, UsbSerialPort.PARITY_NONE); | ||||
|                 if (usbSerialDriver instanceof ProlificSerialDriver) | ||||
|                     ; // todo: add range check in driver | ||||
|                 else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|                     ; // todo: add range check in driver | ||||
|                 else | ||||
|                     fail("invalid databits "+i); | ||||
|             } catch (java.lang.IllegalArgumentException e) { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // telnet -> usb | ||||
|         usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetWrite(new byte[] {0x00}); | ||||
|         Thread.sleep(1); // one bit is 0.05 milliseconds long, wait >> stop bit | ||||
|         telnetWrite(new byte[] {(byte)0xff}); | ||||
|         data = usbRead(2); | ||||
|         assertThat("19200/7N1", data, equalTo(new byte[] {(byte)0x80, (byte)0xff})); | ||||
| 
 | ||||
|         telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetWrite(new byte[] {0x00}); | ||||
|         Thread.sleep(1); | ||||
|         telnetWrite(new byte[] {(byte)0xff}); | ||||
|         data = usbRead(2); | ||||
|         assertThat("19000/6N1", data, equalTo(new byte[] {(byte)0xc0, (byte)0xff})); | ||||
| 
 | ||||
|         telnetParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetWrite(new byte[] {0x00}); | ||||
|         Thread.sleep(1); | ||||
|         telnetWrite(new byte[] {(byte)0xff}); | ||||
|         data = usbRead(2); | ||||
|         assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff})); | ||||
| 
 | ||||
|         // usb -> telnet | ||||
|         telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         usbParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE); | ||||
|         usbWrite(new byte[] {0x00}); | ||||
|         Thread.sleep(1); | ||||
|         usbWrite(new byte[] {(byte)0xff}); | ||||
|         data = telnetRead(2); | ||||
|         assertThat("19000/7N1", data, equalTo(new byte[] {(byte)0x80, (byte)0xff})); | ||||
| 
 | ||||
|         try { | ||||
|             usbParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||
|             usbWrite(new byte[]{0x00}); | ||||
|             Thread.sleep(1); | ||||
|             usbWrite(new byte[]{(byte) 0xff}); | ||||
|             data = telnetRead(2); | ||||
|             assertThat("19000/6N1", data, equalTo(new byte[]{(byte) 0xc0, (byte) 0xff})); | ||||
|         } catch (java.lang.IllegalArgumentException e) { | ||||
|             if (!(usbSerialDriver instanceof FtdiSerialDriver)) | ||||
|                 throw e; | ||||
|         } | ||||
|         try { | ||||
|             usbParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE); | ||||
|             usbWrite(new byte[] {0x00}); | ||||
|             Thread.sleep(1); | ||||
|             usbWrite(new byte[] {(byte)0xff}); | ||||
|             data = telnetRead(2); | ||||
|             assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff})); | ||||
|         } catch (java.lang.IllegalArgumentException e) { | ||||
|             if (!(usbSerialDriver instanceof FtdiSerialDriver)) | ||||
|                 throw e; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void parity() throws Exception { | ||||
|         byte[] _8n1 = {(byte)0x00, (byte)0x01, (byte)0xfe, (byte)0xff}; | ||||
|         byte[] _7n1 = {(byte)0x00, (byte)0x01, (byte)0x7e, (byte)0x7f}; | ||||
|         byte[] _7o1 = {(byte)0x80, (byte)0x01, (byte)0xfe, (byte)0x7f}; | ||||
|         byte[] _7e1 = {(byte)0x00, (byte)0x81, (byte)0x7e, (byte)0xff}; | ||||
|         byte[] _7m1 = {(byte)0x80, (byte)0x81, (byte)0xfe, (byte)0xff}; | ||||
|         byte[] _7s1 = {(byte)0x00, (byte)0x01, (byte)0x7e, (byte)0x7f}; | ||||
|         byte[] data; | ||||
| 
 | ||||
|         for(int i: new int[] {-1, 5}) { | ||||
|             try { | ||||
|                 usbParameters(19200, 8, 1, i); | ||||
|                 fail("invalid parity "+i); | ||||
|             } catch (java.lang.IllegalArgumentException e) { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // usb -> telnet | ||||
|         telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         usbWrite(_8n1); | ||||
|         data = telnetRead(4); | ||||
|         assertThat("19200/8N1", data, equalTo(_8n1)); | ||||
| 
 | ||||
|         usbParameters(19200, 7, 1, UsbSerialPort.PARITY_ODD); | ||||
|         usbWrite(_8n1); | ||||
|         data = telnetRead(4); | ||||
|         assertThat("19200/7O1", data, equalTo(_7o1)); | ||||
| 
 | ||||
|         usbParameters(19200, 7, 1, UsbSerialPort.PARITY_EVEN); | ||||
|         usbWrite(_8n1); | ||||
|         data = telnetRead(4); | ||||
|         assertThat("19200/7E1", data, equalTo(_7e1)); | ||||
| 
 | ||||
|         if (usbSerialDriver instanceof CdcAcmSerialDriver) { | ||||
|             // not supported by arduino_leonardo_bridge.ino, other devices might support it | ||||
|         } else { | ||||
|             usbParameters(19200, 7, 1, UsbSerialPort.PARITY_MARK); | ||||
|             usbWrite(_8n1); | ||||
|             data = telnetRead(4); | ||||
|             assertThat("19200/7M1", data, equalTo(_7m1)); | ||||
| 
 | ||||
|             usbParameters(19200, 7, 1, UsbSerialPort.PARITY_SPACE); | ||||
|             usbWrite(_8n1); | ||||
|             data = telnetRead(4); | ||||
|             assertThat("19200/7S1", data, equalTo(_7s1)); | ||||
|         } | ||||
| 
 | ||||
|         // telnet -> usb | ||||
|         usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetWrite(_8n1); | ||||
|         data = usbRead(4); | ||||
|         assertThat("19200/8N1", data, equalTo(_8n1)); | ||||
| 
 | ||||
|         telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_ODD); | ||||
|         telnetWrite(_8n1); | ||||
|         data = usbRead(4); | ||||
|         assertThat("19200/7O1", data, equalTo(_7o1)); | ||||
| 
 | ||||
|         telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_EVEN); | ||||
|         telnetWrite(_8n1); | ||||
|         data = usbRead(4); | ||||
|         assertThat("19200/7E1", data, equalTo(_7e1)); | ||||
| 
 | ||||
|         if (usbSerialDriver instanceof CdcAcmSerialDriver) { | ||||
|             // not supported by arduino_leonardo_bridge.ino, other devices might support it | ||||
|         } else { | ||||
|             telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_MARK); | ||||
|             telnetWrite(_8n1); | ||||
|             data = usbRead(4); | ||||
|             assertThat("19200/7M1", data, equalTo(_7m1)); | ||||
| 
 | ||||
|             telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_SPACE); | ||||
|             telnetWrite(_8n1); | ||||
|             data = usbRead(4); | ||||
|             assertThat("19200/7S1", data, equalTo(_7s1)); | ||||
| 
 | ||||
|             usbParameters(19200, 7, 1, UsbSerialPort.PARITY_ODD); | ||||
|             telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|             telnetWrite(_8n1); | ||||
|             data = usbRead(4); | ||||
|             assertThat("19200/8N1", data, equalTo(_7n1)); // read is resilient against errors | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void stopBits() throws Exception { | ||||
|         byte[] data; | ||||
| 
 | ||||
|         for (int i : new int[]{0, 4}) { | ||||
|             try { | ||||
|                 usbParameters(19200, 8, i, UsbSerialPort.PARITY_NONE); | ||||
|                 fail("invalid stopbits " + i); | ||||
|             } catch (java.lang.IllegalArgumentException e) { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (usbSerialDriver instanceof CdcAcmSerialDriver) { | ||||
|             // software based bridge in arduino_leonardo_bridge.ino is to slow, other devices might support it | ||||
|         } else { | ||||
|             // shift stopbits into next byte, by using different databits | ||||
|             // a - start bit (0) | ||||
|             // o - stop bit  (1) | ||||
|             // d - data bit | ||||
| 
 | ||||
|             // out 8N2:   addddddd doadddddddoo | ||||
|             //             1000001 0  1 | ||||
|             // in 6N1:    addddddo adddddo | ||||
|             //             100000   101 | ||||
|             usbParameters(19200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); | ||||
|             telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||
|             usbWrite(new byte[]{65, 1}); | ||||
|             data = telnetRead(2); | ||||
|             assertThat("19200/8N1", data, equalTo(new byte[]{1, 5})); | ||||
| 
 | ||||
|             // out 8N2:   addddddd dooadddddddoo | ||||
|             //             1000001 0   1 | ||||
|             // in 6N1:    addddddo addddddo | ||||
|             //             100000   1101 | ||||
|             usbParameters(19200, 8, UsbSerialPort.STOPBITS_2, UsbSerialPort.PARITY_NONE); | ||||
|             telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||
|             usbWrite(new byte[]{65, 1}); | ||||
|             data = telnetRead(2); | ||||
|             assertThat("19200/8N1", data, equalTo(new byte[]{1, 11})); | ||||
|             // todo: could create similar test for 1.5 stopbits, by reading at double speed | ||||
|             //       but only some devices support 1.5 stopbits and it is basically not used any more | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Test | ||||
|     public void probeTable() throws Exception { | ||||
|         class DummyDriver implements UsbSerialDriver { | ||||
|             @Override | ||||
|             public UsbDevice getDevice() { return null; } | ||||
|             @Override | ||||
|             public List<UsbSerialPort> getPorts() { return null; } | ||||
|         } | ||||
|         List<UsbSerialDriver> availableDrivers; | ||||
|         ProbeTable probeTable = new ProbeTable(); | ||||
|         UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); | ||||
|         availableDrivers = new UsbSerialProber(probeTable).findAllDrivers(usbManager); | ||||
|         assertEquals(0, availableDrivers.size()); | ||||
| 
 | ||||
|         probeTable.addProduct(0, 0, DummyDriver.class); | ||||
|         availableDrivers = new UsbSerialProber(probeTable).findAllDrivers(usbManager); | ||||
|         assertEquals(0, availableDrivers.size()); | ||||
| 
 | ||||
|         probeTable.addProduct(usbSerialDriver.getDevice().getVendorId(), usbSerialDriver.getDevice().getProductId(), usbSerialDriver.getClass()); | ||||
|         availableDrivers = new UsbSerialProber(probeTable).findAllDrivers(usbManager); | ||||
|         assertEquals(1, availableDrivers.size()); | ||||
|         assertEquals(true, availableDrivers.get(0).getClass() == usbSerialDriver.getClass()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     // data loss es expected, if data is not consumed fast enough | ||||
|     public void readBuffer() throws Exception { | ||||
|         if(usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|             telnetWriteDelay = 10; // arduino_leonardo_bridge.ino sends each byte in own USB packet, which is horribly slow | ||||
|         usbParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
| 
 | ||||
|         StringBuilder expected = new StringBuilder(); | ||||
|         StringBuilder data = new StringBuilder(); | ||||
|         final int maxWait = 2000; | ||||
|         int bufferSize = 0; | ||||
|         for(bufferSize = 8; bufferSize < (2<<15); bufferSize *= 2) { | ||||
|             int linenr = 0; | ||||
|             String line; | ||||
|             expected.setLength(0); | ||||
|             data.setLength(0); | ||||
| 
 | ||||
|             Log.i(TAG, "bufferSize " + bufferSize); | ||||
|             usbReadBlock = true; | ||||
|             for (linenr = 0; linenr < bufferSize/8; linenr++) { | ||||
|                 line = String.format("%06d\r\n", linenr); | ||||
|                 telnetWrite(line.getBytes()); | ||||
|                 expected.append(line); | ||||
|             } | ||||
|             usbReadBlock = false; | ||||
| 
 | ||||
|             // slowly write new data, until old data is comletely read from buffer and new data is received again | ||||
|             boolean found = false; | ||||
|             for (; linenr < bufferSize/8 + maxWait/10 && !found; linenr++) { | ||||
|                 line = String.format("%06d\r\n", linenr); | ||||
|                 telnetWrite(line.getBytes()); | ||||
|                 Thread.sleep(10); | ||||
|                 expected.append(line); | ||||
|                 data.append(new String(usbRead(0))); | ||||
|                 found = data.toString().endsWith(line); | ||||
|             } | ||||
|             if(!found) { | ||||
|                 // use waiting read to clear input queue, else next test would see unexpected data | ||||
|                 byte[] rest = null; | ||||
|                 while(rest==null || rest.length>0) | ||||
|                     rest = usbRead(-1); | ||||
|                 fail("end not found"); | ||||
|             } | ||||
|             if (data.length() != expected.length()) | ||||
|                 break; | ||||
|         } | ||||
|         int pos = StringUtils.indexOfDifference(data, expected); | ||||
|         Log.i(TAG, "bufferSize " + bufferSize + ", first difference at " + pos); | ||||
|         // actual values have large variance for same device, e.g. | ||||
|         //  bufferSize 4096, first difference at 164 | ||||
|         //  bufferSize 64, first difference at 57 | ||||
|         assertTrue(bufferSize > 16); | ||||
|         assertTrue(data.length() != expected.length()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     // see logcat for performance results | ||||
|     public void readSpeed() throws Exception { | ||||
|         // CDC arduino_leonardo_bridge.ini has transfer speed ~ 100 byte/sec | ||||
|         // all other devices are near physical limit with ~ 10-12k/sec | ||||
| 
 | ||||
|         // CH340 w/o asyncReads (bulkTransfer) is much slower and fails reproducibly here | ||||
|         // FTDI w/o asyncReads (bulkTransfer) does not continue to read after ~2k | ||||
|         // CP2102 and PL2303 do not have data loss issues with bulkTransfer | ||||
|         usbParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
| 
 | ||||
|         // limited write ahead to avoid buffer overrun | ||||
|         // with unlimited write ahead all devices fail sporadically. is it windows/device/usb-buffer overrun? | ||||
|         int writeAhead = 2000; | ||||
|         if(usbSerialDriver instanceof CdcAcmSerialDriver) | ||||
|             writeAhead = 50; | ||||
| 
 | ||||
|         int linenr = 0; | ||||
|         String line=""; | ||||
|         StringBuilder data = new StringBuilder(); | ||||
|         StringBuilder expected = new StringBuilder(); | ||||
|         int dlen = 0, elen = 0; | ||||
|         Log.i(TAG, "readSpeed: 'in' should be near "+115200/10); | ||||
|         long begin = System.currentTimeMillis(); | ||||
|         long next = System.currentTimeMillis(); | ||||
|         for(int seconds=1; seconds<=5; seconds++) { | ||||
|             next += 1000; | ||||
|             while (System.currentTimeMillis() < next) { | ||||
|                 if(expected.length() < data.length() + writeAhead) { | ||||
|                     line = String.format("%06d\r\n", linenr++); | ||||
|                     telnetWrite(line.getBytes()); | ||||
|                     expected.append(line); | ||||
|                 } else { | ||||
|                     Thread.sleep(0, 100000); | ||||
|                 } | ||||
|                 data.append(new String(usbRead(0))); | ||||
|             } | ||||
|             Log.i(TAG, "readSpeed: t="+(next-begin)+", in="+(data.length()-dlen)+", out="+(expected.length()-elen)); | ||||
|             dlen = data.length(); | ||||
|             elen = expected.length(); | ||||
|         } | ||||
|         boolean found = false; | ||||
|         for (linenr=0; linenr < 2000 && !found; linenr++) { | ||||
|             data.append(new String(usbRead(0))); | ||||
|             Thread.sleep(1); | ||||
|             found = data.toString().endsWith(line); | ||||
|         } | ||||
|         next = System.currentTimeMillis(); | ||||
|         //Log.i(TAG, "readSpeed: t="+(next-begin)+", in="+(data.length()-dlen)); | ||||
|         assertTrue(found); | ||||
|         int pos = StringUtils.indexOfDifference(data, expected); | ||||
|         if(pos!=-1) { | ||||
|             Log.i(TAG, "readSpeed: first difference at " + pos); | ||||
|             String datasub     =     data.substring(Math.max(pos - 20, 0), Math.min(pos + 20, data.length())); | ||||
|             String expectedsub = expected.substring(Math.max(pos - 20, 0), Math.min(pos + 20, expected.length())); | ||||
|             assertThat(datasub, equalTo(expectedsub)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     // see logcat for performance results | ||||
|     public void writeSpeed() throws Exception { | ||||
|         // CDC arduino_leonardo_bridge.ini has transfer speed ~ 100 byte/sec | ||||
|         // all other devices can get near physical limit: | ||||
|         // longlines=true:, speed is near physical limit at 11.5k | ||||
|         // longlines=false: speed is 3-4k for all devices, as more USB packets are required | ||||
|         usbParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         telnetParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||
|         boolean longlines = !(usbSerialDriver instanceof CdcAcmSerialDriver); | ||||
| 
 | ||||
|         int linenr = 0; | ||||
|         String line=""; | ||||
|         StringBuilder data = new StringBuilder(); | ||||
|         StringBuilder expected = new StringBuilder(); | ||||
|         int dlen = 0, elen = 0; | ||||
|         Log.i(TAG, "writeSpeed: 'out' should be near "+115200/10); | ||||
|         long begin = System.currentTimeMillis(); | ||||
|         long next = System.currentTimeMillis(); | ||||
|         for(int seconds=1; seconds<=5; seconds++) { | ||||
|             next += 1000; | ||||
|             while (System.currentTimeMillis() < next) { | ||||
|                 if(longlines) | ||||
|                     line = String.format("%060d\r\n", linenr++); | ||||
|                 else | ||||
|                     line = String.format("%06d\r\n", linenr++); | ||||
|                 usbWrite(line.getBytes()); | ||||
|                 expected.append(line); | ||||
|                 data.append(new String(telnetRead(0))); | ||||
|             } | ||||
|             Log.i(TAG, "writeSpeed: t="+(next-begin)+", out="+(expected.length()-elen)+", in="+(data.length()-dlen)); | ||||
|             dlen = data.length(); | ||||
|             elen = expected.length(); | ||||
|         } | ||||
|         boolean found = false; | ||||
|         for (linenr=0; linenr < 2000 && !found; linenr++) { | ||||
|             data.append(new String(telnetRead(0))); | ||||
|             Thread.sleep(1); | ||||
|             found = data.toString().endsWith(line); | ||||
|         } | ||||
|         next = System.currentTimeMillis(); | ||||
|         Log.i(TAG, "writeSpeed: t="+(next-begin)+", in="+(data.length()-dlen)); | ||||
|         assertTrue(found); | ||||
|         int pos = StringUtils.indexOfDifference(data, expected); | ||||
|         if(pos!=-1) { | ||||
|             Log.i(TAG, "writeSpeed: first difference at " + pos); | ||||
|             String datasub     =     data.substring(Math.max(pos - 20, 0), Math.min(pos + 20, data.length())); | ||||
|             String expectedsub = expected.substring(Math.max(pos - 20, 0), Math.min(pos + 20, expected.length())); | ||||
|             assertThat(datasub, equalTo(expectedsub)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user