├── .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 |
7 |
8 | [](https://github.com/xmaihh/Android-Serialport/network)[](https://github.com/xmaihh/Android-Serialport/issues)[](https://github.com/xmaihh/Android-Serialport/stargazers)[](https://github.com/xmaihh/Android-Serialport/search?l=C)[](https://bintray.com/xmaihh/maven/serialport)
9 | [](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://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 | 
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 |
7 |
8 | [](https://github.com/xmaihh/Android-Serialport/network)[](https://github.com/xmaihh/Android-Serialport/issues)[](https://github.com/xmaihh/Android-Serialport/stargazers)[](https://github.com/xmaihh/Android-Serialport/search?l=C)[](https://bintray.com/xmaihh/maven/serialport)
9 | [](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://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 | 
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 | 
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 |
205 |
206 |
211 |
212 |
213 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
25 |
26 |
33 |
34 |
41 |
42 |
50 |
51 |
52 |
62 |
63 |
73 |
74 |
81 |
82 |
83 |
92 |
93 |
101 |
102 |
103 |
113 |
114 |
121 |
122 |
123 |
132 |
133 |
141 |
142 |
152 |
153 |
160 |
161 |
162 |
172 |
173 |
181 |
182 |
193 |
194 |
202 |
203 |
211 |
212 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 发送Text
4 | 发送Hex
5 | *自定义波特率:%s
6 | 串口号:
7 | 波特率:
8 | 数据位:
9 | 校验位:
10 | 停止位:
11 | 流控:
12 | 开启
13 | 发送
14 | 清空
15 | 串口没打开
16 | 先填数据吧
17 | 格式不正确,不是16进制字符串
18 | 输入自定义波特率
19 | 打开失败。msg:%s
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 36dp
7 | 8dp
8 | 32dp
9 | 32dp
10 | 32dp
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Serial Port API sample
5 |
6 | Serial Port API sample v1.1\n
7 | \n
8 | This program is the sample application provided with project android-serialport-api.\n
9 | \n
10 | More information and bug report on [http://code.google.com/p/android-serialport-api]\n
11 | \n
12 | Copyright 2010-2011 Cedric Priscal\n
13 | \n
14 | Licensed under the Apache License, Version 2.0 (the "License");
15 | you may not use this file except in compliance with the License.
16 | You may obtain a copy of the License at\n
17 | \n
18 | http://www.apache.org/licenses/LICENSE-2.0\n
19 | \n
20 | Unless required by applicable law or agreed to in writing, software
21 | distributed under the License is distributed on an "AS IS" BASIS,
22 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 | See the License for the specific language governing permissions and
24 | limitations under the License.\n
25 |
26 | Please configure your serial port first.
27 | You do not have read/write permission to the serial
28 | port.
29 | The serial port can not be opened for an unknown
30 | reason.
31 | Send Text
32 | Send Hex
33 | *custom baudrate:%s
34 | Port:
35 | Baudrate:
36 | Data Bits:
37 | Parity:
38 | Stop Bits:
39 | Flow Type:
40 | Open
41 | Send
42 | Clear
43 | Serial Port not open
44 | Please enter a data
45 | The formatting is bad,not hexadecimal
46 | Enter custom baudrate
47 | Cannot be opened.msg:%s
48 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/art/compile_env.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/art/compile_env.png
--------------------------------------------------------------------------------
/art/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
1021 |
--------------------------------------------------------------------------------
/art/screen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/art/screen.jpg
--------------------------------------------------------------------------------
/art/screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/art/screen.png
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | mavenLocal()
7 | google()
8 | mavenCentral()
9 | gradlePluginPortal()
10 | maven {
11 | url = uri("https://plugins.gradle.org/m2/")
12 | }
13 | maven { url = uri("https://jitpack.io") }
14 | }
15 | dependencies {
16 | classpath 'com.android.tools.build:gradle:7.4.2'
17 | // NOTE: Do not place your application dependencies here; they belong
18 | // in the individual module build.gradle files
19 | classpath 'org.ajoberstar.grgit:grgit-gradle:5.2.0'
20 | }
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | mavenLocal()
26 | google()
27 | mavenCentral()
28 | gradlePluginPortal()
29 | maven {
30 | url = uri("https://plugins.gradle.org/m2/")
31 | }
32 | maven { url = uri("https://jitpack.io") }
33 | }
34 | }
35 |
36 | task clean(type: Delete) {
37 | delete rootProject.buildDir
38 | }
39 |
40 | ext {
41 | Grgit = org.ajoberstar.grgit.Grgit
42 | }
43 |
44 | apply from: 'versions.gradle'
45 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | android.useDeprecatedNdk=true
15 | android.useAndroidX=true
16 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jun 21 10:59:24 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/serial_port_utility_latest.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmaihh/Android-Serialport/c78d168451fc5e63248443d5c840b9e929ef4acb/serial_port_utility_latest.exe
--------------------------------------------------------------------------------
/serialport/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | local.properties
3 | .cxx/
4 |
--------------------------------------------------------------------------------
/serialport/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 |
6 | cmake_minimum_required(VERSION 3.4.1)
7 |
8 | # Creates and names a library, sets it as either STATIC
9 | # or SHARED, and provides the relative paths to its source code.
10 | # You can define multiple libraries, and CMake builds them for you.
11 | # Gradle automatically packages shared libraries with your APK.
12 |
13 | add_library( # Sets the name of the library.
14 | serialport
15 |
16 | # Sets the library as a shared library.
17 | SHARED
18 |
19 | # Provides a relative path to your source file(s).
20 | src/main/jni/SerialPort.c)
21 |
22 | # Searches for a specified prebuilt library and stores the path as a
23 | # variable. Because CMake includes system libraries in the search path by
24 | # default, you only need to specify the name of the public NDK library
25 | # you want to add. CMake verifies that the library exists before
26 | # completing its build.
27 |
28 | find_library( # Sets the name of the path variable.
29 | log-lib
30 |
31 | # Specifies the name of the NDK library that
32 | # you want CMake to locate.
33 | log)
34 |
35 | # Specifies libraries CMake should link to your target library. You
36 | # can link multiple libraries, such as libraries you define in this
37 | # build script, prebuilt third-party libraries, or system libraries.
38 |
39 | target_link_libraries( # Specifies the target library.
40 | serialport
41 |
42 | # Links the target library to the log library
43 | # included in the NDK.
44 | ${log-lib})
--------------------------------------------------------------------------------
/serialport/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdk 34
5 |
6 | defaultConfig {
7 | minSdk 14
8 | targetSdk 34
9 | versionCode 13
10 | versionName "2.1.2"
11 | }
12 |
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | externalNativeBuild {
21 | cmake {
22 | path "CMakeLists.txt"
23 | }
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/serialport/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 | -keep class tp.xmaihh.serialport.**{*;}
23 | -keep class android_serialport_api.**{*;}
--------------------------------------------------------------------------------
/serialport/project.properties:
--------------------------------------------------------------------------------
1 | #project
2 | project.name=serialport
3 | project.groupId=tp.xmaihh
4 | project.artifactId=serialport
5 | project.packaging=aar
6 | project.siteUrl=https://github.com/xmaihh/Android-Serialport
7 | project.gitUrl=https://github.com/xmaihh/Android-Serialport.git
8 |
9 | #javadoc
10 | javadoc.name=serialport
--------------------------------------------------------------------------------
/serialport/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/serialport/src/main/java/android_serialport_api/SerialPort.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009 Cedric Priscal
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android_serialport_api;
18 |
19 | import android.util.Log;
20 |
21 | import java.io.File;
22 | import java.io.FileDescriptor;
23 | import java.io.FileInputStream;
24 | import java.io.FileOutputStream;
25 | import java.io.IOException;
26 | import java.io.InputStream;
27 | import java.io.OutputStream;
28 |
29 | public class SerialPort {
30 |
31 | private static final String TAG = "SerialPort";
32 |
33 | /**
34 | * Commonly used serial port baudrate
35 | */
36 | public enum BAUDRATE {
37 | B0(0),
38 | B50(50),
39 | B75(75),
40 | B110(110),
41 | B134(134),
42 | B150(150),
43 | B200(200),
44 | B300(300),
45 | B600(600),
46 | B1200(1200),
47 | B1800(1800),
48 | B2400(2400),
49 | B4800(4800),
50 | B9600(9600),
51 | B19200(19200),
52 | B38400(38400),
53 | B57600(57600),
54 | B115200(115200),
55 | B230400(230400),
56 | B460800(460800),
57 | B500000(500000),
58 | B576000(576000),
59 | B921600(921600),
60 | B1000000(1000000),
61 | B1152000(1152000),
62 | B1500000(1500000),
63 | B2000000(2000000),
64 | B2500000(2500000),
65 | B3000000(3000000),
66 | B3500000(3500000),
67 | B4000000(4000000);
68 |
69 | int baudrate;
70 |
71 | BAUDRATE(int baudrate) {
72 | this.baudrate = baudrate;
73 | }
74 |
75 | int getBaudrate() {
76 | return this.baudrate;
77 | }
78 |
79 | }
80 |
81 | /**
82 | * Serial port stop bit
83 | */
84 | public enum STOPB {
85 | B1(1),
86 | B2(2);
87 |
88 | int stopBit;
89 |
90 | STOPB(int stopBit) {
91 | this.stopBit = stopBit;
92 | }
93 |
94 | public int getStopBit() {
95 | return this.stopBit;
96 | }
97 |
98 | }
99 |
100 | /**
101 | * Serial port data bits
102 | */
103 | public enum DATAB {
104 | CS5(5),
105 | CS6(6),
106 | CS7(7),
107 | CS8(8);
108 |
109 | int dataBit;
110 |
111 | DATAB(int dataBit) {
112 | this.dataBit = dataBit;
113 | }
114 |
115 | public int getDataBit() {
116 | return this.dataBit;
117 | }
118 | }
119 |
120 | /**
121 | * Serial port parity
122 | */
123 | public enum PARITY {
124 | NONE(0),
125 | ODD(1),
126 | EVEN(2),
127 | SPACE(3),
128 | MARK(4);
129 |
130 | int parity;
131 |
132 | PARITY(int parity) {
133 | this.parity = parity;
134 | }
135 |
136 | public int getParity() {
137 | return this.parity;
138 | }
139 | }
140 |
141 | /**
142 | * Serial port flow type
143 | */
144 | public enum FLOWCON {
145 | NONE(0),
146 | HARD(1),
147 | SOFT(2);
148 |
149 | int flowCon;
150 |
151 | FLOWCON(int flowCon) {
152 | this.flowCon = flowCon;
153 | }
154 |
155 | public int getFlowCon() {
156 | return this.flowCon;
157 | }
158 | }
159 |
160 |
161 | /*
162 | * Do not remove or rename the field mFd: it is used by native method close();
163 | */
164 | private final FileDescriptor mFd;
165 | private final FileInputStream mFileInputStream;
166 | private final FileOutputStream mFileOutputStream;
167 |
168 | public SerialPort(File device, int baudrate, int stopBits, int dataBits, int parity, int flowCon, int flags) throws SecurityException, IOException {
169 |
170 | /* Check access permission */
171 | if (!device.canRead() || !device.canWrite()) {
172 | try {
173 | /* Missing read/write permission, trying to chmod the file */
174 | Process su;
175 | su = Runtime.getRuntime().exec("/system/bin/su");
176 | String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
177 | + "exit\n";
178 | su.getOutputStream().write(cmd.getBytes());
179 | if ((su.waitFor() != 0) || !device.canRead()
180 | || !device.canWrite()) {
181 | throw new SecurityException();
182 | }
183 | } catch (Exception e) {
184 | e.printStackTrace();
185 | throw new SecurityException();
186 | }
187 | }
188 |
189 | mFd = open(device.getAbsolutePath(), baudrate, stopBits, dataBits, parity, flowCon, flags);
190 | if (mFd == null) {
191 | Log.e(TAG, "native open returns null");
192 | throw new IOException();
193 | }
194 | mFileInputStream = new FileInputStream(mFd);
195 | mFileOutputStream = new FileOutputStream(mFd);
196 | }
197 |
198 | // Getters and setters
199 | public InputStream getInputStream() {
200 | return mFileInputStream;
201 | }
202 |
203 | public OutputStream getOutputStream() {
204 | return mFileOutputStream;
205 | }
206 |
207 | /**
208 | * The serial port has 5 parameters: serial device name, baud rate, check bit, data bit, stop bit
209 | * Among them, the check bit is generally defaulted to the NONE, the data bit is generally defaulted to 8, and the stop bit is defaulted to 1
210 | *
211 | * @param path to the data pair of the serial device
212 | * @param baudrate {@link BAUDRATE} baudrate
213 | * @param stopBits {@link STOPB} stop bit
214 | * @param dataBits {@link DATAB} data bits
215 | * @param parity {@link PARITY} check digit
216 | * @param flowCon {@link FLOWCON} flow control
217 | * @param flags O_RDWR read and write mode to open | O_NOCTTY Do not allow process management serial port | O_NDELAY Non-blocking
218 | * @return
219 | */
220 | private native static FileDescriptor open(String path, int baudrate, int stopBits, int dataBits, int parity, int flowCon, int flags);
221 |
222 | public native void close();
223 |
224 | static {
225 | System.loadLibrary("serialport");
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/serialport/src/main/java/android_serialport_api/SerialPortFinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009 Cedric Priscal
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android_serialport_api;
18 |
19 | import android.util.Log;
20 |
21 | import java.io.File;
22 | import java.io.FileReader;
23 | import java.io.IOException;
24 | import java.io.LineNumberReader;
25 | import java.util.Iterator;
26 | import java.util.Vector;
27 |
28 | public class SerialPortFinder {
29 |
30 | public class Driver {
31 | public Driver(String name, String root) {
32 | mDriverName = name;
33 | mDeviceRoot = root;
34 | }
35 |
36 | private String mDriverName;
37 | private String mDeviceRoot;
38 | Vector mDevices = null;
39 |
40 | public Vector getDevices() {
41 | if (mDevices == null) {
42 | mDevices = new Vector();
43 | File dev = new File("/dev");
44 |
45 | File[] files = dev.listFiles();
46 |
47 | if (files != null) {
48 | int i;
49 | for (i = 0; i < files.length; i++) {
50 | if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
51 | Log.d(TAG, "Found new device: " + files[i]);
52 | mDevices.add(files[i]);
53 | }
54 | }
55 | }
56 | }
57 | return mDevices;
58 | }
59 |
60 | public String getName() {
61 | return mDriverName;
62 | }
63 | }
64 |
65 | private static final String TAG = "SerialPort";
66 |
67 | private Vector mDrivers = null;
68 |
69 | Vector getDrivers() throws IOException {
70 | if (mDrivers == null) {
71 | mDrivers = new Vector();
72 | LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers"));
73 | String l;
74 | while((l = r.readLine()) != null) {
75 | // Issue 3:
76 | // Since driver name may contain spaces, we do not extract driver name with split()
77 | String drivername = l.substring(0, 0x15).trim();
78 | String[] w = l.split(" +");
79 | if ((w.length >= 5) && (w[w.length-1].equals("serial"))) {
80 | Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length-4]);
81 | mDrivers.add(new Driver(drivername, w[w.length-4]));
82 | }
83 | }
84 | r.close();
85 | }
86 | return mDrivers;
87 | }
88 |
89 | public String[] getAllDevices() {
90 | Vector devices = new Vector();
91 | // Parse each driver
92 | Iterator itdriv;
93 | try {
94 | itdriv = getDrivers().iterator();
95 | while(itdriv.hasNext()) {
96 | Driver driver = itdriv.next();
97 | Iterator itdev = driver.getDevices().iterator();
98 | while(itdev.hasNext()) {
99 | String device = itdev.next().getName();
100 | String value = String.format("%s (%s)", device, driver.getName());
101 | devices.add(value);
102 | }
103 | }
104 | } catch (IOException e) {
105 | e.printStackTrace();
106 | }
107 | return devices.toArray(new String[devices.size()]);
108 | }
109 |
110 | public String[] getAllDevicesPath() {
111 | Vector devices = new Vector();
112 | // Parse each driver
113 | Iterator itdriv;
114 | try {
115 | itdriv = getDrivers().iterator();
116 | while(itdriv.hasNext()) {
117 | Driver driver = itdriv.next();
118 | Iterator itdev = driver.getDevices().iterator();
119 | while(itdev.hasNext()) {
120 | String device = itdev.next().getAbsolutePath();
121 | devices.add(device);
122 | }
123 | }
124 | } catch (IOException e) {
125 | e.printStackTrace();
126 | }
127 | return devices.toArray(new String[devices.size()]);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/SerialHelper.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.security.InvalidParameterException;
10 |
11 | import android_serialport_api.SerialPort;
12 | import tp.xmaihh.serialport.bean.ComBean;
13 | import tp.xmaihh.serialport.stick.AbsStickPackageHelper;
14 | import tp.xmaihh.serialport.stick.BaseStickPackageHelper;
15 | import tp.xmaihh.serialport.utils.ByteUtil;
16 |
17 | public abstract class SerialHelper {
18 | private SerialPort mSerialPort;
19 | private OutputStream mOutputStream;
20 | private InputStream mInputStream;
21 | private ReadThread mReadThread;
22 | private SendThread mSendThread;
23 | private String sPort = "/dev/ttyS1";
24 | private int iBaudRate = 9600;
25 | private int stopBits = 1;
26 | private int dataBits = 8;
27 | private int parity = 0;
28 | private int flowCon = 0;
29 | private int flags = 0;
30 | private boolean _isOpen = false;
31 | private byte[] _bLoopData = {48};
32 | private int iDelay = 500;
33 |
34 |
35 | public SerialHelper(String sPort, int iBaudRate) {
36 | this.sPort = sPort;
37 | this.iBaudRate = iBaudRate;
38 | }
39 |
40 | public void open()
41 | throws SecurityException, IOException, InvalidParameterException {
42 | this.mSerialPort = new SerialPort(new File(this.sPort), this.iBaudRate, this.stopBits, this.dataBits, this.parity, this.flowCon, this.flags);
43 | this.mOutputStream = this.mSerialPort.getOutputStream();
44 | this.mInputStream = this.mSerialPort.getInputStream();
45 | this.mReadThread = new ReadThread();
46 | this.mReadThread.start();
47 | this.mSendThread = new SendThread();
48 | this.mSendThread.setSuspendFlag();
49 | this.mSendThread.start();
50 | this._isOpen = true;
51 | }
52 |
53 | public void close() {
54 | if (this.mReadThread != null) {
55 | this.mReadThread.interrupt();
56 | }
57 | if (this.mSerialPort != null) {
58 | this.mSerialPort.close();
59 | this.mSerialPort = null;
60 | }
61 | this._isOpen = false;
62 | }
63 |
64 | public void send(byte[] bOutArray) {
65 | try {
66 | this.mOutputStream.write(bOutArray);
67 | } catch (IOException e) {
68 | e.printStackTrace();
69 | }
70 | }
71 |
72 | public void sendHex(String sHex) {
73 | byte[] bOutArray = ByteUtil.HexToByteArr(sHex);
74 | send(bOutArray);
75 | }
76 |
77 | public void sendTxt(String sTxt) {
78 | byte[] bOutArray = sTxt.getBytes();
79 | send(bOutArray);
80 | }
81 |
82 | private class ReadThread
83 | extends Thread {
84 | private ReadThread() {
85 | }
86 |
87 | public void run() {
88 | super.run();
89 | while (!isInterrupted()) {
90 | try {
91 | if (SerialHelper.this.mInputStream == null) {
92 | return;
93 | }
94 |
95 | byte[] buffer = getStickPackageHelper().execute(SerialHelper.this.mInputStream);
96 | if (buffer != null && buffer.length > 0) {
97 | ComBean ComRecData = new ComBean(SerialHelper.this.sPort, buffer, buffer.length);
98 | SerialHelper.this.onDataReceived(ComRecData);
99 | }
100 | // int available = SerialHelper.this.mInputStream.available();
101 | //
102 | // if (available > 0) {
103 | // byte[] buffer = new byte['?'];
104 | // int size = SerialHelper.this.mInputStream.read(buffer);
105 | // if (size > 0) {
106 | // ComBean ComRecData = new ComBean(SerialHelper.this.sPort, buffer, size);
107 | // SerialHelper.this.onDataReceived(ComRecData);
108 | // }
109 | // } else {
110 | // SystemClock.sleep(50);
111 | // }
112 |
113 | } catch (Throwable e) {
114 | if (e.getMessage() != null) {
115 | Log.e("error", e.getMessage());
116 | }
117 | return;
118 | }
119 | }
120 | }
121 | }
122 |
123 | private class SendThread
124 | extends Thread {
125 | public boolean suspendFlag = true;
126 |
127 | private SendThread() {
128 | }
129 |
130 | public void run() {
131 | super.run();
132 | while (!isInterrupted()) {
133 | synchronized (this) {
134 | while (this.suspendFlag) {
135 | try {
136 | wait();
137 | } catch (InterruptedException e) {
138 | e.printStackTrace();
139 | }
140 | }
141 | }
142 | SerialHelper.this.send(SerialHelper.this.getbLoopData());
143 | try {
144 | Thread.sleep(SerialHelper.this.iDelay);
145 | } catch (InterruptedException e) {
146 | e.printStackTrace();
147 | }
148 | }
149 | }
150 |
151 | public void setSuspendFlag() {
152 | this.suspendFlag = true;
153 | }
154 |
155 | public synchronized void setResume() {
156 | this.suspendFlag = false;
157 | notify();
158 | }
159 | }
160 |
161 | public int getBaudRate() {
162 | return this.iBaudRate;
163 | }
164 |
165 | public boolean setBaudRate(int iBaud) {
166 | if (this._isOpen) {
167 | return false;
168 | }
169 | this.iBaudRate = iBaud;
170 | return true;
171 | }
172 |
173 | public boolean setBaudRate(String sBaud) {
174 | int iBaud = Integer.parseInt(sBaud);
175 | return setBaudRate(iBaud);
176 | }
177 |
178 | public int getStopBits() {
179 | return this.stopBits;
180 | }
181 |
182 | public boolean setStopBits(int stopBits) {
183 | if (this._isOpen) {
184 | return false;
185 | }
186 | this.stopBits = stopBits;
187 | return true;
188 | }
189 |
190 | public int getDataBits() {
191 | return this.dataBits;
192 | }
193 |
194 | public boolean setDataBits(int dataBits) {
195 | if (this._isOpen) {
196 | return false;
197 | }
198 | this.dataBits = dataBits;
199 | return true;
200 | }
201 |
202 | public int getParity() {
203 | return this.parity;
204 | }
205 |
206 | public boolean setParity(int parity) {
207 | if (this._isOpen) {
208 | return false;
209 | }
210 | this.parity = parity;
211 | return true;
212 | }
213 |
214 | public int getFlowCon() {
215 | return this.flowCon;
216 | }
217 |
218 | public boolean setFlowCon(int flowCon) {
219 | if (this._isOpen) {
220 | return false;
221 | }
222 | this.flowCon = flowCon;
223 | return true;
224 | }
225 |
226 | public String getPort() {
227 | return this.sPort;
228 | }
229 |
230 | public boolean setPort(String sPort) {
231 | if (this._isOpen) {
232 | return false;
233 | }
234 | this.sPort = sPort;
235 | return true;
236 | }
237 |
238 | public boolean isOpen() {
239 | return this._isOpen;
240 | }
241 |
242 | public byte[] getbLoopData() {
243 | return this._bLoopData;
244 | }
245 |
246 | public void setbLoopData(byte[] bLoopData) {
247 | this._bLoopData = bLoopData;
248 | }
249 |
250 | public void setTxtLoopData(String sTxt) {
251 | this._bLoopData = sTxt.getBytes();
252 | }
253 |
254 | public void setHexLoopData(String sHex) {
255 | this._bLoopData = ByteUtil.HexToByteArr(sHex);
256 | }
257 |
258 | public int getiDelay() {
259 | return this.iDelay;
260 | }
261 |
262 | public void setiDelay(int iDelay) {
263 | this.iDelay = iDelay;
264 | }
265 |
266 | public void startSend() {
267 | if (this.mSendThread != null) {
268 | this.mSendThread.setResume();
269 | }
270 | }
271 |
272 | public void stopSend() {
273 | if (this.mSendThread != null) {
274 | this.mSendThread.setSuspendFlag();
275 | }
276 | }
277 |
278 | protected abstract void onDataReceived(ComBean paramComBean);
279 |
280 | private AbsStickPackageHelper mStickPackageHelper = new BaseStickPackageHelper();
281 |
282 | public AbsStickPackageHelper getStickPackageHelper() {
283 | return mStickPackageHelper;
284 | }
285 |
286 | public void setStickPackageHelper(AbsStickPackageHelper mStickPackageHelper) {
287 | this.mStickPackageHelper = mStickPackageHelper;
288 | }
289 |
290 | }
291 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/bean/ComBean.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport.bean;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import java.text.SimpleDateFormat;
7 | import java.util.Date;
8 |
9 | public class ComBean implements Parcelable {
10 | public byte[] bRec;
11 | public String sRecTime;
12 | public String sComPort;
13 |
14 | public ComBean(String sPort, byte[] buffer, int size) {
15 | this.sComPort = sPort;
16 | this.bRec = new byte[size];
17 | for (int i = 0; i < size; i++) {
18 | this.bRec[i] = buffer[i];
19 | }
20 | SimpleDateFormat sDateFormat = new SimpleDateFormat("hh:mm:ss.SSS");
21 | this.sRecTime = sDateFormat.format(new Date());
22 | }
23 |
24 | protected ComBean(Parcel in) {
25 | bRec = in.createByteArray();
26 | sRecTime = in.readString();
27 | sComPort = in.readString();
28 | }
29 |
30 | public static final Creator CREATOR = new Creator() {
31 | @Override
32 | public ComBean createFromParcel(Parcel in) {
33 | return new ComBean(in);
34 | }
35 |
36 | @Override
37 | public ComBean[] newArray(int size) {
38 | return new ComBean[size];
39 | }
40 | };
41 |
42 | @Override
43 | public int describeContents() {
44 | return 0;
45 | }
46 |
47 | @Override
48 | public void writeToParcel(Parcel dest, int flags) {
49 | dest.writeByteArray(bRec);
50 | dest.writeString(sRecTime);
51 | dest.writeString(sComPort);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/stick/AbsStickPackageHelper.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport.stick;
2 |
3 | import java.io.InputStream;
4 |
5 | /**
6 | * Accept the message, the helper of the sticky packet processing,
7 | * return the final data through the inputstream,
8 | * manually process the sticky packet, and the returned byte[] is the complete data we expect
9 | * Note: This method will be called repeatedly until a complete piece of data is parsed.
10 | * This method is synchronous, try not to do time-consuming operations, otherwise it will block reading data
11 | */
12 | public interface AbsStickPackageHelper {
13 | byte[] execute(InputStream is);
14 | }
15 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/stick/BaseStickPackageHelper.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport.stick;
2 |
3 | import android.os.SystemClock;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 |
8 | /**
9 | * The simplest thing to do is not to deal with sticky packets,
10 | * read directly and return as much as InputStream.available() reads
11 | */
12 | public class BaseStickPackageHelper implements AbsStickPackageHelper {
13 | public BaseStickPackageHelper() {
14 | }
15 |
16 | @Override
17 | public byte[] execute(InputStream is) {
18 | try {
19 | int available = is.available();
20 | if (available > 0) {
21 | byte[] buffer = new byte[available];
22 | int size = is.read(buffer);
23 | if (size > 0) {
24 | return buffer;
25 | }
26 | } else {
27 | SystemClock.sleep(50);
28 | }
29 |
30 | } catch (IOException e) {
31 | e.printStackTrace();
32 | }
33 | return null;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/stick/SpecifiedStickPackageHelper.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport.stick;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | /**
10 | * The sticky packet processing of specific characters,
11 | * one Byte[] at the beginning and the end, cannot be empty at the same time,
12 | * if one of them is empty, then the non-empty is used as the split marker
13 | * Example: The protocol is formulated as ^+data+$, starting with ^ and ending with $
14 | */
15 | public class SpecifiedStickPackageHelper implements AbsStickPackageHelper {
16 | private final byte[] head;
17 | private final byte[] tail;
18 | private final List bytes;
19 | private final int headLen;
20 | private final int tailLen;
21 |
22 | public SpecifiedStickPackageHelper(byte[] head, byte[] tail) {
23 | this.head = head;
24 | this.tail = tail;
25 | if (head == null || tail == null) {
26 | throw new IllegalStateException(" head or tail ==null");
27 | }
28 | if (head.length == 0 && tail.length == 0) {
29 | throw new IllegalStateException(" head and tail length==0");
30 | }
31 | headLen = head.length;
32 | tailLen = tail.length;
33 | bytes = new ArrayList<>();
34 | }
35 |
36 | private boolean endWith(Byte[] src, byte[] target) {
37 | if (src.length < target.length) {
38 | return false;
39 | }
40 | for (int i = 0; i < target.length; i++) {
41 | if (target[target.length - i - 1] != src[src.length - i - 1]) {
42 | return false;
43 | }
44 | }
45 | return true;
46 | }
47 |
48 | private byte[] getRangeBytes(List list, int start, int end) {
49 | Byte[] temps = Arrays.copyOfRange(list.toArray(new Byte[0]), start, end);
50 | byte[] result = new byte[temps.length];
51 | for (int i = 0; i < result.length; i++) {
52 | result[i] = temps[i];
53 | }
54 | return result;
55 | }
56 |
57 | @Override
58 | public byte[] execute(InputStream is) {
59 | bytes.clear();
60 | int len = -1;
61 | byte temp;
62 | int startIndex = -1;
63 | byte[] result = null;
64 | boolean isFindStart = false, isFindEnd = false;
65 | try {
66 | while ((len = is.read()) != -1) {
67 | temp = (byte) len;
68 | bytes.add(temp);
69 | Byte[] byteArray = bytes.toArray(new Byte[]{});
70 | if (headLen == 0 || tailLen == 0) {//Only head or tail markers
71 | if (endWith(byteArray, head) || endWith(byteArray, tail)) {
72 | if (startIndex == -1) {
73 | startIndex = bytes.size() - headLen;
74 | } else {
75 | result = getRangeBytes(bytes, startIndex, bytes.size());
76 | break;
77 | }
78 | }
79 | } else {
80 | if (!isFindStart) {
81 | if (endWith(byteArray, head)) {
82 | startIndex = bytes.size() - headLen;
83 | isFindStart = true;
84 | }
85 | } else if (!isFindEnd) {
86 | if (endWith(byteArray, tail)) {
87 | if (startIndex + headLen <= bytes.size() - tailLen) {
88 | isFindEnd = true;
89 | result = getRangeBytes(bytes, startIndex, bytes.size());
90 | break;
91 | }
92 | }
93 | }
94 |
95 | }
96 | }
97 | if (len == -1) {
98 | return null;
99 | }
100 | } catch (IOException e) {
101 | e.printStackTrace();
102 | return null;
103 | }
104 | return result;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/stick/StaticLenStickPackageHelper.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport.stick;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 |
6 | /**
7 | * Fixed-length adhesive package treatment
8 | * Example: The protocol stipulates that the length of each packet is 16
9 | */
10 | public class StaticLenStickPackageHelper implements AbsStickPackageHelper {
11 | private int stackLen = 16;
12 |
13 | public StaticLenStickPackageHelper(int stackLen) {
14 | this.stackLen = stackLen;
15 | }
16 |
17 | @Override
18 | public byte[] execute(InputStream is) {
19 | int count = 0;
20 | int len = -1;
21 | byte temp;
22 | byte[] result = new byte[stackLen];
23 | try {
24 | while (count < stackLen && (len = is.read()) != -1) {
25 | temp = (byte) len;
26 | result[count] = temp;
27 | count++;
28 | }
29 | if (len == -1) {
30 | return null;
31 | }
32 | } catch (IOException e) {
33 | e.printStackTrace();
34 | return null;
35 | }
36 | return result;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/stick/VariableLenStickPackageHelper.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport.stick;
2 |
3 |
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.nio.ByteOrder;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * Variable-length sticky packet processing, used in the protocol with a length field
12 | * Example: The protocol is: type+dataLen+data+md5
13 | * type: Named type, two bytes
14 | * dataLen: The length of the data field, two bytes
15 | * data: Data field, variable length, length dataLen
16 | * md5: md5 field, 8 bytes
17 | * Use: 1.byteOrder: first determine the big and small ends, ByteOrder.BIG_ENDIAN or ByteOrder.LITTLE_ENDIAN;
18 | * 2.lenSize: The length of the len field, 2 in this example
19 | * 3.lenIndex: The position of the len field, 2 in this example, because the len field is preceded by type, and its length is 2
20 | * 4.offset: the length of the entire package -len, this example is the length of the three fields of type+dataLen+md5, that is, 2+2+8=12
21 | */
22 | public class VariableLenStickPackageHelper implements AbsStickPackageHelper {
23 | private int offset = 0;
24 | private int lenIndex = 0;
25 | private int lenSize = 2;
26 | private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
27 | private final List mBytes;
28 | private final int lenStartIndex;
29 | private final int lenEndIndex;
30 |
31 | public VariableLenStickPackageHelper(ByteOrder byteOrder, int lenSize, int lenIndex, int offset) {
32 | this.byteOrder = byteOrder;
33 | this.lenSize = lenSize;
34 | this.offset = offset;
35 | this.lenIndex = lenIndex;
36 | mBytes = new ArrayList<>();
37 | lenStartIndex = lenIndex;
38 | lenEndIndex = lenIndex + lenSize - 1;
39 | if (lenStartIndex > lenEndIndex) {
40 | throw new IllegalStateException("lenStartIndex>lenEndIndex");
41 | }
42 | }
43 |
44 | private int getLen(byte[] src, ByteOrder order) {
45 | int re = 0;
46 | if (order == ByteOrder.BIG_ENDIAN) {
47 | for (byte b : src) {
48 | re = (re << 8) | (b & 0xff);
49 | }
50 | } else {
51 | for (int i = src.length - 1; i >= 0; i--) {
52 | re = (re << 8) | (src[i] & 0xff);
53 | }
54 | }
55 | return re;
56 | }
57 |
58 | @Override
59 | public byte[] execute(InputStream is) {
60 | mBytes.clear();
61 | int count = 0;
62 | int len = -1;
63 | byte temp;
64 | byte[] result;
65 | int msgLen = -1;
66 | byte[] lenField = new byte[lenSize];
67 | try {
68 | while ((len = is.read()) != -1) {
69 | temp = (byte) len;
70 | if (count >= lenStartIndex && count <= lenEndIndex) {
71 | lenField[count - lenStartIndex] = temp;
72 | if (count == lenEndIndex) {
73 | msgLen = getLen(lenField, byteOrder);
74 | }
75 | }
76 | count++;
77 | mBytes.add(temp);
78 | if (msgLen != -1) {
79 | if (count == msgLen + offset) {
80 | break;
81 | } else if (count > msgLen + offset) {
82 | len = -1;
83 | break;
84 | }
85 | }
86 | }
87 | if (len == -1) {
88 | return null;
89 | }
90 | } catch (IOException e) {
91 | e.printStackTrace();
92 | return null;
93 | }
94 | result = new byte[mBytes.size()];
95 | for (int i = 0; i < result.length; i++) {
96 | result[i] = mBytes.get(i);
97 | }
98 | return result;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/serialport/src/main/java/tp/xmaihh/serialport/utils/ByteUtil.java:
--------------------------------------------------------------------------------
1 | package tp.xmaihh.serialport.utils;
2 |
3 | public class ByteUtil {
4 | public static int isOdd(int num) {
5 | return num & 0x1;
6 | }
7 |
8 | public static int HexToInt(String inHex) {
9 | return Integer.parseInt(inHex, 16);
10 | }
11 |
12 | public static byte HexToByte(String inHex) {
13 | return (byte) Integer.parseInt(inHex, 16);
14 | }
15 |
16 | public static String Byte2Hex(Byte inByte) {
17 | return String.format("%02x", new Object[]{inByte}).toUpperCase();
18 | }
19 |
20 | public static String ByteArrToHex(byte[] inBytArr) {
21 | StringBuilder strBuilder = new StringBuilder();
22 | int j = inBytArr.length;
23 | for (int i = 0; i < j; i++) {
24 | strBuilder.append(Byte2Hex(Byte.valueOf(inBytArr[i])));
25 | strBuilder.append("");
26 | }
27 | return strBuilder.toString();
28 | }
29 |
30 | public static String ByteArrToHex(byte[] inBytArr, int offset, int byteCount) {
31 | StringBuilder strBuilder = new StringBuilder();
32 | int j = byteCount;
33 | for (int i = offset; i < j; i++) {
34 | strBuilder.append(Byte2Hex(Byte.valueOf(inBytArr[i])));
35 | }
36 | return strBuilder.toString();
37 | }
38 |
39 | public static byte[] HexToByteArr(String inHex) {
40 | int hexlen = inHex.length();
41 | byte[] result;
42 | if (isOdd(hexlen) == 1) {
43 | hexlen++;
44 | result = new byte[hexlen / 2];
45 | inHex = "0" + inHex;
46 | } else {
47 | result = new byte[hexlen / 2];
48 | }
49 | int j = 0;
50 | for (int i = 0; i < hexlen; i += 2) {
51 | result[j] = HexToByte(inHex.substring(i, i + 2));
52 | j++;
53 | }
54 | return result;
55 | }
56 | }
--------------------------------------------------------------------------------
/serialport/src/main/jni/SerialPort.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009-2011 Cedric Priscal
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | //#include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "SerialPort.h"
27 |
28 | #include "android/log.h"
29 |
30 | static const char *TAG = "serial_port";
31 | #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
32 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
33 | #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
34 |
35 | /*
36 | * Class: android_serialport_SerialPort
37 | * Method: open
38 | * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
39 | */
40 | JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
41 | (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint stopBits, jint dataBits,
42 | jint parity, jint flowCon, jint flags) {
43 | int fd;
44 | speed_t speed;
45 | jobject mFileDescriptor;
46 |
47 | /* Check arguments */
48 | {
49 | speed = baudrate;
50 | if (speed == -1) {
51 | /* TODO: throw an exception */
52 | LOGE("Invalid baudrate");
53 | return NULL;
54 | }
55 | }
56 |
57 | /* Opening device */
58 | {
59 | jboolean iscopy;
60 | const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
61 | LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
62 | fd = open(path_utf, O_RDWR | flags);
63 | LOGD("open() fd = %d", fd);
64 | (*env)->ReleaseStringUTFChars(env, path, path_utf);
65 | if (fd == -1) {
66 | /* Throw an exception */
67 | LOGE("Cannot open port");
68 | /* TODO: throw an exception */
69 | return NULL;
70 | }
71 | }
72 |
73 | /* Configure device */
74 | {
75 | struct termios2 cfg;
76 |
77 | ioctl (fd, TCGETS2, &cfg);
78 | // Set baudrate
79 | cfg.c_cflag &= ~CBAUD;
80 | cfg.c_cflag |= BOTHER;
81 |
82 | cfg.c_ispeed = baudrate;
83 | cfg.c_ospeed = baudrate;
84 |
85 | cfg.c_cflag &= ~CSIZE;
86 | switch (dataBits) {
87 | case 5:
88 | cfg.c_cflag |= CS5; //5 Data Bits
89 | break;
90 | case 6:
91 | cfg.c_cflag |= CS6; //6 Data Bits
92 | break;
93 | case 7:
94 | cfg.c_cflag |= CS7; //7 Data Bits
95 | break;
96 | case 8:
97 | cfg.c_cflag |= CS8; //8 Data Bits
98 | break;
99 | default:
100 | cfg.c_cflag |= CS8;
101 | break;
102 | }
103 |
104 | switch (parity) {
105 | case 0:
106 | cfg.c_cflag &= ~PARENB; //PARITY OFF
107 | break;
108 | case 1:
109 | cfg.c_cflag |= (PARODD | PARENB); //PARITY ODD
110 | cfg.c_iflag &= ~IGNPAR;
111 | cfg.c_iflag |= PARMRK;
112 | cfg.c_iflag |= INPCK;
113 | break;
114 | case 2:
115 | cfg.c_iflag &= ~(IGNPAR | PARMRK); //PARITY EVEN
116 | cfg.c_iflag |= INPCK;
117 | cfg.c_cflag |= PARENB;
118 | cfg.c_cflag &= ~PARODD;
119 | break;
120 | case 3:
121 | // PARITY SPACE
122 | cfg.c_iflag &= ~IGNPAR; // Make sure wrong parity is not ignored
123 | cfg.c_iflag |= PARMRK; // Marks parity error, parity error
124 | // is given as three char sequence
125 | cfg.c_iflag |= INPCK; // Enable input parity checking
126 | cfg.c_cflag |= PARENB | CMSPAR; // Enable parity and set space parity
127 | cfg.c_cflag &= ~PARODD; //
128 | break;
129 | case 4:
130 | // PARITY MARK
131 | cfg.c_iflag &= ~IGNPAR; // Make sure wrong parity is not ignored
132 | cfg.c_iflag |= PARMRK; // Marks parity error, parity error
133 | // is given as three char sequence
134 | cfg.c_iflag |= INPCK; // Enable input parity checking
135 | cfg.c_cflag |= PARENB | CMSPAR | PARODD;
136 | break;
137 | default:
138 | cfg.c_cflag &= ~PARENB;
139 | break;
140 | }
141 |
142 | switch (stopBits) {
143 | case 1:
144 | cfg.c_cflag &= ~CSTOPB; //1 Stop Bit
145 | break;
146 | case 2:
147 | cfg.c_cflag |= CSTOPB; //2 Stop Bits
148 | break;
149 | default:
150 | cfg.c_cflag &= ~CSTOPB;
151 | break;
152 | }
153 |
154 | // hardware flow control
155 | switch (flowCon) {
156 | case 0:
157 | cfg.c_cflag &= ~CRTSCTS; //No Flow Control
158 | break;
159 | case 1:
160 | cfg.c_cflag |= CRTSCTS; //Hardware Flow Control
161 | break;
162 | case 2:
163 | cfg.c_cflag |= IXON | IXOFF | IXANY; //Software Flow Control
164 | break;
165 | default:
166 | cfg.c_cflag &= ~CRTSCTS;
167 | break;
168 | }
169 |
170 |
171 | if (ioctl(fd, TCSETS2, &cfg)) {
172 | LOGE("tcsets2() failed");
173 | close(fd);
174 | /* TODO: throw an exception */
175 | return NULL;
176 | }
177 | }
178 |
179 | /* Create a corresponding file descriptor */
180 | {
181 | jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
182 | jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V");
183 | jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
184 | mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
185 | (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint) fd);
186 | }
187 |
188 | return mFileDescriptor;
189 | }
190 |
191 | /*
192 | * Class: cedric_serial_SerialPort
193 | * Method: close
194 | * Signature: ()V
195 | */
196 | JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close
197 | (JNIEnv *env, jobject thiz) {
198 | jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
199 | jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
200 |
201 | jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
202 | jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
203 |
204 | jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
205 | jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
206 |
207 | LOGD("close(fd = %d)", descriptor);
208 | close(descriptor);
209 | }
210 |
211 |
--------------------------------------------------------------------------------
/serialport/src/main/jni/SerialPort.h:
--------------------------------------------------------------------------------
1 | /* DO NOT EDIT THIS FILE - it is machine generated */
2 | #include
3 | /* Header for class android_serialport_api_SerialPort */
4 |
5 | #ifndef _Included_android_serialport_api_SerialPort
6 | #define _Included_android_serialport_api_SerialPort
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 | /*
11 | * Class: android_serialport_api_SerialPort
12 | * Method: open
13 | * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
14 | */
15 | JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
16 | (JNIEnv *, jclass, jstring, jint, jint, jint, jint, jint, jint);
17 |
18 | /*
19 | * Class: android_serialport_api_SerialPort
20 | * Method: close
21 | * Signature: ()V
22 | */
23 | JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close
24 | (JNIEnv *, jobject);
25 |
26 | #ifdef __cplusplus
27 | }
28 | #endif
29 | #endif
30 |
--------------------------------------------------------------------------------
/serialport/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | serialport
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':serialport'
2 |
--------------------------------------------------------------------------------
/versions.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | git = rootProject.ext.Grgit.open(currentDir: projectDir)
3 | gitBranchName = git.branch.current().name
4 | gitVersionName = git.describe(tags: true)
5 | gitVersionCode = git.tag.list().size()
6 | gitVersionCodeTime = git.head().dateTime.toEpochSecond().intValue()
7 |
8 | task printVersion() {
9 | println("Version Name: $gitVersionName")
10 | println("Version Code: $gitVersionCode")
11 | println("Version Code Time: $gitVersionCodeTime")
12 | }
13 | }
14 |
15 | def buildTime() {
16 | def date = new Date()
17 | def formattedDate = date.format('yyyyMMdd_HHmm')
18 | return formattedDate
19 | }
20 |
21 | ext.gitBranchName = {
22 | def branch = ""
23 | def proc = "git rev-parse --abbrev-ref HEAD".execute()
24 | proc.in.eachLine { line -> branch = line }
25 | proc.err.eachLine { line -> println line }
26 | proc.waitFor()
27 | return branch
28 | }
29 |
30 | ext.getGitHeadRefsSuffix = {
31 | try {
32 | def headFile = new File(rootDir.getParent(), '.git/HEAD')
33 | if (headFile.exists()) {
34 | String[] strings = headFile.getText('UTF-8').split(" ")
35 | if (strings.size() > 1) {
36 | def refHeadFile = new File(rootDir.getParent(), '.git/' + strings[1])
37 | String refFilePath = refHeadFile.getPath()
38 | def refFile = new File(refFilePath.replace("\n", ""))
39 | return refFile.getText('UTF-8').substring(0, 7)
40 | }
41 | } else {
42 | return "headFile"
43 | }
44 | } catch (Exception e) {
45 | logger.log(LogLevel.ERROR, e.getMessage())
46 | return "error"
47 | }
48 | return "default_version"
49 | }
--------------------------------------------------------------------------------