android串口開發入門 Android 串口開發之 串口讀寫操作

原文鏈接:https://blog.csdn.net/qq_32136827/article/details/81129640

Android 串口開發之 串口讀寫操作

開發串口程序首先要求你的設備需要支持串口通信,可以在設備上裝一個App端的串口工具來檢測一下

鏈接:https://pan.baidu.com/s/11L4aZI9orBhbnztka6H1Og 
提取碼:bvot 

或者在電腦端下載一個友善串口助手檢測一下,一般在Android工控主板上面都會帶有串口。

首先我們是用到了谷歌開源的API serialPort 

先貼出來下載地址 https://github.com/cepr/android-serialport-api

第一步  配置環境

1、開發工具Android studio,2.2-3.1.2都可以

2、配置NDK(http://blog.csdn.net/yehui928186846/article/details/52787773),網上教程很多,這裏不做重點講解,查看配置是否成功

3、Android studio配置ndk

二、用開源庫代碼複製到自己項目裏

1、如下圖所示

裏面的操作類我做了重構,可能會跟開源裏面的不一樣,不過都是爲了達到自己的需求嘛

2、配置build-gridle

配置信息直接粘上去就可以了

在project目錄下的gradle.properties文件內加上

Android.useDeprecatedNdk=true這句話 ,爲了兼容新老版本ndk

3、類的講解

  1. public class SerialPortFinder {
  2. public class Driver {
  3. public Driver(String name, String root) {
  4. mDriverName = name;
  5. mDeviceRoot = root;
  6. }
  7. private String mDriverName;
  8. private String mDeviceRoot;
  9. Vector<File> mDevices = null;
  10. public Vector<File> getDevices() {
  11. if (mDevices == null) {
  12. mDevices = new Vector<File>();
  13. File dev = new File("/dev");
  14. File[] files = dev.listFiles();
  15. int i;
  16. for (i = 0; i < files.length; i++) {
  17. if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
  18. Log.d(TAG, "Found new device: " + files[i]);
  19. mDevices.add(files[i]);
  20. }
  21. }
  22. }
  23. return mDevices;
  24. }
  25. public String getName() {
  26. return mDriverName;
  27. }
  28. }
  29. private static final String TAG = "SerialPort";
  30. private Vector<Driver> mDrivers = null;
  31. Vector<Driver> getDrivers() throws IOException {
  32. if (mDrivers == null) {
  33. mDrivers = new Vector<Driver>();
  34. LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers"));
  35. String l;
  36. while ((l = r.readLine()) != null) {
  37. // Issue 3:
  38. // Since driver name may contain spaces, we do not extract driver name with split()
  39. String drivername = l.substring(0, 0x15).trim();
  40. String[] w = l.split(" +");
  41. if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) {
  42. Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]);
  43. mDrivers.add(new Driver(drivername, w[w.length - 4]));
  44. }
  45. }
  46. r.close();
  47. }
  48. return mDrivers;
  49. }
  50. public String[] getAllDevices() {
  51. Vector<String> devices = new Vector<String>();
  52. // Parse each driver
  53. Iterator<Driver> itdriv;
  54. try {
  55. itdriv = getDrivers().iterator();
  56. while (itdriv.hasNext()) {
  57. Driver driver = itdriv.next();
  58. Iterator<File> itdev = driver.getDevices().iterator();
  59. while (itdev.hasNext()) {
  60. String device = itdev.next().getName();
  61. String value = String.format("%s (%s)", device, driver.getName());
  62. devices.add(value);
  63. }
  64. }
  65. } catch (IOException e) {
  66. e.printStackTrace();
  67. }
  68. return devices.toArray(new String[devices.size()]);
  69. }
  70. //獲取設備上所有的串口節點
  71. public String[] getAllDevicesPath() {
  72. Vector<String> devices = new Vector<String>();
  73. // Parse each driver
  74. Iterator<Driver> itdriv;
  75. try {
  76. itdriv = getDrivers().iterator();
  77. while (itdriv.hasNext()) {
  78. Driver driver = itdriv.next();
  79. Iterator<File> itdev = driver.getDevices().iterator();
  80. while (itdev.hasNext()) {
  81. String device = itdev.next().getAbsolutePath();
  82. devices.add(device);
  83. }
  84. }
  85. } catch (IOException e) {
  86. e.printStackTrace();
  87. }
  88. return devices.toArray(new String[devices.size()]);
  89. }
  90. }

