当前位置:   article > 正文

Android硬件通信之 串口通信_android 串口通信

android 串口通信

一,串口介绍

1.1 串口简介

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口;

串行接口(SerialInterface)是指数据一位一位地顺序传送。其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢;

1.2 串口使用场景

串口是一种用于android开发板对硬件设备通信的一种协议,通过发送某种指令控制硬件设备,通常用于物联网设备的信息传输,比如切割器,打印机,ATM吐卡机、IC/ID卡读卡等。

1.3 波特率

波特率表示串口传输速率,用来衡量数据传输的快慢,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。波特率与距离成反比,波特率越大传输距离相应的就越短;

1.4 数据位

这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息;

1.5 停止位

用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢;

1.6 校验位

在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位;

1.7 串口地址

不同操作系统的串口地址,Android是基于Linux的所以一般情况下使用Android系统的设备串口地址为/dev/ttyS0;

  • /dev的串口包括:虚拟串口,真实串口,USB转串口
  • 真实串口:/dev/tty0..tty1这个一般为机器自带COM口
  • 虚拟串口:/dev/ttyS1...ttyS2...ttyS3...均为虚拟console,同样可以作为输入输出口
  • USB转串口:/dev/tty/USB0

二 Android中串口的实践

2.1 由于串口底层需要调用C代码,所以需要用jni来进行C交互,下面是全部的C代码,以及JNI调用

2.1 SerialPort.h

  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class android_serialport_SerialPort */
  4. #ifndef _Included_android_serialport_SerialPort
  5. #define _Included_android_serialport_SerialPort
  6. #ifdef __cplusplus
  7. extern "C" {
  8. #endif
  9. /*
  10. * Class: android_serialport_SerialPort
  11. * Method: open
  12. * Signature: (Ljava/lang/String;IIIII)Ljava/io/FileDescriptor;
  13. */
  14. JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
  15. (JNIEnv *, jobject, jstring, jint, jint, jint, jint, jint);
  16. /*
  17. * Class: android_serialport_SerialPort
  18. * Method: close
  19. * Signature: ()V
  20. */
  21. JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close
  22. (JNIEnv *, jobject);
  23. #ifdef __cplusplus
  24. }
  25. #endif
  26. #endif
  27. /* Header for class android_serialport_SerialPort_Builder */
  28. #ifndef _Included_android_serialport_SerialPort_Builder
  29. #define _Included_android_serialport_SerialPort_Builder
  30. #ifdef __cplusplus
  31. extern "C" {
  32. #endif
  33. #ifdef __cplusplus
  34. }
  35. #endif
  36. #endif

