android作为服务器的开发,安卓与pc的串口开发
公司要求,一个比较扯淡的架构
在安卓系统上完成一套mini的完整服务
包括规约解析的规约插件;包括调度,采集,存库等的采集系统;以及上层面向客户的各个应用
也就接触了android开发;
先说踩的坑把
在IDEA上做android开发;gradle的构建工具最好选下面版本,不然可能会报版本问题
classpath 'com.android.tools.build:gradle:7.0.0'
其他问题几乎没有,有应该也不是啥大问题把,没印象了
然后要在安卓上实现AB芯的通信,通过串口
应该是安卓模拟器天生的bug,发送数据超过8个字节就会挂掉,
环境构建的坑:
1.安卓模拟器没有root权限,需要命令行修改:
adb root
adb shell setenforce 0
adb shell
chmod 777 /dev/tty*
2.命令行启动模拟器,挂载串口 emulator @Nexus_9_API_30 -qemu -serial COM1
为了规避安卓模拟一发就挂,只能安卓往外发,在另一端写好接收后的代码,调试OK,搬回android项目,等上了真实终端调试
调试代码需要依赖rxtx
http://fizzed.com/oss/rxtx-for-java
还需要将dll文件放入:1将jar包放入jre/lib下的ext文件夹;2C:/System/System32
可能放一个地方也行,我两边都放了,项目能运行,也就没管
串口工具网上很多的我主要用了Configure Virtual Serial Port Driver开端口,serial_port_utility来开端口发数据
网上确实有解决安卓模拟器异常退出的方法,我倒腾了半天,没有成功,安装的程序又大又笨重,最后还是自己在PC写个接收程序,实现安卓端程序的分包问题等。
贴一下接收端的纯PC的java代码,代码主要也是从网上来得,但是源码的接收写的有问题,我这里优化了接收,以及我自己的分包策略
代码
package com.company; import gnu.io.SerialPort; import java.nio.charset.StandardCharsets; import java.util.Locale; import static com.company.SerialPortConstants.BEGIN_BYTES; import static com.company.SerialPortConstants.END_BYTE; public class client { static int i = 0; public static StringBuffer sb = new StringBuffer(); public static void main(String[] args) throws SerialPortParameterFailure, NoSuchPort, InterruptedException { /* ArrayList<String> findPort = SerialTool.findPort(); System.out.println("可用串口"); for(String f:findPort) { System.out.println(f); } System.out.println(); //设定发送字符串 System.out.println(Arrays.toString(bs));//打印字符串 System.out.println(findPort.get(0));//找到COM1串口*/ byte[] bs = "AA".getBytes(); SerialPort Port = SerialTool.openPort("COM2", 9600);//打开串口 System.out.println(Port.getName());//获取串口名 SerialTool.addListener(Port, new SerialTool.DataAvailableListener() { @SuppressWarnings("unused") @Override public void dataAvailable() { try { if (Port == null) { System.out.println("串口对象为空,监听失败!"); } else { // 读取串口数据 new Thread(() -> { while (true) { byte[] data = null; try { data = SerialTool.readFromPort(Port); /*System.out.println("打印"+data);*/ if (data != null && data.length > 0) { sb.append(new String(data)); spilt(sb); System.out.println(); System.out.println(); System.out.println("------------"); System.out.println(new String(data)); System.out.println(sb.toString()); System.out.println("------------"); System.out.println(); System.out.println(); } } catch (NoSuchPort e) { e.printStackTrace(); } finally { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } catch (Exception e) { System.out.println("错误"); } } }); String s = "##0897{\"businessObjList\":[{\"commObj\":{\"authId\":0,\"commAddr\":\"116000009488\",\"deviceId\":\"200000312\",\"eigenvalue\":3,\"extendObj\":{\"comInfo\":{\"baudRate\":\"2400\",\"byteSize\":\"0\",\"comPort\":31,\"parity\":\"0\",\"stopBits\":\"0\"},\"measurNo\":201,\"port\":0,\"pulseSerial\":-1,\"roadNo\":201,\"totalSerial\":0},\"protocolNumber\":401070011},\"dataObj\":{\"collBeginTime\":\"2023-02-07 15:15:00\",\"collCycle\":15,\"collEndTime\":\"2023-02-07 15:15:00\",\"dataItemList\":[{\"finshFlag\":0,\"itemId\":\"30303006\"}],\"timeType\":4},\"soureDeviceId\":\"200023123\",\"transCommand\":false}],\"cmdType\":2,\"commObj\":{\"authId\":0,\"commAddr\":\"201500010134\",\"deviceId\":\"200023123\",\"eigenvalue\":2,\"extendObj\":{\"comInfo\":{\"baudRate\":\"0\",\"byteSize\":\"0\",\"comPort\":111111,\"parity\":\"0\",\"stopBits\":\"0\"},\"measurNo\":0,\"port\":0,\"pulseSerial\":-1,\"roadNo\":0,\"totalSerial\":0},\"protocolNumber\":201130010},\"priority\":0,\"spathId\":111111,\"taskId\":280370423755964416,\"timeout\":0,\"udSign\":0}+\r\n"; //send(s,Port); //SerialTool.sendToPort(Port, s.getBytes()); /*byte[] readFromPort = SerialTool.readFromPort(Port); System.out.println(readFromPort);*/ /*SerialTool.closePort(Port);*/ } public static void spilt(StringBuffer sb) { int begin = sb.indexOf(new String(BEGIN_BYTES)); int end = sb.indexOf(new String(END_BYTE)); if (begin != -1 && begin > 0) { System.out.println("起始符不在0位置,去掉起始符之前的数据,当前数据:" + sb); sb.replace(0, begin, ""); } if (begin != -1 && end != -1) { i++; String s = sb.substring(begin + 6, end - 1); sb.replace(begin, end + 2, ""); System.out.println("拆分的第" + i + "条数据:" + s); // System.out.println("sb内容" + sb); } } private static void send(String s, SerialPort Port) throws NoSuchPort, InterruptedException { byte[] bytes = s.getBytes(StandardCharsets.UTF_8); for (int i = 0; i < bytes.length; i = i + 7) { if (i + 7 < bytes.length) { byte[] bytes1 = new byte[7]; for (int j = 0; j < 7; j++) { bytes1[j] = bytes[i + j]; } SerialTool.sendToPort(Port, bytes1); Thread.sleep(1000); } else { if (bytes.length % 7 != 0) { byte[] bytes1 = new byte[bytes.length % 7]; for (int j = 0; j < bytes.length % 7; j++) { bytes1[j] = bytes[i + j]; } SerialTool.sendToPort(Port, bytes1); } } } } public static byte[] hex2Bytes(String hex) { if (hex == null || hex.length() == 0) { return null; } char[] hexChars = hex.toCharArray(); byte[] bytes = new byte[hexChars.length / 2]; // 如果 hex 中的字符不是偶数个, 则忽略最后一个 for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16); } return bytes; } /** * byte[]转十六进制字符串 * * @param array byte[] * @return 十六进制字符串 */ public static String byteArrayToHexString(byte[] array) { if (array == null) { return ""; } StringBuffer buffer = new StringBuffer(); for (int i = 0; i < array.length; i++) { buffer.append(byteToHex(array[i])); } return buffer.toString(); } /** * byte转十六进制字符 * * @param b byte * @return 十六进制字符 */ public static String byteToHex(byte b) { String hex = Integer.toHexString(b & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } return hex.toUpperCase(Locale.getDefault()); } }
package com.company; public class NoSuchPort extends Exception { /** * */ private static final long serialVersionUID = 1L; @Override public String toString() { return "没有该端口对应的串口设备!"; } }
package com.company; public interface SerialPortConstants { /** * 帧起始字符 */ byte[] BEGIN_BYTES = "##".getBytes(); String BEGIN_STR = "##"; /** * 帧结束字符 */ byte[] END_BYTE = new byte[]{0x0d, 0x0a}; String UP_TOPIC_PRE = "iot/A-CHIP/700020010/up/"; }
package com.company; public class SerialPortParameterFailure extends Exception { /** * */ private static final long serialVersionUID = 1L; public SerialPortParameterFailure() {} @Override public String toString() { return "设置串口参数失败!打开串口操作未完成!"; } }
package com.company; import gnu.io.*; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.TooManyListenersException; public class SerialTool { /** * 查找所有可用端口 * * @return 可用端口名称列表 */ public static final ArrayList<String> findPort() { //获得当前所有可用串口 @SuppressWarnings("unchecked") Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers(); ArrayList<String> portNameList = new ArrayList<String>(); //将可用串口名添加到List并返回该List while (portList.hasMoreElements()) { String portName = portList.nextElement().getName(); portNameList.add(portName); } return portNameList; } /** * 打开串口 * * @param portName 端口名称 * @param baudrate 波特率 * @return 串口对象 * @throws SerialPortParameterFailure 设置串口参数失败 * @throws NotASerialPort 端口指向设备不是串口类型 * @throws NoSuchPort 没有该端口对应的串口设备 * @throws PortInUse 端口已被占用 */ public static final SerialPort openPort(String portName, int baudrate) throws SerialPortParameterFailure, NoSuchPort, NoSuchPort, NoSuchPort { try { //通过端口名识别端口 CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName); //打开端口,并给端口名字和一个timeout(打开操作的超时时间) CommPort commPort = portIdentifier.open(portName, 2000); //判断是不是串口 if (commPort instanceof SerialPort) { SerialPort serialPort = (SerialPort) commPort; try { //设置一下串口的波特率等参数 serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); /*serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);*/ } catch (UnsupportedCommOperationException e) { throw new SerialPortParameterFailure(); } System.out.println("Open " + portName + " successfully !"); return serialPort; } else { //不是串口 throw new NoSuchPort(); } } catch (NoSuchPortException e1) { throw new NoSuchPort(); } catch (PortInUseException e2) { throw new NoSuchPort(); } } /** * 关闭串口 * * @param serialport 待关闭的串口对象 */ public static void closePort(SerialPort serialPort) { if (serialPort != null) { serialPort.close(); serialPort = null; } } /** * 往串口发送数据 * * @param serialPort 串口对象 * @param order 待发送数据 * @throws SendDataToSerialPortFailure 向串口发送数据失败 * @throws SerialPortOutputStreamCloseFailure 关闭串口对象的输出流出错 */ public static void sendToPort(SerialPort serialPort, byte[] order) throws NoSuchPort, NoSuchPort { OutputStream out = null; try { //order="ad".getBytes(StandardCharsets.UTF_8); out = serialPort.getOutputStream(); out.write(order); out.flush(); System.out.println("写入成功" + new String(order)); } catch (IOException e) { throw new NoSuchPort(); } finally { try { if (out != null) { out.close(); out = null; } } catch (IOException e) { throw new NoSuchPort(); } } } /** * 从串口读取数据 * * @param serialPort 当前已建立连接的SerialPort对象 * @return 读取到的数据 * @throws ReadDataFromSerialPortFailure 从串口读取数据时出错 * @throws SerialPortInputStreamCloseFailure 关闭串口对象输入流出错 */ public static byte[] readFromPort(SerialPort serialPort) throws NoSuchPort, NoSuchPort { InputStream in = null; List<byte[]> list = new ArrayList<>(); int length = 0; try { in = serialPort.getInputStream(); int bufflenth = in.available(); //获取buffer里的数据长度 while (bufflenth != 0) { byte[] bytes = new byte[bufflenth]; //初始化byte数组为buffer中数据的长度 in.read(bytes); bufflenth = in.available(); list.add(bytes); length = length + bytes.length; System.out.println("读取成功"); } } catch (IOException e) { throw new NoSuchPort(); } finally { try { if (in != null) { in.close(); in = null; } } catch (IOException e) { throw new NoSuchPort(); } } if (length > 0) { byte[] bytesb = new byte[length]; int index = 1; for (byte[] item : list) { index = copyToReq(bytesb, item, index); } System.out.println("读取数据" + new String(bytesb)); return bytesb; } return null; } private static int copyToReq(byte[] req, byte[] msg, int index) { for (int i = 0; i < msg.length; i++) { req[i + index - 1] = msg[i]; } return index + msg.length; } /** * 添加监听器 * * @param port 串口对象 * @param listener 串口监听器 * @throws TooManyListeners 监听类对象过多 */ public static void addListener(SerialPort port, DataAvailableListener listener) throws NoSuchPort { try { //给串口添加监听器 port.addEventListener(new SerialPortListener(listener)); //设置当有数据到达时唤醒监听接收线程 port.notifyOnDataAvailable(true); //设置当通信中断时唤醒中断线程 port.notifyOnBreakInterrupt(true); } catch (TooManyListenersException e) { throw new NoSuchPort(); } } /** * 串口监听 */ public static class SerialPortListener implements SerialPortEventListener { private DataAvailableListener mDataAvailableListener; public SerialPortListener(DataAvailableListener mDataAvailableListener) { this.mDataAvailableListener = mDataAvailableListener; } @Override public void serialEvent(SerialPortEvent serialPortEvent) { switch (serialPortEvent.getEventType()) { case SerialPortEvent.DATA_AVAILABLE: // 1.串口存在有效数据 if (mDataAvailableListener != null) { mDataAvailableListener.dataAvailable(); } break; case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2.输出缓冲区已清空 break; case SerialPortEvent.CTS: // 3.清除待发送数据 break; case SerialPortEvent.DSR: // 4.待发送数据准备好了 break; case SerialPortEvent.RI: // 5.振铃指示 break; case SerialPortEvent.CD: // 6.载波检测 break; case SerialPortEvent.OE: // 7.溢位(溢出)错误 break; case SerialPortEvent.PE: // 8.奇偶校验错误 break; case SerialPortEvent.FE: // 9.帧错误 break; case SerialPortEvent.BI: // 10.通讯中断 System.out.println("与串口设备通讯中断"); break; default: break; } } } /** * 串口存在有效数据监听 */ public interface DataAvailableListener { /** * 串口存在有效数据 */ void dataAvailable(); } }