這個類一般不用,不佔主要作用,主要用於可以獲取設備上的所有可用的串口節點,用來選擇設置,根據需求添加

 

  1. public class SerialPort {
  2. private static final String TAG = "SerialPort";
  3. private FileDescriptor mFd;
  4. private FileInputStream mFileInputStream;
  5. private FileOutputStream mFileOutputStream;
  6. public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
  7. //檢查訪問權限,如果沒有讀寫權限,進行文件操作,修改文件訪問權限
  8. if (!device.canRead() || !device.canWrite()) {
  9. try {
  10. //通過掛載到linux的方式,修改文件的操作權限
  11. Process su = Runtime.getRuntime().exec("/system/bin/su");
  12. String cmd = "chmod 777 " + device.getAbsolutePath() + "\n" + "exit\n";
  13. su.getOutputStream().write(cmd.getBytes());
  14. if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) {
  15. throw new SecurityException();
  16. }
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. throw new SecurityException();
  20. }
  21. }
  22. mFd = open(device.getAbsolutePath(), baudrate, flags);
  23. if (mFd == null) {
  24. Log.e(TAG, "native open returns null");
  25. throw new IOException();
  26. }
  27. mFileInputStream = new FileInputStream(mFd);
  28. mFileOutputStream = new FileOutputStream(mFd);
  29. }
  30. // Getters and setters
  31. public InputStream getInputStream() {
  32. return mFileInputStream;
  33. }
  34. public OutputStream getOutputStream() {
  35. return mFileOutputStream;
  36. }
  37. // JNI(調用java本地接口,實現串口的打開和關閉)
  38. /**
  39. * 串口有五個重要的參數:串口設備名,波特率,檢驗位,數據位,停止位
  40. * 其中檢驗位一般默認位NONE,數據位一般默認爲8,停止位默認爲1
  41. */
  42. /**
  43. * @param path 串口設備的絕對路徑
  44. * @param baudrate 波特率
  45. * @param flags 校驗位
  46. */
  47. private native static FileDescriptor open(String path, int baudrate, int flags);
  48. public native void close();
  49. static {//加載jni下的C文件庫
  50. System.loadLibrary("serial_port");
  51. }
  52. }

這個SerialPort類是開源的,沒有經過修改,Android可以,裏面的直接調用,native方法直接和C通信,我們做Android的不需要管

jni目錄下放着c源碼和h頭文件,

jniLibs下面放的就是so庫。

注意:因爲用的谷歌原生so庫,所以SerialPort類的包名一定要是android_serialport_api,如果想修改這個包名,就需要重新生成對應的so庫

 

  1. public class SerialPortUtil {
  2. public static String TAG = "SerialPortUtil";
  3. /**
  4. * 標記當前串口狀態(true:打開,false:關閉)
  5. **/
  6. public static boolean isFlagSerial = false;
  7. public static SerialPort serialPort = null;
  8. public static InputStream inputStream = null;
  9. public static OutputStream outputStream = null;
  10. public static Thread receiveThread = null;
  11. public static String strData = "";
  12. public static Handler mHandler;
  13. /**
  14. * 打開串口
  15. */
  16. public static boolean open() {
  17. boolean isopen = false;
  18. if(isFlagSerial){
  19. LogUtils.e(TAG,"串口已經打開,打開失敗");
  20. return false;
  21. }
  22. try {
  23. serialPort = new SerialPort(new File("/dev/ttyS3"), 115200, 0);
  24. inputStream = serialPort.getInputStream();
  25. outputStream = serialPort.getOutputStream();
  26. receive();
  27. isopen = true;
  28. isFlagSerial = true;
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. isopen = false;
  32. }
  33. return isopen;
  34. }
  35. /**
  36. * 關閉串口
  37. */
  38. public static boolean close() {
  39. if(isFlagSerial){
  40. LogUtils.e(TAG,"串口關閉失敗");
  41. return false;
  42. }
  43. boolean isClose = false;
  44. LogUtils.e(TAG, "關閉串口");
  45. try {
  46. if (inputStream != null) {
  47. inputStream.close();
  48. }
  49. if (outputStream != null) {
  50. outputStream.close();
  51. }
  52. isClose = true;
  53. isFlagSerial = false;//關閉串口時,連接狀態標記爲false
  54. } catch (IOException e) {
  55. e.printStackTrace();
  56. isClose = false;
  57. }
  58. return isClose;
  59. }
  60. /**
  61. * 發送串口指令
  62. */
  63. public static void sendString(String data, Handler handler) {
  64. mHandler = handler;
  65. if (!isFlagSerial) {
  66. LogUtils.e(TAG, "串口未打開,發送失敗" + data);
  67. return;
  68. }
  69. try {
  70. outputStream.write(ByteUtil.hex2byte(data));
  71. outputStream.flush();
  72. LogUtils.e(TAG, "sendSerialData:" + data);
  73. } catch (IOException e) {
  74. e.printStackTrace();
  75. LogUtils.e(TAG, "發送指令出現異常");
  76. }
  77. }
  78. /**
  79. * 接收串口數據的方法
  80. */
  81. public static void receive() {
  82. if (receiveThread != null && !isFlagSerial) {
  83. return;
  84. }
  85. receiveThread = new Thread() {
  86. @Override
  87. public void run() {
  88. while (isFlagSerial) {
  89. try {
  90. byte[] readData = new byte[32];
  91. if (inputStream == null) {
  92. return;
  93. }
  94. int size = inputStream.read(readData);
  95. if (size > 0 && isFlagSerial) {
  96. strData = ByteUtil.byteToStr(readData, size);
  97. LogUtils.e(TAG, "readSerialData:" + strData);
  98. }
  99. } catch (IOException e) {
  100. e.printStackTrace();
  101. }
  102. }
  103. }
  104. };
  105. receiveThread.start();
  106. }
  107. }