2.2 SerialPort.c

  1. #include <termios.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <string.h>
  7. #include <jni.h>
  8. #include "SerialPort.h"
  9. #include "android/log.h"
  10. static const char *TAG = "serial_port";
  11. #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
  12. #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
  13. #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
  14. static speed_t getBaudrate(jint baudrate) {
  15. switch (baudrate) {
  16. case 0:
  17. return B0;
  18. case 50:
  19. return B50;
  20. case 75:
  21. return B75;
  22. case 110:
  23. return B110;
  24. case 134:
  25. return B134;
  26. case 150:
  27. return B150;
  28. case 200:
  29. return B200;
  30. case 300:
  31. return B300;
  32. case 600:
  33. return B600;
  34. case 1200:
  35. return B1200;
  36. case 1800:
  37. return B1800;
  38. case 2400:
  39. return B2400;
  40. case 4800:
  41. return B4800;
  42. case 9600:
  43. return B9600;
  44. case 19200:
  45. return B19200;
  46. case 38400:
  47. return B38400;
  48. case 57600:
  49. return B57600;
  50. case 115200:
  51. return B115200;
  52. case 230400:
  53. return B230400;
  54. case 460800:
  55. return B460800;
  56. case 500000:
  57. return B500000;
  58. case 576000:
  59. return B576000;
  60. case 921600:
  61. return B921600;
  62. case 1000000:
  63. return B1000000;
  64. case 1152000:
  65. return B1152000;
  66. case 1500000:
  67. return B1500000;
  68. case 2000000:
  69. return B2000000;
  70. case 2500000:
  71. return B2500000;
  72. case 3000000:
  73. return B3000000;
  74. case 3500000:
  75. return B3500000;
  76. case 4000000:
  77. return B4000000;
  78. default:
  79. return -1;
  80. }
  81. }
  82. /*
  83. * Class: android_serialport_SerialPort
  84. * Method: open
  85. * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
  86. */
  87. JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
  88. (JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint dataBits, jint parity,
  89. jint stopBits,
  90. jint flags) {
  91. int fd;
  92. speed_t speed;
  93. jobject mFileDescriptor;
  94. /* Check arguments */
  95. {
  96. speed = getBaudrate(baudrate);
  97. if (speed == -1) {
  98. /* TODO: throw an exception */
  99. LOGE("Invalid baudrate");
  100. return NULL;
  101. }
  102. }
  103. /* Opening device */
  104. {
  105. jboolean iscopy;
  106. const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
  107. LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
  108. fd = open(path_utf, O_RDWR | flags);
  109. LOGD("open() fd = %d", fd);
  110. (*env)->ReleaseStringUTFChars(env, path, path_utf);
  111. if (fd == -1) {
  112. /* Throw an exception */
  113. LOGE("Cannot open port");
  114. /* TODO: throw an exception */
  115. return NULL;
  116. }
  117. }
  118. /* Configure device */
  119. {
  120. struct termios cfg;
  121. LOGD("Configuring serial port");
  122. if (tcgetattr(fd, &cfg)) {
  123. LOGE("tcgetattr() failed");
  124. close(fd);
  125. /* TODO: throw an exception */
  126. return NULL;
  127. }
  128. cfmakeraw(&cfg);
  129. cfsetispeed(&cfg, speed);
  130. cfsetospeed(&cfg, speed);
  131. cfg.c_cflag &= ~CSIZE;
  132. switch (dataBits) {
  133. case 5:
  134. cfg.c_cflag |= CS5; //使用5位数据位
  135. break;
  136. case 6:
  137. cfg.c_cflag |= CS6; //使用6位数据位
  138. break;
  139. case 7:
  140. cfg.c_cflag |= CS7; //使用7位数据位
  141. break;
  142. case 8:
  143. cfg.c_cflag |= CS8; //使用8位数据位
  144. break;
  145. default:
  146. cfg.c_cflag |= CS8;
  147. break;
  148. }
  149. switch (parity) {
  150. case 0:
  151. cfg.c_cflag &= ~PARENB; //无奇偶校验
  152. break;
  153. case 1:
  154. cfg.c_cflag |= (PARODD | PARENB); //奇校验
  155. break;
  156. case 2:
  157. cfg.c_iflag &= ~(IGNPAR | PARMRK); // 偶校验
  158. cfg.c_iflag |= INPCK;
  159. cfg.c_cflag |= PARENB;
  160. cfg.c_cflag &= ~PARODD;
  161. break;
  162. default:
  163. cfg.c_cflag &= ~PARENB;
  164. break;
  165. }
  166. switch (stopBits) {
  167. case 1:
  168. cfg.c_cflag &= ~CSTOPB; //1位停止位
  169. break;
  170. case 2:
  171. cfg.c_cflag |= CSTOPB; //2位停止位
  172. break;
  173. default:
  174. cfg.c_cflag &= ~CSTOPB; //1位停止位
  175. break;
  176. }
  177. if (tcsetattr(fd, TCSANOW, &cfg)) {
  178. LOGE("tcsetattr() failed");
  179. close(fd);
  180. /* TODO: throw an exception */
  181. return NULL;
  182. }
  183. }
  184. /* Create a corresponding file descriptor */
  185. {
  186. jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
  187. jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
  188. jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
  189. mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
  190. (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint) fd);
  191. }
  192. return mFileDescriptor;
  193. }
  194. /*
  195. * Class: cedric_serial_SerialPort
  196. * Method: close
  197. * Signature: ()V
  198. */
  199. JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close
  200. (JNIEnv *env, jobject thiz) {
  201. jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
  202. jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
  203. jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
  204. jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
  205. jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
  206. jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
  207. LOGD("close(fd = %d)", descriptor);
  208. close(descriptor);
  209. }

