├── .gitignore ├── LICENSE ├── README.md ├── README_EN.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── ex │ │ └── serialport │ │ ├── MainActivity.java │ │ └── adapter │ │ ├── LogListAdapter.java │ │ └── SpAdapter.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_background.xml │ ├── drawable │ ├── ic_cleaning.xml │ └── ic_launcher_foreground.xml │ ├── layout-land │ └── activity_main.xml │ ├── layout │ ├── activity_main.xml │ └── item_layout.xml │ ├── menu │ └── main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── values-zh-rCN │ └── strings.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── art ├── compile_env.png ├── logo.svg ├── screen.jpg └── screen.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── serial_port_utility_latest.exe ├── serialport ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro ├── project.properties └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── android_serialport_api │ │ ├── SerialPort.java │ │ └── SerialPortFinder.java │ └── tp │ │ └── xmaihh │ │ └── serialport │ │ ├── SerialHelper.java │ │ ├── bean │ │ └── ComBean.java │ │ ├── stick │ │ ├── AbsStickPackageHelper.java │ │ ├── BaseStickPackageHelper.java │ │ ├── SpecifiedStickPackageHelper.java │ │ ├── StaticLenStickPackageHelper.java │ │ └── VariableLenStickPackageHelper.java │ │ └── utils │ │ └── ByteUtil.java │ ├── jni │ ├── SerialPort.c │ └── SerialPort.h │ └── res │ └── values │ └── strings.xml ├── settings.gradle └── versions.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/androidstudio,c,cmake,c++,gradle 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,c,cmake,c++,gradle 3 | 4 | ### C ### 5 | # Prerequisites 6 | *.d 7 | 8 | # Object files 9 | *.o 10 | *.ko 11 | *.obj 12 | *.elf 13 | 14 | # Linker output 15 | *.ilk 16 | *.map 17 | *.exp 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Libraries 24 | *.lib 25 | *.a 26 | *.la 27 | *.lo 28 | 29 | # Shared objects (inc. Windows DLLs) 30 | *.dll 31 | *.so 32 | *.so.* 33 | *.dylib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | *.i*86 40 | *.x86_64 41 | *.hex 42 | 43 | # Debug files 44 | *.dSYM/ 45 | *.su 46 | *.idb 47 | *.pdb 48 | 49 | # Kernel Module Compile Results 50 | *.mod* 51 | *.cmd 52 | .tmp_versions/ 53 | modules.order 54 | Module.symvers 55 | Mkfile.old 56 | dkms.conf 57 | 58 | ### C++ ### 59 | # Prerequisites 60 | 61 | # Compiled Object files 62 | *.slo 63 | 64 | # Precompiled Headers 65 | 66 | # Compiled Dynamic libraries 67 | 68 | # Fortran module files 69 | *.mod 70 | *.smod 71 | 72 | # Compiled Static libraries 73 | *.lai 74 | 75 | # Executables 76 | 77 | ### CMake ### 78 | CMakeLists.txt.user 79 | CMakeCache.txt 80 | CMakeFiles 81 | CMakeScripts 82 | Testing 83 | Makefile 84 | cmake_install.cmake 85 | install_manifest.txt 86 | compile_commands.json 87 | CTestTestfile.cmake 88 | _deps 89 | 90 | ### CMake Patch ### 91 | # External projects 92 | *-prefix/ 93 | 94 | ### Gradle ### 95 | .gradle 96 | **/build/ 97 | !src/**/build/ 98 | 99 | # Ignore Gradle GUI config 100 | gradle-app.setting 101 | 102 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 103 | !gradle-wrapper.jar 104 | 105 | # Avoid ignore Gradle wrappper properties 106 | !gradle-wrapper.properties 107 | 108 | # Cache of project 109 | .gradletasknamecache 110 | 111 | # Eclipse Gradle plugin generated files 112 | # Eclipse Core 113 | .project 114 | # JDT-specific (Eclipse Java Development Tools) 115 | .classpath 116 | 117 | ### Gradle Patch ### 118 | # Java heap dump 119 | *.hprof 120 | 121 | ### AndroidStudio ### 122 | # Covers files to be ignored for android development using Android Studio. 123 | 124 | # Built application files 125 | *.apk 126 | *.ap_ 127 | *.aab 128 | 129 | # Files for the ART/Dalvik VM 130 | *.dex 131 | 132 | # Java class files 133 | *.class 134 | 135 | # Generated files 136 | bin/ 137 | gen/ 138 | out/ 139 | 140 | # Gradle files 141 | .gradle/ 142 | build/ 143 | 144 | # Signing files 145 | .signing/ 146 | 147 | # Local configuration file (sdk path, etc) 148 | local.properties 149 | 150 | # Proguard folder generated by Eclipse 151 | proguard/ 152 | 153 | # Log Files 154 | *.log 155 | 156 | # Android Studio 157 | .idea/ 158 | /*/build/ 159 | /*/local.properties 160 | /*/out 161 | /*/*/build 162 | /*/*/production 163 | captures/ 164 | .navigation/ 165 | *.ipr 166 | *~ 167 | *.swp 168 | 169 | # Keystore files 170 | *.jks 171 | *.keystore 172 | 173 | # Google Services (e.g. APIs or Firebase) 174 | # google-services.json 175 | 176 | # Android Patch 177 | gen-external-apklibs 178 | 179 | # External native build folder generated in Android Studio 2.2 and later 180 | .externalNativeBuild 181 | 182 | # NDK 183 | obj/ 184 | 185 | # IntelliJ IDEA 186 | *.iml 187 | *.iws 188 | /out/ 189 | 190 | # User-specific configurations 191 | .idea/caches/ 192 | .idea/libraries/ 193 | .idea/shelf/ 194 | .idea/workspace.xml 195 | .idea/tasks.xml 196 | .idea/.name 197 | .idea/compiler.xml 198 | .idea/copyright/profiles_settings.xml 199 | .idea/encodings.xml 200 | .idea/misc.xml 201 | .idea/modules.xml 202 | .idea/scopes/scope_settings.xml 203 | .idea/dictionaries 204 | .idea/vcs.xml 205 | .idea/jsLibraryMappings.xml 206 | .idea/datasources.xml 207 | .idea/dataSources.ids 208 | .idea/sqlDataSources.xml 209 | .idea/dynamic.xml 210 | .idea/uiDesigner.xml 211 | .idea/assetWizardSettings.xml 212 | .idea/gradle.xml 213 | .idea/jarRepositories.xml 214 | .idea/navEditor.xml 215 | .idea/ 216 | publish-mavencentral.gradle 217 | 218 | # Legacy Eclipse project files 219 | .cproject 220 | .settings/ 221 | 222 | # Mobile Tools for Java (J2ME) 223 | .mtj.tmp/ 224 | 225 | # Package Files # 226 | *.war 227 | *.ear 228 | 229 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 230 | hs_err_pid* 231 | 232 | ## Plugin-specific files: 233 | 234 | # mpeltonen/sbt-idea plugin 235 | .idea_modules/ 236 | 237 | # JIRA plugin 238 | atlassian-ide-plugin.xml 239 | 240 | # Mongo Explorer plugin 241 | .idea/mongoSettings.xml 242 | 243 | # Crashlytics plugin (for Android Studio and IntelliJ) 244 | com_crashlytics_export_strings.xml 245 | crashlytics.properties 246 | crashlytics-build.properties 247 | fabric.properties 248 | 249 | ### AndroidStudio Patch ### 250 | 251 | !/gradle/wrapper/gradle-wrapper.jar 252 | 253 | # End of https://www.toptal.com/developers/gitignore/api/androidstudio,c,cmake,c++,gradle -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 xmaihh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Android-Serialport 3 | 移植谷歌官方串口库[android-serialport-api](https://code.google.com/archive/p/android-serialport-api/),仅支持串口名称及波特率,该项目添加支持校验位、数据位、停止位、流控配置项 4 | 5 | 6 | Android-Serialport 7 | 8 | [![GitHub forks](https://img.shields.io/github/forks/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport/network)[![GitHub issues](https://img.shields.io/github/issues/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport/issues)[![GitHub stars](https://img.shields.io/github/stars/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport/stargazers)[![Source persent](https://img.shields.io/badge/Java-73.2%25-brightgreen.svg)](https://github.com/xmaihh/Android-Serialport/search?l=C)[![Jcenter2.1](https://img.shields.io/badge/jcenter-2.1-brightgreen.svg)](https://bintray.com/xmaihh/maven/serialport)![Maven Central](https://img.shields.io/maven-central/v/io.github.xmaihh/serialport) 9 | [![GitHub license](https://img.shields.io/github/license/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport) 10 | 11 | # 文档 12 | 13 |