這個類就比較重要了,打開串口、關閉串口、讀寫操作,都在這個類裏面寫了詳細的註釋,另外下面在貼一個工具類出來

  1. package com.sqy.scancode.util;
  2. import android.graphics.Bitmap;
  3. import android.graphics.BitmapFactory;
  4. import android.util.Base64;
  5. import java.io.FileInputStream;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import Decoder.BASE64Decoder;
  9. import Decoder.BASE64Encoder;
  10. /**
  11. * Created by Administrator on 2018/6/15.
  12. */
  13. public class ByteUtil {
  14. /**
  15. * 字符串轉化成爲16進制字符串
  16. *
  17. * @param s
  18. * @return
  19. */
  20. public static String strTo16(String s) {
  21. String str = "";
  22. for (int i = 0; i < s.length(); i++) {
  23. int ch = (int) s.charAt(i);
  24. String s4 = Integer.toHexString(ch);
  25. str = str + s4;
  26. }
  27. return str;
  28. }
  29. /**
  30. * 16進制轉換成爲string類型字符串
  31. *
  32. * @param s
  33. * @return
  34. */
  35. public static String hexStringToString(String s) {
  36. if (s == null || s.equals("")) {
  37. return null;
  38. }
  39. s = s.replace(" ", "");
  40. byte[] baKeyword = new byte[s.length() / 2];
  41. for (int i = 0; i < baKeyword.length; i++) {
  42. try {
  43. baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
  44. } catch (Exception e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. try {
  49. s = new String(baKeyword, "UTF-8");
  50. new String();
  51. } catch (Exception e1) {
  52. e1.printStackTrace();
  53. }
  54. return s;
  55. }
  56. /**
  57. * 向串口發送數據轉爲字節數組
  58. */
  59. public static byte[] hex2byte(String hex) {
  60. String digital = "0123456789ABCDEF";
  61. String hex1 = hex.replace(" ", "");
  62. char[] hex2char = hex1.toCharArray();
  63. byte[] bytes = new byte[hex1.length() / 2];
  64. byte temp;
  65. for (int p = 0; p < bytes.length; p++) {
  66. temp = (byte) (digital.indexOf(hex2char[2 * p]) * 16);
  67. temp += digital.indexOf(hex2char[2 * p + 1]);
  68. bytes[p] = (byte) (temp & 0xff);
  69. }
  70. return bytes;
  71. }
  72. /**
  73. * 接收到的字節數組轉換16進制字符串
  74. */
  75. public static String bytes2HexString(byte[] b, int size) {
  76. String ret = "";
  77. for (int i = 0; i < size; i++) {
  78. String hex = Integer.toHexString(b[i] & 0xFF);
  79. if (hex.length() == 1) {
  80. hex = '0' + hex;
  81. }
  82. ret += hex.toUpperCase();
  83. }
  84. return ret;
  85. }
  86. public static String bytesToHexString(byte[] src) {
  87. StringBuilder stringBuilder = new StringBuilder("");
  88. if (src == null || src.length <= 0) {
  89. return null;
  90. }
  91. for (int i = 0; i < src.length; i++) {
  92. int v = src[i] & 0xFF;
  93. String hv = Integer.toHexString(v);
  94. if (hv.length() < 2) {
  95. stringBuilder.append(0);
  96. }
  97. stringBuilder.append(hv);
  98. }
  99. return stringBuilder.toString();
  100. }
  101. /**
  102. * 接收到的字節數組轉換16進制字符串
  103. */
  104. public static String byteToStr(byte[] b, int size) {
  105. String ret = "";
  106. for (int i = 0; i < size; i++) {
  107. String hex = Integer.toHexString(b[i] & 0xFF);
  108. if (hex.length() == 1) {
  109. hex = '0' + hex;
  110. }
  111. ret += hex.toUpperCase();
  112. }
  113. return ret;
  114. }
  115. /**
  116. * BASE64碼解密成圖片
  117. */
  118. public static Bitmap Base64ToImage(String imgStr) { // 對字節數組字符串進行Base64解碼並生成圖片
  119. BASE64Decoder decoder = new BASE64Decoder();
  120. Bitmap bitmap = null;
  121. try {
  122. // Base64解碼
  123. byte[] b = decoder.decodeBuffer(imgStr);
  124. for (int i = 0; i < b.length; ++i) {
  125. if (b[i] < 0) {// 調整異常數據
  126. b[i] += 256;
  127. }
  128. }
  129. bitmap = BitmapFactory.decodeByteArray(b,0,b.length);
  130. return bitmap;
  131. } catch (Exception e) {
  132. LogUtils.e("TAG","解析異常");
  133. return bitmap;
  134. }
  135. }
  136. /**
  137. * 將圖片轉換爲base64加密數據
  138. */
  139. public static String ImageToBase64(String imgFile) {
  140. InputStream in = null;
  141. byte[] data = null;
  142. try {
  143. in = new FileInputStream(imgFile);
  144. data = new byte[in.available()];
  145. in.read(data);
  146. in.close();
  147. } catch (IOException e) {
  148. LogUtils.e("TAG","加密異常");
  149. e.printStackTrace();
  150. }
  151. BASE64Encoder encoder = new BASE64Encoder();
  152. return encoder.encode(data);
  153. }
  154. /**
  155. * 計算CRC16校驗碼
  156. * 逐個求和
  157. *
  158. * @param bytes 字節數組
  159. * @return {@link String} 校驗碼
  160. * @since 1.0
  161. */
  162. public static String getCRC_16(byte[] bytes) {
  163. int CRC = 0x0000ffff;
  164. int POLYNOMIAL = 0x0000a001;
  165. int i, j;
  166. for (i = 0; i < bytes.length; i++) {
  167. CRC ^= ((int) bytes[i] & 0x000000ff);
  168. for (j = 0; j < 8; j++) {
  169. if ((CRC & 0x00000001) != 0) {
  170. CRC >>= 1;
  171. CRC ^= POLYNOMIAL;
  172. } else {
  173. CRC >>= 1;
  174. }
  175. }
  176. }
  177. if (Integer.toHexString(CRC).toUpperCase().length() == 2) {
  178. return byteToStr(bytes, bytes.length) + "00" + Integer.toHexString(CRC).toUpperCase();
  179. } else if (Integer.toHexString(CRC).toUpperCase().length() == 3) {
  180. return byteToStr(bytes, bytes.length) + "0" + Integer.toHexString(CRC).toUpperCase();
  181. }
  182. return byteToStr(bytes, bytes.length) + Integer.toHexString(CRC).toUpperCase();
  183. }
  184. /**
  185. * 指令校驗和,並取出後兩位字節
  186. * */
  187. public static String getSum16(byte[] msg, int length) {
  188. long mSum = 0;
  189. byte[] mByte = new byte[length];
  190. /** 逐Byte添加位數和 */
  191. for (byte byteMsg : msg) {
  192. long mNum = ((long) byteMsg >= 0) ? (long) byteMsg : ((long) byteMsg + 256);
  193. mSum += mNum;
  194. } /** end of for (byte byteMsg : msg) */
  195. /** 位數和轉化爲Byte數組 */
  196. for (int liv_Count = 0; liv_Count < length; liv_Count++) {
  197. mByte[length - liv_Count - 1] = (byte) (mSum >> (liv_Count * 8) & 0xff);
  198. } /** end of for (int liv_Count = 0; liv_Count < length; liv_Count++) */
  199. return byteToStr(msg, length) + byteToStr(mByte, mByte.length).substring(byteToStr(mByte, mByte.length).length() - 4, byteToStr(mByte, mByte.length).length());
  200. }
  201. }

4、demo下載地址 : https://github.com/z-jc/ScanCode   裏面可能還會有一些別的功能,需要的話自行下載

5、另外再提供一個自己封裝好的module,https://github.com/z-jc/SerialProject-master,app導入module

然後在activity內直接這樣調用

省心又省勁是不是,用到串口的項目可以直接當一個libray導入項目,不過需要檢驗的話還是得根據自己的校驗方法來進行校驗

以上全爲原創,如有講解不到之處,還請廣大朋友指點一下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章