├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── androidusbserialport ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── nianlun │ │ └── androidusbserialport │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── nianlun │ │ │ └── androidusbserialport │ │ │ ├── AndroidSerialFragment.java │ │ │ ├── MainActivity.java │ │ │ ├── SplashActivity.java │ │ │ ├── UsbSerialPortFragment.java │ │ │ └── adapter │ │ │ ├── SerialPortAdapter.java │ │ │ └── UsbSerialPortAdapter.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── bg_btn_selector.xml │ │ ├── bg_white_stroke.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_serial.png │ │ ├── shape_btn_normal.xml │ │ └── shape_btn_press.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_splash.xml │ │ ├── fragment_android_serial_port.xml │ │ ├── fragment_usb_serial_port.xml │ │ ├── layout_controller_serial_port.xml │ │ ├── layout_device_measure_top.xml │ │ ├── layout_serial_port_list.xml │ │ ├── layout_serial_receive_send_test.xml │ │ ├── recyclerview_serial_port_item.xml │ │ └── recyclerview_usb_serial_port_item.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── nianlun │ └── androidusbserialport │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libserialport ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── libs │ ├── armeabi-v7a │ │ └── libserial_port.so │ ├── armeabi │ │ └── libserial_port.so │ └── x86 │ │ └── libserial_port.so ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── android_serialport_api │ │ ├── SerialPort.java │ │ └── SerialPortFinder.java │ └── com │ │ └── nianlun │ │ └── libserialport │ │ ├── SerialController.java │ │ ├── UsbBroadcastReceiver.java │ │ ├── UsbSerialPortController.java │ │ ├── UsbSerialPortParameters.java │ │ └── usbdriver │ │ ├── CdcAcmSerialDriver.java │ │ ├── Ch34xSerialDriver.java │ │ ├── CommonUsbSerialPort.java │ │ ├── Cp21xxSerialDriver.java │ │ ├── FtdiSerialDriver.java │ │ ├── HexDump.java │ │ ├── ProbeTable.java │ │ ├── ProlificSerialDriver.java │ │ ├── SerialInputOutputManager.java │ │ ├── UsbId.java │ │ ├── UsbSerialDriver.java │ │ ├── UsbSerialPort.java │ │ ├── UsbSerialProber.java │ │ └── UsbSerialRuntimeException.java │ └── res │ └── values │ └── strings.xml ├── settings.gradle └── tool └── serial_port_utility_latest.exe /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidUSBSerialPort 2 | 安卓串口通信 3 | 4 | 博客文章: 5 | 6 | [Android串口通信(一)](https://www.cnblogs.com/jqnl/p/13633800.html) 7 | 8 | [Android串口通信(二)](https://www.cnblogs.com/jqnl/p/13633814.html) 9 | 10 | tool文件夹中附有PC串口调试工具供使用:serial_port_utility_latest.exe 11 | 12 | 参考库: 13 | 14 | 谷歌官方android-serialport-api库:[https://github.com/cepr/android-serialport-api](https://github.com/cepr/android-serialport-api) 15 | 16 | Usb转串口驱动usb-serial-for-android库:[https://github.com/mik3y/usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android) 17 | 18 | Kolin使用参考库:[https://github.com/HelloHuDi/usbSerialPortTools](https://github.com/HelloHuDi/usbSerialPortTools) -------------------------------------------------------------------------------- /androidusbserialport/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /androidusbserialport/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 30 5 | buildToolsVersion "30.0.2" 6 | defaultConfig { 7 | applicationId "com.nianlun.androidusbserialport" 8 | minSdkVersion 19 9 | targetSdkVersion 30 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'androidx.appcompat:appcompat:1.0.2' 25 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 26 | testImplementation 'junit:junit:4.12' 27 | androidTestImplementation 'androidx.test.ext:junit:1.1.0' 28 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 29 | 30 | implementation project(path: ':libserialport') 31 | 32 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 33 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.47-androidx' 34 | implementation 'com.qw:soulpermission:1.3.0' 35 | } 36 | -------------------------------------------------------------------------------- /androidusbserialport/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /androidusbserialport/src/androidTest/java/com/nianlun/androidusbserialport/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.nianlun.androidusbserialport; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.nianlun.androidusbserialport", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/java/com/nianlun/androidusbserialport/AndroidSerialFragment.java: -------------------------------------------------------------------------------- 1 | package com.nianlun.androidusbserialport; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.Button; 8 | import android.widget.CheckBox; 9 | import android.widget.EditText; 10 | import android.widget.LinearLayout; 11 | import android.widget.ScrollView; 12 | import android.widget.Spinner; 13 | import android.widget.TextView; 14 | 15 | import androidx.annotation.NonNull; 16 | import androidx.annotation.Nullable; 17 | import androidx.fragment.app.Fragment; 18 | import androidx.recyclerview.widget.LinearLayoutManager; 19 | import androidx.recyclerview.widget.RecyclerView; 20 | 21 | import com.chad.library.adapter.base.BaseQuickAdapter; 22 | import com.nianlun.androidusbserialport.adapter.SerialPortAdapter; 23 | import com.nianlun.libserialport.SerialController; 24 | import com.nianlun.libserialport.usbdriver.HexDump; 25 | 26 | import java.util.concurrent.ExecutorService; 27 | import java.util.concurrent.Executors; 28 | 29 | public class AndroidSerialFragment extends Fragment implements View.OnClickListener, SerialController.OnSerialListener { 30 | 31 | private ExecutorService execute = Executors.newCachedThreadPool(); 32 | private SerialPortAdapter mSerialPortAdapter; 33 | private SerialController mSerialController; 34 | private String mSerialPortPath; 35 | 36 | private Spinner spBaudRate; 37 | private Spinner spData; 38 | private Spinner spStop; 39 | private LinearLayout usbTitle; 40 | private TextView tvParity; 41 | private Spinner spParity; 42 | private Button btnOpenOrClose; 43 | private RecyclerView rvListUsbSerialPort; 44 | private TextView tvSendDataCount; 45 | private TextView tvReceiveDataCount; 46 | private TextView tvLoseDataCount; 47 | private TextView tvAbnormalDataCount; 48 | private TextView tvResult; 49 | private ScrollView svResult; 50 | private EditText etWriteData; 51 | private CheckBox cbHex; 52 | private CheckBox cbHexRev; 53 | private TextView tvReceiveNumber; 54 | private Button btnClear; 55 | private Button btnSend; 56 | private Button btnStartTest; 57 | private Button btnStopTest; 58 | private SendingThread mTestSendingThread; 59 | 60 | @Nullable 61 | @Override 62 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 63 | View view = inflater.inflate(R.layout.fragment_android_serial_port, container, false); 64 | initView(view); 65 | return view; 66 | } 67 | 68 | @Override 69 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 70 | super.onViewCreated(view, savedInstanceState); 71 | initData(); 72 | initListener(); 73 | } 74 | 75 | private void initListener() { 76 | mSerialController.setOnSerialListener(this); 77 | mSerialPortAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { 78 | @Override 79 | public void onItemClick(BaseQuickAdapter adapter, View view, int position) { 80 | mSerialPortPath = (String) adapter.getItem(position); 81 | mSerialPortAdapter.setSelectedPosition(position); 82 | } 83 | }); 84 | } 85 | 86 | private void initData() { 87 | mSerialController = new SerialController(); 88 | mSerialPortAdapter = new SerialPortAdapter(mSerialController.getAllSerialPortPath()); 89 | rvListUsbSerialPort.setAdapter(mSerialPortAdapter); 90 | } 91 | 92 | private void initView(View view) { 93 | spBaudRate = (Spinner) view.findViewById(R.id.sp_baud_rate); 94 | spData = (Spinner) view.findViewById(R.id.sp_data); 95 | spStop = (Spinner) view.findViewById(R.id.sp_stop); 96 | usbTitle = (LinearLayout) view.findViewById(R.id.usb_title); 97 | usbTitle.setVisibility(View.GONE); 98 | tvParity = (TextView) view.findViewById(R.id.tv_parity); 99 | spParity = (Spinner) view.findViewById(R.id.sp_parity); 100 | btnOpenOrClose = (Button) view.findViewById(R.id.btn_open_or_close); 101 | rvListUsbSerialPort = (RecyclerView) view.findViewById(R.id.rv_list_usb_serial_port); 102 | rvListUsbSerialPort.setLayoutManager(new LinearLayoutManager(getContext())); 103 | tvSendDataCount = (TextView) view.findViewById(R.id.tv_sendDataCount); 104 | tvReceiveDataCount = (TextView) view.findViewById(R.id.tv_receiveDataCount); 105 | tvLoseDataCount = (TextView) view.findViewById(R.id.tv_loseDataCount); 106 | tvAbnormalDataCount = (TextView) view.findViewById(R.id.tv_abnormalDataCount); 107 | tvResult = (TextView) view.findViewById(R.id.tv_result); 108 | svResult = (ScrollView) view.findViewById(R.id.sv_result); 109 | etWriteData = (EditText) view.findViewById(R.id.et_write_data); 110 | cbHex = (CheckBox) view.findViewById(R.id.cb_hex); 111 | cbHexRev = (CheckBox) view.findViewById(R.id.cb_hex_rev); 112 | tvReceiveNumber = (TextView) view.findViewById(R.id.tv_receive_number); 113 | btnClear = (Button) view.findViewById(R.id.btn_clear); 114 | btnSend = (Button) view.findViewById(R.id.btn_send); 115 | btnStartTest = (Button) view.findViewById(R.id.btn_start_test); 116 | btnStopTest = (Button) view.findViewById(R.id.btn_stop_test); 117 | 118 | btnOpenOrClose.setOnClickListener(this); 119 | btnClear.setOnClickListener(this); 120 | btnSend.setOnClickListener(this); 121 | btnStartTest.setOnClickListener(this); 122 | btnStopTest.setOnClickListener(this); 123 | } 124 | 125 | @Override 126 | public void onClick(View v) { 127 | switch (v.getId()) { 128 | case R.id.btn_open_or_close: 129 | if (mSerialController.isOpened()) { 130 | mSerialController.closeSerialPort(); 131 | btnOpenOrClose.setText(R.string.open_port); 132 | } else { 133 | mSerialController.openSerialPort(mSerialPortPath, 134 | Integer.parseInt(spBaudRate.getSelectedItem().toString()), 135 | Integer.parseInt(spParity.getSelectedItem().toString())); 136 | btnOpenOrClose.setText(R.string.close_port); 137 | } 138 | break; 139 | case R.id.btn_clear: 140 | tvResult.setText(""); 141 | break; 142 | case R.id.btn_send: 143 | String data = etWriteData.getText().toString().trim(); 144 | byte[] bytes; 145 | if (cbHex.isChecked()) { 146 | try { 147 | bytes = HexDump.hexStringToByteArray(data); 148 | } catch (Exception e) { 149 | showData(getResources().getString(R.string.is_not_hex,data)); 150 | return; 151 | } 152 | } else { 153 | bytes = data.getBytes(); 154 | } 155 | mSerialController.sendSerialPort(bytes); 156 | break; 157 | case R.id.btn_start_test: 158 | mIsTesting = true; 159 | resetTest(); 160 | if (mSerialController.isOpened()) { 161 | mTestSendingThread = new SendingThread(); 162 | if (!execute.isShutdown()) { 163 | execute.execute(mTestSendingThread); 164 | } 165 | } 166 | break; 167 | case R.id.btn_stop_test: 168 | mIsTesting = false; 169 | mTestSendingThread.interrupt(); 170 | break; 171 | default: 172 | break; 173 | } 174 | } 175 | 176 | @Override 177 | public void onReceivedData(byte[] data, int size) { 178 | if (mIsTesting) { 179 | synchronized (mByteReceivedBackSemaphore) { 180 | int i; 181 | for (i = 0; i < size; i++) { 182 | if (data[i] == mValueToSend && (!mByteReceivedBack)) { 183 | mValueToSend++; 184 | mByteReceivedBack = true; 185 | mByteReceivedBackSemaphore.notify(); 186 | } else { 187 | mCorrupted++; 188 | } 189 | } 190 | } 191 | } else { 192 | String receivedData = ""; 193 | if (cbHexRev.isChecked()) { 194 | try { 195 | receivedData = HexDump.dumpHexString(data, 0, size); 196 | } catch (Exception e) { 197 | showData(getResources().getString(R.string.is_not_hex)); 198 | } 199 | } else { 200 | receivedData = new String(data, 0, size); 201 | } 202 | showData(receivedData); 203 | } 204 | } 205 | 206 | @Override 207 | public void onSerialOpenSuccess() { 208 | showData(getResources().getString(R.string.open_port_success)); 209 | } 210 | 211 | @Override 212 | public void onSerialOpenException(Exception e) { 213 | showData(getResources().getString(R.string.open_port_fail) + ":" + e.getMessage()); 214 | } 215 | 216 | private void showData(String msg) { 217 | tvResult.append(msg + "\n"); 218 | } 219 | 220 | private boolean mIsTesting; 221 | private byte mValueToSend; 222 | private boolean mByteReceivedBack; 223 | private final Object mByteReceivedBackSemaphore = new Object(); 224 | private int mIncoming = 0; 225 | private int mOutgoing = 0; 226 | private int mLost = 0; 227 | private int mCorrupted = 0; 228 | 229 | private class SendingThread extends Thread { 230 | @Override 231 | public void run() { 232 | while (mIsTesting) { 233 | synchronized (mByteReceivedBackSemaphore) { 234 | mByteReceivedBack = false; 235 | mSerialController.sendSerialPort(new byte[]{mValueToSend}); 236 | mOutgoing++; 237 | try { 238 | mByteReceivedBackSemaphore.wait(100); 239 | if (mByteReceivedBack) { 240 | mIncoming++; 241 | } else { 242 | mLost++; 243 | } 244 | getActivity().runOnUiThread(new Runnable() { 245 | @Override 246 | public void run() { 247 | setTestData(mOutgoing, mLost, mIncoming, mCorrupted); 248 | } 249 | }); 250 | } catch (Exception e) { 251 | e.printStackTrace(); 252 | } 253 | } 254 | } 255 | } 256 | } 257 | 258 | private void setTestData(int mOutgoing, int mLost, int mIncoming, int mCorrupted) { 259 | tvSendDataCount.setText(String.valueOf(mOutgoing)); 260 | tvLoseDataCount.setText(String.valueOf(mLost)); 261 | tvReceiveDataCount.setText(String.valueOf(mIncoming)); 262 | tvAbnormalDataCount.setText(String.valueOf(mCorrupted)); 263 | } 264 | 265 | private void resetTest() { 266 | mIncoming = 0; 267 | mOutgoing = 0; 268 | mLost = 0; 269 | mCorrupted = 0; 270 | setTestData(0, 0, 0, 0); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/java/com/nianlun/androidusbserialport/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.nianlun.androidusbserialport; 2 | 3 | import android.os.Bundle; 4 | import android.view.Menu; 5 | import android.view.MenuItem; 6 | import android.widget.Toast; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.appcompat.widget.Toolbar; 11 | import androidx.fragment.app.Fragment; 12 | 13 | public class MainActivity extends AppCompatActivity { 14 | 15 | private Toolbar toolbar; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | toolbar = findViewById(R.id.tool_bar_main); 22 | setSupportActionBar(toolbar); 23 | getSupportFragmentManager().beginTransaction().replace(R.id.fl_content_main, new AndroidSerialFragment()).commit(); 24 | } 25 | 26 | @Override 27 | public boolean onCreateOptionsMenu(Menu menu) { 28 | getMenuInflater().inflate(R.menu.menu_main, menu); 29 | return true; 30 | } 31 | 32 | @Override 33 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 34 | Fragment fragment = null; 35 | switch (item.getItemId()) { 36 | case R.id.action_setting1: 37 | fragment = new AndroidSerialFragment(); 38 | toolbar.setTitle(R.string.android_serial_port); 39 | break; 40 | case R.id.action_setting2: 41 | fragment = new UsbSerialPortFragment(); 42 | toolbar.setTitle(R.string.usb_serial_port); 43 | break; 44 | default: 45 | break; 46 | } 47 | if (fragment != null) { 48 | getSupportFragmentManager().beginTransaction().replace(R.id.fl_content_main, fragment).commit(); 49 | } 50 | return true; 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/java/com/nianlun/androidusbserialport/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.nianlun.androidusbserialport; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.Manifest; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.os.CountDownTimer; 9 | import android.view.Window; 10 | 11 | import com.qw.soul.permission.SoulPermission; 12 | import com.qw.soul.permission.bean.Permission; 13 | import com.qw.soul.permission.callbcak.CheckRequestPermissionListener; 14 | 15 | public class SplashActivity extends AppCompatActivity { 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_splash); 21 | requestPermission(); 22 | } 23 | 24 | private void requestPermission() { 25 | SoulPermission.getInstance().checkAndRequestPermission( 26 | Manifest.permission.WRITE_EXTERNAL_STORAGE, new CheckRequestPermissionListener() { 27 | @Override 28 | public void onPermissionOk(Permission permission) { 29 | CountDownTimer countDownTimer = new CountDownTimer(3000, 1000) { 30 | @Override 31 | public void onTick(long l) { 32 | 33 | } 34 | 35 | @Override 36 | public void onFinish() { 37 | Intent intent = new Intent(SplashActivity.this, MainActivity.class); 38 | startActivity(intent); 39 | finish(); 40 | } 41 | }; 42 | countDownTimer.start(); 43 | } 44 | 45 | @Override 46 | public void onPermissionDenied(Permission permission) { 47 | 48 | } 49 | } 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/java/com/nianlun/androidusbserialport/UsbSerialPortFragment.java: -------------------------------------------------------------------------------- 1 | package com.nianlun.androidusbserialport; 2 | 3 | import android.hardware.usb.UsbDevice; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.Button; 9 | import android.widget.CheckBox; 10 | import android.widget.EditText; 11 | import android.widget.Spinner; 12 | import android.widget.TextView; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.fragment.app.Fragment; 17 | import androidx.recyclerview.widget.LinearLayoutManager; 18 | import androidx.recyclerview.widget.RecyclerView; 19 | 20 | import com.chad.library.adapter.base.BaseQuickAdapter; 21 | import com.nianlun.androidusbserialport.adapter.UsbSerialPortAdapter; 22 | import com.nianlun.libserialport.UsbSerialPortController; 23 | import com.nianlun.libserialport.UsbSerialPortParameters; 24 | import com.nianlun.libserialport.usbdriver.HexDump; 25 | import com.nianlun.libserialport.usbdriver.UsbSerialPort; 26 | 27 | import java.util.List; 28 | import java.util.Locale; 29 | 30 | public class UsbSerialPortFragment extends Fragment implements UsbSerialPortController.OnUsbSerialListener, View.OnClickListener { 31 | 32 | private UsbSerialPort mUsbSerialPort; 33 | 34 | private UsbSerialPortAdapter mUsbSerialPortAdapter; 35 | private UsbSerialPortController mUsbSerialPortController; 36 | 37 | private RecyclerView rvListUsbSerialPort; 38 | private Spinner spBaudRate; 39 | private Spinner spData; 40 | private Spinner spStop; 41 | private Spinner spParity; 42 | private Button btnOpenOrClose; 43 | private TextView tvResult; 44 | private EditText etWriteData; 45 | private CheckBox cbHex; 46 | private CheckBox cbHexRev; 47 | private TextView tvReceiveNumber; 48 | private Button btnClear; 49 | private Button btnSend; 50 | private List mUsbSerialPortList; 51 | 52 | 53 | @Nullable 54 | @Override 55 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 56 | View view = inflater.inflate(R.layout.fragment_usb_serial_port, container, false); 57 | initView(view); 58 | return view; 59 | } 60 | 61 | @Override 62 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 63 | super.onViewCreated(view, savedInstanceState); 64 | initData(); 65 | initListener(); 66 | } 67 | 68 | private void initListener() { 69 | mUsbSerialPortAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { 70 | @Override 71 | public void onItemClick(BaseQuickAdapter adapter, View view, int position) { 72 | mUsbSerialPortAdapter.setSelectedPosition(position); 73 | mUsbSerialPort = (UsbSerialPort) adapter.getItem(position); 74 | } 75 | }); 76 | } 77 | 78 | private void initData() { 79 | mUsbSerialPortController = new UsbSerialPortController(getContext()); 80 | mUsbSerialPortController.setUsbSerialConnectListener(this); 81 | mUsbSerialPortList = mUsbSerialPortController.getAllSerialPort(); 82 | mUsbSerialPortAdapter = new UsbSerialPortAdapter(mUsbSerialPortList); 83 | rvListUsbSerialPort.setAdapter(mUsbSerialPortAdapter); 84 | } 85 | 86 | private void initView(View view) { 87 | rvListUsbSerialPort = (RecyclerView) view.findViewById(R.id.rv_list_usb_serial_port); 88 | rvListUsbSerialPort.setLayoutManager(new LinearLayoutManager(getContext())); 89 | spBaudRate = (Spinner) view.findViewById(R.id.sp_baud_rate); 90 | spData = (Spinner) view.findViewById(R.id.sp_data); 91 | spStop = (Spinner) view.findViewById(R.id.sp_stop); 92 | spParity = (Spinner) view.findViewById(R.id.sp_parity); 93 | btnOpenOrClose = (Button) view.findViewById(R.id.btn_open_or_close); 94 | btnOpenOrClose.setOnClickListener(this); 95 | tvResult = (TextView) view.findViewById(R.id.tv_result); 96 | etWriteData = (EditText) view.findViewById(R.id.et_write_data); 97 | cbHex = (CheckBox) view.findViewById(R.id.cb_hex); 98 | cbHexRev = (CheckBox) view.findViewById(R.id.cb_hex_rev); 99 | tvReceiveNumber = (TextView) view.findViewById(R.id.tv_receive_number); 100 | btnClear = (Button) view.findViewById(R.id.btn_clear); 101 | btnClear.setOnClickListener(this); 102 | btnSend = (Button) view.findViewById(R.id.btn_send); 103 | btnSend.setOnClickListener(this); 104 | } 105 | 106 | @Override 107 | public void onUsbDeviceStateChange(int usbDeviceState, UsbDevice usbDevice) { 108 | mUsbSerialPortList = mUsbSerialPortController.getAllSerialPort(); 109 | mUsbSerialPortAdapter.setNewData(mUsbSerialPortList); 110 | mUsbSerialPortAdapter.setSelectedPosition(-1); 111 | } 112 | 113 | @Override 114 | public void onSerialOpenSuccess() { 115 | showData(getResources().getString(R.string.open_port_success)); 116 | } 117 | 118 | @Override 119 | public void onSerialOpenException(Exception e) { 120 | showData(getResources().getString(R.string.open_port_fail)); 121 | } 122 | 123 | @Override 124 | public void onReceivedData(byte[] data) { 125 | String receivedData; 126 | if (cbHexRev.isChecked()) { 127 | receivedData = HexDump.toHexString(data); 128 | } else { 129 | StringBuilder stringBuilder = new StringBuilder(); 130 | for (byte b : data) { 131 | stringBuilder.append((int) b).append(" "); 132 | } 133 | receivedData = stringBuilder.toString(); 134 | } 135 | showData(receivedData); 136 | tvReceiveNumber.setText(String.format(Locale.US, "%d", data.length)); 137 | } 138 | 139 | @Override 140 | public void onSendException(Exception e) { 141 | showData(getResources().getString(R.string.serial_write_fail)); 142 | } 143 | 144 | @Override 145 | public void onClick(View v) { 146 | switch (v.getId()) { 147 | case R.id.btn_open_or_close: 148 | if (mUsbSerialPortController.isOpened()) { 149 | mUsbSerialPortController.closeSerialPort(); 150 | btnOpenOrClose.setText(R.string.open_port); 151 | } else { 152 | mUsbSerialPortController.openSerialPort(mUsbSerialPort, new UsbSerialPortParameters( 153 | Integer.parseInt(spBaudRate.getSelectedItem().toString()), 154 | Integer.parseInt(spData.getSelectedItem().toString()), 155 | Integer.parseInt(spStop.getSelectedItem().toString()), 156 | Integer.parseInt(spParity.getSelectedItem().toString()))); 157 | btnOpenOrClose.setText(R.string.close_port); 158 | } 159 | break; 160 | case R.id.btn_clear: 161 | tvResult.setText(""); 162 | break; 163 | case R.id.btn_send: 164 | String command = etWriteData.getText().toString().trim(); 165 | boolean isWrite; 166 | if (cbHex.isChecked()) { 167 | isWrite = sendHexData(command); 168 | } else { 169 | isWrite = mUsbSerialPortController.sendSerialPort(command.getBytes()); 170 | } 171 | if (isWrite) { 172 | showData(getResources().getString(R.string.serial_write_success)); 173 | } else { 174 | showData(getResources().getString(R.string.serial_write_fail)); 175 | } 176 | break; 177 | } 178 | } 179 | 180 | private boolean sendHexData(String command) { 181 | // String[] hexs = command.split(" "); 182 | // byte[] bytes = new byte[hexs.length]; 183 | // boolean okflag = true; 184 | // int i = 0; 185 | // for (String hex : hexs) { 186 | // try { 187 | // int d = Integer.parseInt(hex, 16); 188 | // if (d > 255) { 189 | // showData(String.format(getResources().getString(R.string.greater_than_ff), hex)); 190 | // okflag = false; 191 | // } else { 192 | // bytes[i] = (byte) d; 193 | // } 194 | // } catch (NumberFormatException e) { 195 | // showData(String.format(getResources().getString(R.string.is_not_hex), hex)); 196 | // e.printStackTrace(); 197 | // okflag = false; 198 | // } 199 | // i++; 200 | // } 201 | command = command.replace(" ", ""); 202 | byte[] bytes; 203 | try { 204 | bytes = HexDump.hexStringToByteArray(command); 205 | } catch (Exception e) { 206 | showData(getResources().getString(R.string.is_not_hex, command)); 207 | return false; 208 | } 209 | if (bytes.length > 0) { 210 | return mUsbSerialPortController.sendSerialPort(bytes); 211 | } 212 | return false; 213 | } 214 | 215 | private void showData(final String str) { 216 | getActivity().runOnUiThread(new Runnable() { 217 | @Override 218 | public void run() { 219 | tvResult.append(str + "\n"); 220 | } 221 | }); 222 | 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/java/com/nianlun/androidusbserialport/adapter/SerialPortAdapter.java: -------------------------------------------------------------------------------- 1 | package com.nianlun.androidusbserialport.adapter; 2 | 3 | import android.graphics.Color; 4 | import android.os.Build; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.chad.library.adapter.base.BaseQuickAdapter; 9 | import com.chad.library.adapter.base.BaseViewHolder; 10 | import com.nianlun.androidusbserialport.R; 11 | import com.nianlun.libserialport.usbdriver.UsbSerialPort; 12 | 13 | import java.util.List; 14 | 15 | import android_serialport_api.SerialPort; 16 | import android_serialport_api.SerialPortFinder; 17 | 18 | public class SerialPortAdapter extends BaseQuickAdapter { 19 | 20 | private int mSelectedPosition = -1; 21 | 22 | public SerialPortAdapter(List data) { 23 | super(R.layout.recyclerview_serial_port_item, data); 24 | } 25 | 26 | @Override 27 | protected void convert(@NonNull BaseViewHolder helper, String item) { 28 | helper.setText(R.id.tv_name_serial_port_item, item); 29 | if (mSelectedPosition == helper.getAdapterPosition()) { 30 | helper.itemView.setBackgroundColor(Color.GREEN); 31 | }else{ 32 | helper.itemView.setBackgroundColor(Color.TRANSPARENT); 33 | } 34 | } 35 | 36 | public void setSelectedPosition(int position) { 37 | this.mSelectedPosition = position; 38 | notifyDataSetChanged(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/java/com/nianlun/androidusbserialport/adapter/UsbSerialPortAdapter.java: -------------------------------------------------------------------------------- 1 | package com.nianlun.androidusbserialport.adapter; 2 | 3 | import android.graphics.Color; 4 | import android.os.Build; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.RequiresApi; 8 | 9 | import com.chad.library.adapter.base.BaseQuickAdapter; 10 | import com.chad.library.adapter.base.BaseViewHolder; 11 | import com.nianlun.androidusbserialport.R; 12 | import com.nianlun.libserialport.usbdriver.UsbSerialPort; 13 | 14 | import java.util.List; 15 | 16 | public class UsbSerialPortAdapter extends BaseQuickAdapter { 17 | 18 | private int mSelectedPosition = -1; 19 | 20 | public UsbSerialPortAdapter(List data) { 21 | super(R.layout.recyclerview_usb_serial_port_item, data); 22 | } 23 | 24 | @Override 25 | protected void convert(@NonNull BaseViewHolder helper, UsbSerialPort item) { 26 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 27 | helper.setText(R.id.tv_manufacturer_usb_serial_port_item, item.getDriver().getDevice().getManufacturerName()); 28 | } 29 | helper.setText(R.id.tv_vendor_usb_serial_port_item, "vendorId:" + item.getDriver().getDevice().getVendorId()); 30 | helper.setText(R.id.tv_product_usb_serial_port_item, "productId:" + item.getDriver().getDevice().getProductId() + ""); 31 | helper.setText(R.id.tv_name_usb_serial_port_item, "name:" + item.getDriver().getDevice().getDeviceName()); 32 | 33 | if (mSelectedPosition == helper.getAdapterPosition()) { 34 | helper.itemView.setBackgroundColor(Color.GREEN); 35 | } else { 36 | helper.itemView.setBackgroundColor(Color.TRANSPARENT); 37 | } 38 | } 39 | 40 | public void setSelectedPosition(int position) { 41 | this.mSelectedPosition = position; 42 | notifyDataSetChanged(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/drawable/bg_btn_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/drawable/bg_white_stroke.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/drawable/ic_serial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MickJson/AndroidUSBSerialPort/57bd67beacbabf98c603365bdb628bb2d169fd6d/androidusbserialport/src/main/res/drawable/ic_serial.png -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/drawable/shape_btn_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/drawable/shape_btn_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 32 | 33 | 37 | 38 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/layout/fragment_android_serial_port.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 19 | 20 | 25 | 26 | 32 | 33 | 39 | 40 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/layout/fragment_usb_serial_port.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 19 | 20 | 25 | 26 | 32 | 33 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /androidusbserialport/src/main/res/layout/layout_controller_serial_port.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 22 | 23 | 24 | 31 | 32 | 38 | 39 | 43 | 44 | 49 | 50 | 54 | 55 |