14 | 中文 15 | | English 16 |

17 | 18 | # 使用依赖 19 | 1. 在项目根目录的`build.gradle`文件中添加: 20 | ``` 21 | allprojects { 22 | repositories { 23 | ... 24 | mavenCentral() 25 | } 26 | } 27 | ``` 28 | 2. 在项目Module下的`build.gradle`文件中添加: 29 | ``` 30 | dependencies { 31 | implementation 'io.github.xmaihh:serialport:2.1.1' 32 | } 33 | ``` 34 | # 属性支持 35 | | 属性 | 参数 | 36 | | --- |:------------------------------------------------------------------------------------------------------------------------------------:| 37 | |波特率 | [BAUDRATE](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/android_serialport_api/SerialPort.java) | 38 | |数据位 | 5,6,7,8 ;默认值8 | 39 | |校验位 | 无奇偶校验(NONE), 奇校验(ODD), 偶校验(EVEN), 0校验(SPACE), 1校验(MARK); 默认无奇偶校验 | 40 | | 停止位| 1,2 ;默认值1 | 41 | |流控 | 不使用流控(NONE), 硬件流控(RTS/CTS), 软件流控(XON/XOFF); 默认不使用流控 | 42 | # 代码功能 43 | ## 1.列出串口列表 44 | ``` 45 | serialPortFinder.getAllDevicesPath(); 46 | ``` 47 | ## 2.串口属性设置 48 | ``` 49 | serialHelper.setPort(String sPort); //设置串口 50 | serialHelper.setBaudRate(int iBaud); //设置波特率 51 | serialHelper.setStopBits(int stopBits); //设置停止位 52 | serialHelper.setDataBits(int dataBits); //设置数据位 53 | serialHelper.setParity(int parity); //设置校验位 54 | serialHelper.setFlowCon(int flowcon); //设置流控 55 | ``` 56 | [![](https://img.shields.io/badge/warning-%09%20admonition-yellow.svg)](https://github.com/xmaihh/Android-Serialport) 57 | 58 | 串口属性设置需在执行`open()`函数之前才能设置生效 59 | ## 3.打开串口 60 | ``` 61 | serialHelper.open(); 62 | ``` 63 | ## 4.关闭串口 64 | ``` 65 | serialHelper.close(); 66 | ``` 67 | ## 5.发送 68 | ``` 69 | serialHelper.send(byte[] bOutArray); // 发送byte[] 70 | serialHelper.sendHex(String sHex); // 发送Hex 71 | serialHelper.sendTxt(String sTxt); // 发送ASCII 72 | ``` 73 | ## 6.接收 74 | ``` 75 | @Override 76 | protected void onDataReceived(final ComBean comBean) { 77 | Toast.makeText(getBaseContext(), new String(comBean.bRec, "UTF-8"), Toast.LENGTH_SHORT).show(); 78 | } 79 | ``` 80 | ## 7.粘包处理 81 | 支持粘包处理,原因见[issues#1](https://github.com/xmaihh/Android-Serialport/issues/1),提供的粘包处理有 82 | 1. [不处理](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/tp/xmaihh/serialport/stick/BaseStickPackageHelper.java)(默认) 83 | 2. [首尾特殊字符处理](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/tp/xmaihh/serialport/stick/SpecifiedStickPackageHelper.java) 84 | 3. [固定长度处理](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/tp/xmaihh/serialport/stick/StaticLenStickPackageHelper.java) 85 | 4. [动态长度处理](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/tp/xmaihh/serialport/stick/VariableLenStickPackageHelper.java) 86 | 支持自定义粘包处理,第一步实现[AbsStickPackageHelper](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/tp/xmaihh/serialport/stick/AbsStickPackageHelper.java)接口 87 | ``` 88 | /** 89 | * 接受消息,粘包处理的helper,通过inputstream,返回最终的数据,需手动处理粘包,返回的byte[]是我们预期的完整数据 90 | * note:这个方法会反复调用,直到解析到一条完整的数据。该方法是同步的,尽量不要做耗时操作,否则会阻塞读取数据 91 | */ 92 | public interface AbsStickPackageHelper { 93 | byte[] execute(InputStream is); 94 | } 95 | ``` 96 | 设置粘包处理 97 | ``` 98 | serialHelper.setStickPackageHelper(AbsStickPackageHelper mStickPackageHelper); 99 | ``` 100 | * 其实数据粘包可参考socket通讯的粘包处理,例如此处粘包处理方法出自于[XAndroidSocket](https://github.com/Blankeer/XAndroidSocket) 101 | 102 | # 屏幕截图 103 | 104 | ![Screenshot showing screen](art/screen.png "Screenshot showing screen") 105 | 106 | 107 | 108 | PC端调试工具 [友善串口调试工具](https://github.com/xmaihh/Android-Serialport/raw/master/serial_port_utility_latest.exe) 109 | 110 | # 更新日志 111 | ## [2.1.2](https://github.com/xmaihh/Android-Serialport/tree/v2.1.2) 112 | ### 新增 113 | - 添加支持设置校验位:0校验(SPACE)、1校验(MARK) 114 | - 添加支持设置自定义波特率 见[issues#26](https://github.com/xmaihh/Android-Serialport/issues/26) 115 | 116 | ## [2.1.1](https://github.com/xmaihh/Android-Serialport/tree/v2.1.1) 117 | ### 修复 118 | - 修复引用依赖问题见[issues#17](https://github.com/xmaihh/Android-Serialport/issues/17) 119 | - 迁移到到MavenCentral 120 | 121 | ## [2.1](https://github.com/xmaihh/Android-Serialport/tree/v2.1) 122 | ### 新增 123 | - 添加支持设置接收数据粘包处理,支持设置自定义粘包处理 124 | 125 | ## [2.0](https://github.com/xmaihh/Android-Serialport/tree/v2.0) 126 | ### 新增 127 | - 添加支持设置校验位、数据位、停止位、流控配置项 128 | 129 | ## [1.0](https://github.com/xmaihh/Android-Serialport/tree/v1.0) 130 | ### 新增 131 | - 基础功能,串口设置串口号、波特率,发送、接收数据 132 | 133 | 134 | # FAQ 135 | * 此library不提供ROOT权限,请自行打开串口`666`权限 136 | ```shell 137 | adb shell chmod 666 /dev/ttyS1 138 | ``` 139 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | 2 | # Android-Serialport 3 | Porting Google's official serial port library [android-serialport-api](https://code.google.com/archive/p/android-serialport-api/),only supports serial port name and baud rate. This item adds support check digit, data bit, stop bit, flow control configuration item. 4 | 5 | 6 | Android-Serialport 7 | 8 | [![GitHub forks](https://img.shields.io/github/forks/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport/network)[![GitHub issues](https://img.shields.io/github/issues/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport/issues)[![GitHub stars](https://img.shields.io/github/stars/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport/stargazers)[![Source persent](https://img.shields.io/badge/Java-73.2%25-brightgreen.svg)](https://github.com/xmaihh/Android-Serialport/search?l=C)[![Jcenter2.1](https://img.shields.io/badge/jcenter-2.1-brightgreen.svg)](https://bintray.com/xmaihh/maven/serialport)![Maven Central](https://img.shields.io/maven-central/v/io.github.xmaihh/serialport) 9 | [![GitHub license](https://img.shields.io/github/license/xmaihh/Android-Serialport.svg)](https://github.com/xmaihh/Android-Serialport) 10 | 11 | # Document 12 | 13 |