2.3 gen_SerialPort_h.sh 生成java的文件目录

  1. #!/bin/sh
  2. javah -o SerialPort.h -jni -classpath ../java android.serialport.SerialPort

2.4 SerialPort.java jni对应的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. package android.serialport;
  17. import android.util.Log;
  18. import androidx.annotation.NonNull;
  19. import androidx.annotation.Nullable;
  20. import java.io.DataOutputStream;
  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. public final class SerialPort {
  29. private static final String TAG = "SerialPort";
  30. public static final String DEFAULT_SU_PATH = "/system/bin/su";
  31. private static String sSuPath = DEFAULT_SU_PATH;
  32. private File device;
  33. private int baudrate;
  34. private int dataBits;
  35. private int parity;
  36. private int stopBits;
  37. private int flags;
  38. /**
  39. * Set the su binary path, the default su binary path is {@link #DEFAULT_SU_PATH}
  40. *
  41. * @param suPath su binary path
  42. */
  43. public static void setSuPath(@Nullable String suPath) {
  44. if (suPath == null) {
  45. return;
  46. }
  47. sSuPath = suPath;
  48. }
  49. /**
  50. * Get the su binary path
  51. *
  52. * @return
  53. */
  54. @NonNull
  55. public static String getSuPath() {
  56. return sSuPath;
  57. }
  58. /*
  59. * Do not remove or rename the field mFd: it is used by native method close();
  60. */
  61. private FileDescriptor mFd;
  62. private FileInputStream mFileInputStream;
  63. private FileOutputStream mFileOutputStream;
  64. /**
  65. * 串口
  66. *
  67. * @param device 串口设备文件
  68. * @param baudrate 波特率
  69. * @param dataBits 数据位;默认8,可选值为5~8
  70. * @param parity 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
  71. * @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位
  72. * @param flags 默认0
  73. * @throws SecurityException
  74. * @throws IOException
  75. */
  76. public SerialPort(@NonNull File device, int baudrate, int dataBits, int parity, int stopBits,
  77. int flags) throws SecurityException, IOException {
  78. this.device = device;
  79. this.baudrate = baudrate;
  80. this.dataBits = dataBits;
  81. this.parity = parity;
  82. this.stopBits = stopBits;
  83. this.flags = flags;
  84. /* Check access permission */
  85. Log.e(TAG, "SerialPort: canRead" + device.canRead());
  86. if (!device.canRead() || !device.canWrite()) {
  87. try {
  88. /* Missing read/write permission, trying to chmod the file */
  89. Process su;
  90. su = Runtime.getRuntime().exec(sSuPath);
  91. String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + "exit\n";
  92. su.getOutputStream().write(cmd.getBytes());
  93. if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) {
  94. throw new SecurityException();
  95. }
  96. } catch (Exception e) {
  97. e.printStackTrace();
  98. throw new SecurityException();
  99. }
  100. }
  101. mFd = open(device.getAbsolutePath(), baudrate, dataBits, parity, stopBits, flags);
  102. if (mFd == null) {
  103. Log.e(TAG, "native open returns null");
  104. throw new IOException();
  105. }
  106. mFileInputStream = new FileInputStream(mFd);
  107. mFileOutputStream = new FileOutputStream(mFd);
  108. }
  109. /**
  110. * 串口,默认的8n1
  111. *
  112. * @param device 串口设备文件
  113. * @param baudrate 波特率
  114. * @throws SecurityException
  115. * @throws IOException
  116. */
  117. public SerialPort(@NonNull File device, int baudrate) throws SecurityException, IOException {
  118. this(device, baudrate, 8, 0, 1, 0);
  119. }
  120. /**
  121. * 串口
  122. *
  123. * @param device 串口设备文件
  124. * @param baudrate 波特率
  125. * @param dataBits 数据位;默认8,可选值为5~8
  126. * @param parity 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
  127. * @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位
  128. * @throws SecurityException
  129. * @throws IOException
  130. */
  131. public SerialPort(@NonNull File device, int baudrate, int dataBits, int parity, int stopBits)
  132. throws SecurityException, IOException {
  133. this(device, baudrate, dataBits, parity, stopBits, 0);
  134. }
  135. // Getters and setters
  136. @NonNull
  137. public InputStream getInputStream() {
  138. return mFileInputStream;
  139. }
  140. @NonNull
  141. public OutputStream getOutputStream() {
  142. return mFileOutputStream;
  143. }
  144. /**
  145. * 串口设备文件
  146. */
  147. @NonNull
  148. public File getDevice() {
  149. return device;
  150. }
  151. /**
  152. * 波特率
  153. */
  154. public int getBaudrate() {
  155. return baudrate;
  156. }
  157. /**
  158. * 数据位;默认8,可选值为5~8
  159. */
  160. public int getDataBits() {
  161. return dataBits;
  162. }
  163. /**
  164. * 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
  165. */
  166. public int getParity() {
  167. return parity;
  168. }
  169. /**
  170. * 停止位;默认1;1:1位停止位;2:2位停止位
  171. */
  172. public int getStopBits() {
  173. return stopBits;
  174. }
  175. public int getFlags() {
  176. return flags;
  177. }
  178. // JNI
  179. private native FileDescriptor open(String absolutePath, int baudrate, int dataBits, int parity,
  180. int stopBits, int flags);
  181. public native void close();
  182. /**
  183. * 关闭流和串口,已经try-catch
  184. */
  185. public void tryClose() {
  186. try {
  187. mFileInputStream.close();
  188. } catch (IOException e) {
  189. //e.printStackTrace();
  190. }
  191. try {
  192. mFileOutputStream.close();
  193. } catch (IOException e) {
  194. //e.printStackTrace();
  195. }
  196. try {
  197. close();
  198. } catch (Exception e) {
  199. //e.printStackTrace();
  200. }
  201. }
  202. static {
  203. System.loadLibrary("serial_port");
  204. }
  205. public static Builder newBuilder(File device, int baudrate) {
  206. return new Builder(device, baudrate);
  207. }
  208. public static Builder newBuilder(String devicePath, int baudrate) {
  209. return new Builder(devicePath, baudrate);
  210. }
  211. public final static class Builder {
  212. private File device;
  213. private int baudrate;
  214. private int dataBits = 8;
  215. private int parity = 0;
  216. private int stopBits = 1;
  217. private int flags = 0;
  218. private Builder(File device, int baudrate) {
  219. this.device = device;
  220. this.baudrate = baudrate;
  221. }
  222. private Builder(String devicePath, int baudrate) {
  223. this(new File(devicePath), baudrate);
  224. }
  225. /**
  226. * 数据位
  227. *
  228. * @param dataBits 默认8,可选值为5~8
  229. * @return
  230. */
  231. public Builder dataBits(int dataBits) {
  232. this.dataBits = dataBits;
  233. return this;
  234. }
  235. /**
  236. * 校验位
  237. *
  238. * @param parity 0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
  239. * @return
  240. */
  241. public Builder parity(int parity) {
  242. this.parity = parity;
  243. return this;
  244. }
  245. /**
  246. * 停止位
  247. *
  248. * @param stopBits 默认11:1位停止位;2:2位停止位
  249. * @return
  250. */
  251. public Builder stopBits(int stopBits) {
  252. this.stopBits = stopBits;
  253. return this;
  254. }
  255. /**
  256. * 标志
  257. *
  258. * @param flags 默认0
  259. * @return
  260. */
  261. public Builder flags(int flags) {
  262. this.flags = flags;
  263. return this;
  264. }
  265. /**
  266. * 打开并返回串口
  267. *
  268. * @return
  269. * @throws SecurityException
  270. * @throws IOException
  271. */
  272. public SerialPort build() throws SecurityException, IOException {
  273. return new SerialPort(device, baudrate, dataBits, parity, stopBits, flags);
  274. }
  275. }
  276. public static void checkFilePermission(File file) {
  277. Log.e(TAG, "canRead: " + file.canRead());
  278. Log.e(TAG, "canWrite: " + file.canWrite());
  279. if (!file.canRead() || !file.canWrite()) {
  280. try {
  281. /* Missing read/write permission, trying to chmod the file */
  282. Process su;
  283. su = Runtime.getRuntime().exec(sSuPath);
  284. String cmd = "chmod 7777 " + file.getAbsolutePath() + "\n" + "exit\n";
  285. su.getOutputStream().write(cmd.getBytes());
  286. Log.e(TAG, "checkFilePermission: " + file.getAbsolutePath());
  287. if ((su.waitFor() != 0) || !file.canRead() || !file.canWrite()) {
  288. throw new SecurityException();
  289. }
  290. } catch (Exception e) {
  291. e.printStackTrace();
  292. Log.e(TAG, "checkFilePermission: Exception:" + e.getMessage());
  293. throw new SecurityException();
  294. }
  295. }
  296. }
  297. //隐藏系统导航栏
  298. public void hideBottomNavation() {
  299. chmod("mount -o remount -w /system");
  300. chmod("chmod 777 /system");
  301. chmod("echo qemu.hw.mainkeys=1 >> /system/build.prop");
  302. }
  303. public void chmod(String instruct) {
  304. try {
  305. Process process = null;
  306. DataOutputStream os = null;
  307. process = Runtime.getRuntime().exec("su");
  308. os = new DataOutputStream(process.getOutputStream());
  309. os.writeBytes(instruct);
  310. os.flush();
  311. os.close();
  312. } catch (Exception ex) {
  313. ex.printStackTrace();
  314. }
  315. }
  316. }