14 | 中文 15 | | English 16 |

17 | 18 | # Usage 19 | 1. Open your root `build.gradle` and add `mavenCentral()`: 20 | ``` 21 | allprojects { 22 | repositories { 23 | ... 24 | mavenCentral() 25 | } 26 | } 27 | ``` 28 | 2. To add a dependency to your project, specify a dependency configuration such as implementation in the dependencies block of your module's build.gradle file. 29 | ``` 30 | dependencies { 31 | implementation 'io.github.xmaihh:serialport:2.1.1' 32 | } 33 | ``` 34 | # Attribute 35 | | Attributes | parameter | 36 | |--------------|:------------------------------------------------------------------------------------------------------------------------------------:| 37 | | Baud rate | [BAUDRATE](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/android_serialport_api/SerialPort.java) | 38 | | Data bit | 5,6,7,8 ; default value 8 | 39 | | Parity bit | No parity (NONE), odd parity (ODD), even parity (EVEN), 0 parity(SPACE), 1 parity(MARK); default no parity | 40 | | Stop bit | 1,2 ; default value 1 | 41 | | Flow Control | No flow control (NONE), hardware flow control (RTS/CTS), software flow control (XON/XOFF); flow control is not used by default | 42 | # Function 43 | ## 1.List the serial port 44 | ``` 45 | serialPortFinder.getAllDevicesPath(); 46 | ``` 47 | ## 2.Serial port property settings 48 | ``` 49 | serialHelper.setPort(String sPort); //set serial port 50 | serialHelper.setBaudRate(int iBaud); //set baud rate 51 | serialHelper.setStopBits(int stopBits); //set stop bit 52 | serialHelper.setDataBits(int dataBits); //set data bit 53 | serialHelper.setParity(int parity); //set parity 54 | serialHelper.setFlowCon(int flowcon); //set flow control 55 | ``` 56 | [![](https://img.shields.io/badge/warning-%09%20admonition-yellow.svg)](https://github.com/xmaihh/Android-Serialport) 57 | 58 | Serial port property settings must be set before the function 'open()' is executed. 59 | ## 3. Open the serial port 60 | ``` 61 | serialHelper.open(); 62 | ``` 63 | ## 4.Close the serial port 64 | ``` 65 | serialHelper.close(); 66 | ``` 67 | ## 5.Send 68 | ``` 69 | serialHelper.send(byte[] bOutArray); // send byte[] 70 | serialHelper.sendHex(String sHex); // send Hex 71 | serialHelper.sendTxt(String sTxt); // send ASCII 72 | ``` 73 | ## 6.Receiving 74 | ``` 75 | @Override 76 | protected void onDataReceived(final ComBean comBean) { 77 | Toast.makeText(getBaseContext(), new String(comBean.bRec, "UTF-8"), Toast.LENGTH_SHORT).show(); 78 | } 79 | ``` 80 | ## 7.Sticky processing 81 | Support sticky package processing, the reason is seen in the [issues#1](https://github.com/xmaihh/Android-Serialport/issues/1) , the provided sticky package processing 82 | - Not processed (default) 83 | - First and last special character processing 84 | - Fixed length processing 85 | - Dynamic length processing 86 | 87 | Supports custom sticky packet processing. 88 | 89 | ## Step 1 90 | The first step is to implement the [AbsStickPackageHelper](https://github.com/xmaihh/Android-Serialport/blob/master/serialport/src/main/java/tp/xmaihh/serialport/stick/AbsStickPackageHelper.java) interface. 91 | ``` 92 | /** 93 | * Accept the message, the helper of the sticky packet processing, return the final data through inputstream, need to manually process the sticky packet, and the returned byte[] is the complete data we expected. 94 | * Note: This method will be called repeatedly until it resolves to a complete piece of data. This method is synchronous, try not to do time-consuming operations, otherwise it will block reading data. 95 | */ 96 | public interface AbsStickPackageHelper { 97 | byte[] execute(InputStream is); 98 | } 99 | ``` 100 | ## Step 2 101 | Set sticky package processing 102 | ``` 103 | serialHelper.setStickPackageHelper(AbsStickPackageHelper mStickPackageHelper); 104 | ``` 105 | 106 | # Screenshots 107 | 108 | ![Screenshot showing screen](art/screen.png "Screenshot showing screen") 109 | 110 | 111 | 112 | PC-side debugging tools [Serial debugging tool for Win](https://github.com/xmaihh/Android-Serialport/raw/master/serial_port_utility_latest.exe) 113 | 114 | # Changelog 115 | ## [2.1.2](https://github.com/xmaihh/Android-Serialport/tree/v2.1.2) 116 | ### Added 117 | - Add support for setting parity: 0 parity(SPACE), 1 parity(MARK) 118 | - Added support for setting custom baud rate [issues#26](https://github.com/xmaihh/Android-Serialport/issues/26) 119 | 120 | ## [2.1.1](https://github.com/xmaihh/Android-Serialport/tree/v2.1.1) 121 | ### Fixed 122 | - Fix bug.[issues#17](https://github.com/xmaihh/Android-Serialport/issues/17) 123 | - Migrate to MavenCentral 124 | 125 | ## [2.1](https://github.com/xmaihh/Android-Serialport/tree/v2.1) 126 | ### Added 127 | - Add support settings to receive data sticky packet processing, support for setting custom sticky packet processing 128 | 129 | ## [2.0](https://github.com/xmaihh/Android-Serialport/tree/v2.0) 130 | ### Added 131 | - Add support for setting parity, data bits, stop bits, flow control configuration items 132 | 133 | ## [1.0](https://github.com/xmaihh/Android-Serialport/tree/v1.0) 134 | ### Added 135 | - Basic function, serial port set serial port number, baud rate, send and receive data 136 | 137 | 138 | # FAQ 139 | 140 | ![Watch out](https://img.shields.io/badge/Watch%20out%20-FF4081) 141 | This library does not provide ROOT permissions, please open the serial port '666' permissions yourself. 142 | ``` 143 | adb shell chmod 666 /dev/ttyS1 144 | ``` 145 | 146 | ## Contribute 147 | 148 | Please do contribute! Issues and pull requests are welcome. 149 | 150 | Thank you for your help improving software one changelog at a time! 151 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdk 34 5 | defaultConfig { 6 | applicationId "com.ex.serialport" 7 | minSdk 14 8 | targetSdk 34 9 | versionCode gitVersionCode 10 | versionName gitVersionName 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled true 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | android.applicationVariants.configureEach { variant -> 19 | variant.outputs.configureEach { output -> 20 | def fileName = "AndroidSerialportSample-${defaultConfig.versionCode}_${defaultConfig.versionName}.apk" 21 | outputFileName = fileName 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(include: ['*.jar'], dir: 'libs') 28 | implementation 'androidx.appcompat:appcompat:1.6.1' 29 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 30 | implementation 'androidx.recyclerview:recyclerview:1.3.1' 31 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.7' 32 | implementation project(path: ':serialport') 33 | // implementation 'io.github.xmaihh:serialport:2.1.2' 34 | } 35 | 36 | -------------------------------------------------------------------------------- /app/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 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/ex/serialport/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ex.serialport; 2 | 3 | import android.content.DialogInterface; 4 | import android.os.Bundle; 5 | import android.text.InputFilter; 6 | import android.text.Spanned; 7 | import android.view.Menu; 8 | import android.view.MenuInflater; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.AdapterView; 12 | import android.widget.Button; 13 | import android.widget.EditText; 14 | import android.widget.RadioButton; 15 | import android.widget.RadioGroup; 16 | import android.widget.Spinner; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import androidx.annotation.Nullable; 21 | import androidx.appcompat.app.AlertDialog; 22 | import androidx.appcompat.app.AppCompatActivity; 23 | import androidx.recyclerview.widget.DividerItemDecoration; 24 | import androidx.recyclerview.widget.LinearLayoutManager; 25 | import androidx.recyclerview.widget.RecyclerView; 26 | 27 | import com.ex.serialport.adapter.LogListAdapter; 28 | import com.ex.serialport.adapter.SpAdapter; 29 | 30 | import java.io.IOException; 31 | import java.nio.charset.StandardCharsets; 32 | import java.text.SimpleDateFormat; 33 | import java.util.Date; 34 | 35 | import android_serialport_api.SerialPortFinder; 36 | import tp.xmaihh.serialport.SerialHelper; 37 | import tp.xmaihh.serialport.bean.ComBean; 38 | import tp.xmaihh.serialport.utils.ByteUtil; 39 | 40 | public class MainActivity extends AppCompatActivity { 41 | 42 | private RecyclerView recy; 43 | private Spinner spSerial; 44 | private EditText edInput; 45 | private Button btSend; 46 | private RadioGroup radioGroup; 47 | private RadioButton radioButton1; 48 | private RadioButton radioButton2; 49 | private SerialPortFinder serialPortFinder; 50 | private SerialHelper serialHelper; 51 | private Spinner spBote; 52 | private Button btOpen; 53 | private LogListAdapter logListAdapter; 54 | private Spinner spDatab; 55 | private Spinner spParity; 56 | private Spinner spStopb; 57 | private Spinner spFlowcon; 58 | private TextView customBaudrate; 59 | 60 | @Override 61 | protected void onDestroy() { 62 | super.onDestroy(); 63 | serialHelper.close(); 64 | } 65 | 66 | @Override 67 | protected void onCreate(@Nullable Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | setContentView(R.layout.activity_main); 70 | recy = findViewById(R.id.recyclerView); 71 | spSerial = findViewById(R.id.sp_serial); 72 | edInput = findViewById(R.id.ed_input); 73 | btSend = findViewById(R.id.btn_send); 74 | spBote = findViewById(R.id.sp_baudrate); 75 | btOpen = findViewById(R.id.btn_open); 76 | 77 | radioGroup = findViewById(R.id.radioGroup); 78 | radioButton1 = findViewById(R.id.radioButton1); 79 | radioButton2 = findViewById(R.id.radioButton2); 80 | 81 | spDatab = findViewById(R.id.sp_databits); 82 | spParity = findViewById(R.id.sp_parity); 83 | spStopb = findViewById(R.id.sp_stopbits); 84 | spFlowcon = findViewById(R.id.sp_flowcon); 85 | customBaudrate = findViewById(R.id.tv_custom_baudrate); 86 | 87 | logListAdapter = new LogListAdapter(null); 88 | recy.setLayoutManager(new LinearLayoutManager(this)); 89 | recy.setAdapter(logListAdapter); 90 | recy.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); 91 | 92 | serialPortFinder = new SerialPortFinder(); 93 | serialHelper = new SerialHelper("dev/ttyS1", 115200) { 94 | @Override 95 | protected void onDataReceived(final ComBean comBean) { 96 | runOnUiThread(new Runnable() { 97 | @Override 98 | public void run() { 99 | if (radioGroup.getCheckedRadioButtonId() == R.id.radioButton1) { 100 | logListAdapter.addData(comBean.sRecTime + " Rx:<==" + new String(comBean.bRec, StandardCharsets.UTF_8)); 101 | if (logListAdapter.getData() != null && logListAdapter.getData().size() > 0) { 102 | recy.smoothScrollToPosition(logListAdapter.getData().size()); 103 | } 104 | } else { 105 | logListAdapter.addData(comBean.sRecTime + " Rx:<==" + ByteUtil.ByteArrToHex(comBean.bRec)); 106 | if (logListAdapter.getData() != null && logListAdapter.getData().size() > 0) { 107 | recy.smoothScrollToPosition(logListAdapter.getData().size()); 108 | } 109 | } 110 | } 111 | }); 112 | } 113 | }; 114 | 115 | final String[] ports = serialPortFinder.getAllDevicesPath(); 116 | final String[] botes = new String[]{"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200", "230400", "460800", "500000", "576000", "921600", "1000000", "1152000", "1500000", "2000000", "2500000", "3000000", "3500000", "4000000", "CUSTOM"}; 117 | final String[] databits = new String[]{"8", "7", "6", "5"}; 118 | final String[] paritys = new String[]{"NONE", "ODD", "EVEN", "SPACE", "MARK"}; 119 | final String[] stopbits = new String[]{"1", "2"}; 120 | final String[] flowcons = new String[]{"NONE", "RTS/CTS", "XON/XOFF"}; 121 | 122 | 123 | SpAdapter spAdapter = new SpAdapter(this); 124 | spAdapter.setDatas(ports); 125 | spSerial.setAdapter(spAdapter); 126 | 127 | spSerial.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 128 | @Override 129 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 130 | serialHelper.close(); 131 | serialHelper.setPort(ports[position]); 132 | btOpen.setEnabled(true); 133 | } 134 | 135 | @Override 136 | public void onNothingSelected(AdapterView parent) { 137 | 138 | } 139 | }); 140 | 141 | SpAdapter spAdapter2 = new SpAdapter(this); 142 | spAdapter2.setDatas(botes); 143 | spBote.setAdapter(spAdapter2); 144 | 145 | spBote.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 146 | @Override 147 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 148 | if (position == botes.length - 1) { 149 | showInputDialog(); 150 | return; 151 | } 152 | findViewById(R.id.tv_custom_baudrate).setVisibility(View.GONE); 153 | serialHelper.close(); 154 | serialHelper.setBaudRate(botes[position]); 155 | btOpen.setEnabled(true); 156 | } 157 | 158 | @Override 159 | public void onNothingSelected(AdapterView parent) { 160 | 161 | } 162 | }); 163 | 164 | SpAdapter spAdapter3 = new SpAdapter(this); 165 | spAdapter3.setDatas(databits); 166 | spDatab.setAdapter(spAdapter3); 167 | 168 | spDatab.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 169 | @Override 170 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 171 | serialHelper.close(); 172 | serialHelper.setDataBits(Integer.parseInt(databits[position])); 173 | btOpen.setEnabled(true); 174 | } 175 | 176 | @Override 177 | public void onNothingSelected(AdapterView parent) { 178 | 179 | } 180 | }); 181 | 182 | SpAdapter spAdapter4 = new SpAdapter(this); 183 | spAdapter4.setDatas(paritys); 184 | spParity.setAdapter(spAdapter4); 185 | 186 | spParity.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 187 | @Override 188 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 189 | serialHelper.close(); 190 | serialHelper.setParity(position); 191 | btOpen.setEnabled(true); 192 | } 193 | 194 | @Override 195 | public void onNothingSelected(AdapterView parent) { 196 | 197 | } 198 | }); 199 | 200 | SpAdapter spAdapter5 = new SpAdapter(this); 201 | spAdapter5.setDatas(stopbits); 202 | spStopb.setAdapter(spAdapter5); 203 | 204 | spStopb.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 205 | @Override 206 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 207 | serialHelper.close(); 208 | serialHelper.setStopBits(Integer.parseInt(stopbits[position])); 209 | btOpen.setEnabled(true); 210 | } 211 | 212 | @Override 213 | public void onNothingSelected(AdapterView parent) { 214 | 215 | } 216 | }); 217 | 218 | SpAdapter spAdapter6 = new SpAdapter(this); 219 | spAdapter6.setDatas(flowcons); 220 | spFlowcon.setAdapter(spAdapter6); 221 | 222 | spFlowcon.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 223 | @Override 224 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 225 | serialHelper.close(); 226 | serialHelper.setFlowCon(position); 227 | btOpen.setEnabled(true); 228 | } 229 | 230 | @Override 231 | public void onNothingSelected(AdapterView parent) { 232 | 233 | } 234 | }); 235 | 236 | 237 | btOpen.setOnClickListener(new View.OnClickListener() { 238 | @Override 239 | public void onClick(View v) { 240 | try { 241 | serialHelper.open(); 242 | btOpen.setEnabled(false); 243 | } catch (IOException e) { 244 | Toast.makeText(MainActivity.this, getString(R.string.tips_cannot_be_opened, e.getMessage()), Toast.LENGTH_SHORT).show(); 245 | e.printStackTrace(); 246 | } catch (SecurityException se) { 247 | Toast.makeText(MainActivity.this, getString(R.string.tips_cannot_be_opened, se.getMessage()), Toast.LENGTH_SHORT).show(); 248 | } 249 | } 250 | }); 251 | 252 | btSend.setOnClickListener(new View.OnClickListener() { 253 | @Override 254 | public void onClick(View v) { 255 | SimpleDateFormat sDateFormat = new SimpleDateFormat("hh:mm:ss.SSS"); 256 | if (radioGroup.getCheckedRadioButtonId() == R.id.radioButton1) { 257 | if (edInput.getText().toString().length() > 0) { 258 | if (serialHelper.isOpen()) { 259 | serialHelper.sendTxt(edInput.getText().toString()); 260 | logListAdapter.addData(sDateFormat.format(new Date()) + " Tx:==>" + edInput.getText().toString()); 261 | if (logListAdapter.getData() != null && logListAdapter.getData().size() > 0) { 262 | recy.smoothScrollToPosition(logListAdapter.getData().size()); 263 | } 264 | } else { 265 | Toast.makeText(getBaseContext(), R.string.tips_serial_port_not_open, Toast.LENGTH_SHORT).show(); 266 | } 267 | } else { 268 | Toast.makeText(getBaseContext(), R.string.tips_please_enter_a_data, Toast.LENGTH_SHORT).show(); 269 | } 270 | } else { 271 | if (edInput.getText().toString().length() > 0) { 272 | if (serialHelper.isOpen()) { 273 | try { 274 | Long.parseLong(edInput.getText().toString(), 16); 275 | } catch (NumberFormatException e) { 276 | Toast.makeText(getBaseContext(), R.string.tips_formatting_hex_error, Toast.LENGTH_SHORT).show(); 277 | return; 278 | } 279 | serialHelper.sendHex(edInput.getText().toString()); 280 | logListAdapter.addData(sDateFormat.format(new Date()) + " Tx:==>" + edInput.getText().toString()); 281 | if (logListAdapter.getData() != null && logListAdapter.getData().size() > 0) { 282 | recy.smoothScrollToPosition(logListAdapter.getData().size()); 283 | } 284 | } else { 285 | Toast.makeText(getBaseContext(), R.string.tips_serial_port_not_open, Toast.LENGTH_SHORT).show(); 286 | } 287 | } else { 288 | Toast.makeText(getBaseContext(), R.string.tips_please_enter_a_data, Toast.LENGTH_SHORT).show(); 289 | } 290 | } 291 | } 292 | }); 293 | } 294 | 295 | private void showInputDialog() { 296 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 297 | builder.setTitle(R.string.tips_please_enter_custom_baudrate); 298 | 299 | final EditText inputField = new EditText(this); 300 | InputFilter filter = new InputFilter() { 301 | public CharSequence filter(CharSequence source, int start, int end, 302 | Spanned dest, int dstart, int dend) { 303 | for (int i = start; i < end; i++) { 304 | if (!Character.isDigit(source.charAt(i))) { 305 | return ""; 306 | } 307 | } 308 | return null; 309 | } 310 | }; 311 | inputField.setFilters(new InputFilter[]{filter}); 312 | builder.setView(inputField); 313 | 314 | builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { 315 | @Override 316 | public void onClick(DialogInterface dialogInterface, int i) { 317 | String userInput = inputField.getText().toString().trim(); 318 | try { 319 | int value = Integer.parseInt(userInput); 320 | if (value >= 0 && value <= 4000000) { 321 | customBaudrate.setVisibility(View.VISIBLE); 322 | customBaudrate.setText(getString(R.string.title_custom_buardate, userInput)); 323 | serialHelper.close(); 324 | serialHelper.setBaudRate(userInput); 325 | btOpen.setEnabled(true); 326 | } 327 | } catch (NumberFormatException e) { 328 | } 329 | } 330 | }); 331 | 332 | builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 333 | @Override 334 | public void onClick(DialogInterface dialogInterface, int i) { 335 | dialogInterface.cancel(); 336 | } 337 | }); 338 | 339 | AlertDialog dialog = builder.create(); 340 | dialog.show(); 341 | } 342 | 343 | 344 | @Override 345 | public boolean onCreateOptionsMenu(Menu menu) { 346 | MenuInflater inflater = getMenuInflater(); 347 | inflater.inflate(R.menu.main, menu); 348 | return true; 349 | } 350 | 351 | @Override 352 | public boolean onOptionsItemSelected(MenuItem item) { 353 | if (item.getItemId() == R.id.menu_clean) { 354 | logListAdapter.clean(); 355 | } 356 | return super.onOptionsItemSelected(item); 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /app/src/main/java/com/ex/serialport/adapter/LogListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ex.serialport.adapter; 2 | 3 | import com.chad.library.adapter.base.BaseQuickAdapter; 4 | import com.chad.library.adapter.base.viewholder.BaseViewHolder; 5 | import com.ex.serialport.R; 6 | 7 | import java.util.List; 8 | 9 | public class LogListAdapter extends BaseQuickAdapter { 10 | 11 | public LogListAdapter(List list) { 12 | super(R.layout.item_layout, list); 13 | } 14 | 15 | @Override 16 | protected void convert(BaseViewHolder helper, String item) { 17 | 18 | helper.setText(R.id.textView, item); 19 | 20 | } 21 | 22 | public void clean() { 23 | this.getData().clear(); 24 | notifyDataSetChanged(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/ex/serialport/adapter/SpAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ex.serialport.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | import com.ex.serialport.R; 11 | 12 | public class SpAdapter extends BaseAdapter { 13 | 14 | String[] datas; 15 | Context mContext; 16 | 17 | public SpAdapter(Context context) { 18 | this.mContext = context; 19 | } 20 | 21 | public void setDatas(String[] datas) { 22 | this.datas = datas; 23 | notifyDataSetChanged(); 24 | } 25 | 26 | @Override 27 | public int getCount() { 28 | return datas == null ? 0 : datas.length; 29 | } 30 | 31 | @Override 32 | public Object getItem(int position) { 33 | return datas == null ? null : datas[position]; 34 | } 35 | 36 | @Override 37 | public long getItemId(int position) { 38 | return position; 39 | } 40 | 41 | @Override 42 | public View getView(int position, View convertView, ViewGroup parent) { 43 | ViewHodler hodler = null; 44 | if (convertView == null) { 45 | hodler = new ViewHodler(); 46 | convertView = LayoutInflater.from(mContext).inflate(R.layout.item_layout, null); 47 | hodler.mTextView = (TextView) convertView; 48 | convertView.setTag(hodler); 49 | } else { 50 | hodler = (ViewHodler) convertView.getTag(); 51 | } 52 | 53 | hodler.mTextView.setText(datas[position]); 54 | 55 | return convertView; 56 | } 57 | 58 | private static class ViewHodler { 59 | TextView mTextView; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cleaning.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 29 | 30 | 34 | 35 | 42 | 43 | 44 | 50 | 51 | 58 | 59 | 60 | 67 | 68 | 72 | 73 | 80 | 81 | 86 | 87 | 88 | 92 | 93 | 101 | 102 | 107 | 108 | 109 | 113 | 114 | 122 | 123 | 128 | 129 | 130 | 134 | 135 | 143 | 144 | 149 | 150 | 151 | 155 | 156 | 164 | 165 | 170 | 171 | 172 | 176 | 177 | 185 | 186 | 191 | 192 | 193 | 199 | 200 |