2.5 CMakeLists.txt,编译c或c++程序的规则文件

Cmake在Jni那篇讲过,这个地方在讲下

CMake是一个可以跨平台的编译工具,可以用简单的语句来描述所有平台的编译过程。他能够输出各种各样的 makefile 或者工程文件。和make与makefile类似,我们在使用CMake时同样也需要一个文件来提供规则,这个文件就是CMakeLists

使用CMake编写跨平台工程的流程如下:

(1)编写源文件

(2)编写CMakeLists.txt

(3)由CMake根据CMakeLists.txt来生成相应的makefile文件

(4)使用make并根据makefile调用gcc来生成相应的可执行文件。

  1. # Sets the minimum version of CMake required to build your native library.
  2. # This ensures that a certain set of CMake features is available to
  3. # your build.
  4. cmake_minimum_required(VERSION 3.4.1)
  5. # Specifies a library name, specifies whether the library is STATIC or
  6. # SHARED, and provides relative paths to the source code. You can
  7. # define multiple libraries by adding multiple add.library() commands,
  8. # and CMake builds them for you. When you build your app, Gradle
  9. # automatically packages shared libraries with your APK.
  10. add_library( # Specifies the name of the library.
  11. serial_port
  12. # Sets the library as a shared library.
  13. SHARED
  14. # Provides a relative path to your source file(s).
  15. src/main/cpp/SerialPort.c )
  16. find_library( # Sets the name of the path variable.
  17. log-lib
  18. # Specifies the name of the NDK library that
  19. # you want CMake to locate.
  20. log )
  21. # Specifies libraries CMake should link to your target library. You
  22. # can link multiple libraries, such as libraries you define in this
  23. # build script, prebuilt third-party libraries, or system libraries.
  24. target_link_libraries( # Specifies the target library.
  25. serial_port
  26. # Links the target library to the log library
  27. # included in the NDK.
  28. ${log-lib} )

2.5 调用串口-连接,并获取输入输出流

  1. Runnable serialConnectRunnable = new Runnable() {
  2. @Override
  3. public void run() {
  4. try {
  5. if (mSerialPort == null) {
  6. mSerialPort = new SerialPort(new File(path), baudrate);
  7. mOutputStream = mSerialPort.getOutputStream();
  8. mInputStream = mSerialPort.getInputStream();
  9. }
  10. } catch (SecurityException e) {
  11. ToastUtil.showToast(App.getInstance(), "You do not have read/write permission to the serial port.");
  12. } catch (IOException e) {
  13. ToastUtil.showToast(App.getInstance(), "The serial port can not be opened for an unknown reason.");
  14. } catch (InvalidParameterException e) {
  15. ToastUtil.showToast(App.getInstance(), "Please configure your serial port first.");
  16. }
  17. //Serial结束
  18. }
  19. };

 2.6 读取串口消息

  1. private class ReadThread extends Thread {
  2. @Override
  3. public void run() {
  4. super.run();
  5. while (!isInterrupted()) {
  6. int size;
  7. try {
  8. byte[] buffer = new byte[512];
  9. if (mInputStream == null) return;
  10. size = mInputStream.read(buffer);
  11. if (size > 0) {
  12. String mReception=new String(buffer, 0, size);
  13. String msg = mReception.toString().trim();
  14. Log.e(TAG, "接收短消息:" + msg);
  15. }
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. return;
  19. }
  20. }
  21. }
  22. }

2.7 发送串口指令

  1. private class WriteRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. try {
  5. String cmd="KZMT;";
  6. Log.e(TAG, "发送短消息:" + cmd);
  7. mOutputStream.write(cmd.getBytes());
  8. mOutputStream.flush();
  9. } catch (IOException e) {
  10. }
  11. }
  12. }

2.8 断开关闭串口

  1. /**
  2. * 关闭串口连接
  3. */
  4. public void closeSerialPortStream() {
  5. try {
  6. if (mOutputStream != null) {
  7. mOutputStream.close();
  8. mOutputStream = null;
  9. }
  10. if (mInputStream != null) {
  11. mInputStream.close();
  12. mInputStream = null;
  13. }
  14. if (mSerialPort != null) {
  15. mSerialPort.close();
  16. mSerialPort = null;
  17. }
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }

三 google官方串口工具类

3.1 除了上面自己编程C底层文件,也可以直接用google官方的串口工具SDK(android-serialport-api),Github串口Demo地址:https://github.com/licheedev/Android-SerialPort-API

3.2 依赖:

  1. allprojects {
  2. repositories {
  3. ...
  4. jcenter()
  5. mavenCentral() // since 2.1.3
  6. }
  7. }
  8. dependencies {
  9. implementation 'com.licheedev:android-serialport:2.1.3'
  10. }

3.3 使用

  1. // 默认8N1(8数据位、无校验位、1停止位)
  2. // Default 8N1 (8 data bits, no parity bit, 1 stop bit)
  3. SerialPort serialPort = new SerialPort(path, baudrate);
  4. // 可选配置数据位、校验位、停止位 - 7E2(7数据位、偶校验、2停止位)
  5. // or with builder (with optional configurations) - 7E2 (7 data bits, even parity, 2 stop bits)
  6. SerialPort serialPort = SerialPort
  7. .newBuilder(path, baudrate)
  8. // 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
  9. // Check bit; 0: no check bit (NONE, default); 1: odd check bit (ODD); 2: even check bit (EVEN)
  10. // .parity(2)
  11. // 数据位,默认8;可选值为5~8
  12. // Data bit, default 8; optional value is 5~8
  13. // .dataBits(7)
  14. // 停止位,默认11:1位停止位;2:2位停止位
  15. // Stop bit, default 1; 1:1 stop bit; 2: 2 stop bit
  16. // .stopBits(2)
  17. .build();
  18. // read/write to serial port - needs to be in different thread!
  19. InputStream in = serialPort.getInputStream();
  20. OutputStream out = serialPort.getOutputStream();
  21. // close
  22. serialPort.tryClose();

四 总结

串口通讯使用到进程、Linux指令、JNI等,但本质最终还是获得一个输入输出流去进行读写操作;

串口通讯对于Android开发者来说,仅需关注如何连接、操作(发送指令)、读取数据。

大部分的物联网通信本质上都是获取io流,通过io流进行数据的传输和读取,比如蓝牙,wifi等,只不过蓝牙,wifi是通过Socket协议维持一个长连接进行通信 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/825803
推荐阅读
相关标签
  

闽ICP备14008679号