美文网首页
SpringBoot 串口通讯

SpringBoot 串口通讯

作者: 会弹琴的程序猿 | 来源:发表于2021-12-08 13:47 被阅读0次

    一、开发环境介绍

    • 系统环境:windows 10 专业版 64位
    • 开发工具:IDEA
    • JDK版本:jdk-8u201-windows-x64 (下载地址
    • SpringBoot版本:2.5.5
    • 构建工具:gradle

    一、所需依赖项

    • 需将(rxtxParallel.dll、rxtxSerial.dll) 下载地址 动态链接库文件放入:C:\Program Files\Java\jdk1.8.0_201\bin 中才能进行串口通讯

    二、虚拟串口以及串口测试工具

    • 使用 Virtual Serial Port Driver Pro 生成模拟串口;

      在这里插入图片描述
    • 使用 UartAssist 串口调试工具收发消息测试;下载地址

      在这里插入图片描述

    二、关键代码

    1. 项目依赖
    plugins {
        id 'org.springframework.boot' version '2.5.5'
        id 'io.spring.dependency-management' version '1.0.11.RELEASE'
        id 'java'
    }
    
    group = 'com.cn'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '1.8'
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'cn.qqhxj.common:spring-boot-starter-rxtx:1.3.1-RELEASE'
        implementation 'org.springframework.boot:spring-boot-starter:2.5.5'
    
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
    
    test {
        useJUnitPlatform()
    }
    
    
    1. 串口通讯配置
      1、相关Bean配置
    import cn.qqhxj.common.rxtx.reader.SerialReader;
    import gnu.io.SerialPortEventListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
    /**
     * @Description: 串口配置类
     * @Author: JC
     * @CreateDate: 2021/10/15 18:17
     * @Version: 1.0
     */
    @Component
    public class SerialBeanConfig {
        @Bean
        public SerialReader serialReader() {
            return new CustomSerialReader();
        }
    
    
        @Bean
        public SerialPortEventListener serialPortEventListener() {
            return new CustomSerialPortEventListener();
        }
    }
    
    

    2、串口数据读取器

    
    import cn.qqhxj.common.rxtx.SerialContext;
    import cn.qqhxj.common.rxtx.reader.SerialReader;
    import com.magic.serialportdemo.util.CRC16;
    
    import java.nio.ByteBuffer;
    import java.util.Arrays;
    
    /**
     * @Description: 串口数据读取器
     * @Author: Juncheng He
     * @CreateDate: 2021/10/18 13:47
     * @Version: 1.0
     */
    public class CustomSerialReader implements SerialReader {
    
        private final byte startChat = 0x48;
    
    
        private int dataLengthIndex = 6;
    
        private int allLength = 0;
    
    
        /**
         * 数据长度
         */
        private byte[] lengthArray = new byte[2];
        /**
         * CRC数据
         */
        private byte[] crcArray = new byte[2];
    
    
        /**
         * 缓存区
         */
        private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    
        private boolean notOver = false;
    
        public CustomSerialReader() {
        }
    
    
        @Override
        public byte[] readBytes() {
            try {
                byte read = ((byte) SerialContext.getSerialPort().getInputStream().read());
                if (read == -1 && byteBuffer.position() < 8) {
                    notOver = false;
                    byteBuffer = ByteBuffer.allocate(1024);
                    allLength = 0;
                    lengthArray = new byte[2];
                    crcArray = new byte[2];
                }
                // 读取一个字节 查找起始标记中是否存在
                if (startChat == read) {
                    //读取的一个字节中存在起始标记
                    byteBuffer.put(read);
                    allLength = 1;
                    notOver = true;
                } else {
                    if (notOver) {
    
                        //获取数据长度
                        if (allLength == dataLengthIndex) {
                            lengthArray[0] = read;
                        } else if (allLength == (dataLengthIndex + 1)) {
                            lengthArray[1] = read;
                        }
    
    
                        allLength += 1;
                        byteBuffer.put(read);
                        //Data长度
                        int l = hex2Int(byteToHex(lengthArray));
                        if (allLength >= 8 + l) {
    
                            //获取校验码
                            if (allLength == 8 + l + 1) {
                                crcArray[0] = read;
                            } else if (allLength == 8 + l + 2) {
                                crcArray[1] = read;
                            }
    
                            if (allLength == 8 + l + 2) {
                                notOver = false;
                                byte[] array = Arrays.copyOf(byteBuffer.array(), byteBuffer.position());
                                byteBuffer = ByteBuffer.allocate(1024);
                                allLength = 0;
                                lengthArray = new byte[2];
    
                                if (CRC16.CRC16_KERMIT(array, 0, 8 + l).equals(byteToHex(crcArray))) {
                                    crcArray = new byte[2];
                                    System.out.println("CRC Success:" + byteToHex(array));
                                    return array;
                                }
                                System.out.println("CRC FAIL:" + byteToHex(array));
                                crcArray = new byte[2];
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
        /***
         * 字节转10进制
         * @param b byte
         * @return 十进制
         */
        public static int byte2Int(byte b) {
            int r = (int) b;
            return r;
        }
    
        public static int toInt(byte[] bytes) {
            int number = 0;
            for (int i = 0; i < 4; i++) {
                number += bytes[i] << i * 8;
            }
            return number;
        }
    
        /**
         * byte数组转hex
         *
         * @param bytes
         * @return
         */
        public static String byteToHex(byte[] bytes) {
            String strHex = "";
            StringBuilder sb = new StringBuilder("");
            for (int n = 0; n < bytes.length; n++) {
                strHex = Integer.toHexString(bytes[n] & 0xFF);
                // 每个字节由两个字符表示,位数不够,高位补0
                sb.append((strHex.length() == 1) ? "0" + strHex : strHex);
            }
            return sb.toString().trim().toUpperCase();
        }
    
        /**
         * hex转int
         *
         * @param hex
         * @return
         */
        public static int hex2Int(String hex) {
            return Integer.parseInt(hex, 16);
        }
    
        /**
         * hex字符串转byte数组
         *
         * @param inHex 待转换的Hex字符串
         * @return 转换后的byte数组结果
         */
        public static byte[] hexToByteArray(String inHex) {
            int hexlen = inHex.length();
            byte[] result;
            if (hexlen % 2 == 1) {
                //奇数
                hexlen++;
                result = new byte[(hexlen / 2)];
                inHex = "0" + inHex;
            } else {
                //偶数
                result = new byte[(hexlen / 2)];
            }
            int j = 0;
            for (int i = 0; i < hexlen; i += 2) {
                result[j] = hexToByte(inHex.substring(i, i + 2));
                j++;
            }
            return result;
        }
    
        /**
         * Hex字符串转byte
         *
         * @param inHex 待转换的Hex字符串
         * @return 转换后的byte
         */
        public static byte hexToByte(String inHex) {
            return (byte) Integer.parseInt(inHex, 16);
        }
    }
    

    3、事件监听器配置

    
    import cn.qqhxj.common.rxtx.SerialContext;
    import cn.qqhxj.common.rxtx.parse.SerialDataParser;
    import cn.qqhxj.common.rxtx.processor.SerialByteDataProcessor;
    import cn.qqhxj.common.rxtx.processor.SerialDataProcessor;
    import gnu.io.SerialPortEvent;
    import gnu.io.SerialPortEventListener;
    import lombok.extern.slf4j.Slf4j;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.Set;
    
    /**
     * @Description: 串口数据监听器
     * @Author: Juncheng He
     * @CreateDate: 2021/10/19 11:50
     * @Version: 1.0
     */
    @Slf4j
    public class CustomSerialPortEventListener implements SerialPortEventListener {
    
        private Logger logger= LoggerFactory.getLogger(CustomSerialPortEventListener.class);
    
        @Override
        public void serialEvent(SerialPortEvent ev) {
            switch (ev.getEventType()) {
                // 通讯中断
                case SerialPortEvent.BI:
                    logger.warn("通讯中断");
                    break;
                // 溢位错误
                case SerialPortEvent.OE:
                    logger.warn("溢位错误");
                    break;
                // 帧错误
                case SerialPortEvent.FE:
                    logger.warn("帧错误");
                    break;
                // 奇偶校验错误
                case SerialPortEvent.PE:
                    logger.warn("奇偶校验错误");
                    break;
                // 载波检测
                case SerialPortEvent.CD:
                    logger.warn("载波检测");
                    break;
                // 清除发送
                case SerialPortEvent.CTS:
                    logger.warn("清除发送");
                    break;
                // 数据设备准备好
                case SerialPortEvent.DSR:
                    logger.warn("数据设备准备好");
                    break;
                // 响铃侦测
                case SerialPortEvent.RI:
                    logger.warn("响铃侦测");
                    break;
                // 输出缓冲区已清空
                case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                    logger.warn("输出缓冲区已清空");
                    break;
                // 有数据到达
                case SerialPortEvent.DATA_AVAILABLE:
                    // 调用读取数据的方法
                    Set<SerialDataParser> parserSet = SerialContext.getSerialDataParserSet();
                    byte[] bytes = SerialContext.readData();
                    if (bytes == null) {
                        return;
                    }
                    Object parse;
                    for (SerialDataParser serialDataParser : parserSet) {
                        parse = serialDataParser.parse(bytes);
                        if (parse != null) {
                            dataProcessors(parse);
                        }
                    }
                    if (bytes.length > 0) {
                        SerialByteDataProcessor processor = SerialContext.getSerialByteDataProcessor();
                        if (processor != null) {
                            processor.process(bytes);
                        }
                    }
                default:
                    break;
            }
        }
    
    
        private void dataProcessors(Object obj) {
            Set<SerialDataProcessor> dataProcessors = SerialContext.getSerialDataProcessorSet();
            for (SerialDataProcessor serialDataProcessor : dataProcessors) {
                Class cl = serialDataProcessor.getClass();
                Class c2 = cl.getSuperclass();
                while (!c2.equals(Object.class)) {
                    cl = cl.getSuperclass();
                    c2 = cl.getSuperclass();
                }
                Type[] types = cl.getGenericInterfaces();
                for (Type type : types) {
                    if (type instanceof ParameterizedType) {
                        ParameterizedType parameterizedType = (ParameterizedType) type;
                        Type rawType = parameterizedType.getRawType();
                        if (rawType instanceof Class) {
                            boolean equals = rawType.equals(SerialDataProcessor.class);
                            if (equals) {
                                String typeName = ((ParameterizedType) type).getActualTypeArguments()[0].getTypeName();
                                try {
                                    Class<?> forName = Class.forName(typeName);
                                    if (forName == obj.getClass()) {
                                        serialDataProcessor.process(obj);
                                    }
                                } catch (ClassNotFoundException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    

    4、数据解析器

    import cn.qqhxj.common.rxtx.parse.SerialDataParser;
    import com.magic.serialportdemo.entity.DataEntity;
    import org.springframework.stereotype.Component;
    
    /**
     * @Description: 串口数据解析器
     * @Author: Juncheng He
     * @CreateDate: 2021/10/15 18:19
     * @Version: 1.0
     */
    @Component
    public class CustomStringSerialDataParser implements SerialDataParser<String> {
    
        @Override
        public String parse(byte[] bytes) {
            return new String(bytes);
        }
    }
    

    5、数据处理类

    import cn.qqhxj.common.rxtx.processor.SerialDataProcessor;
    import com.magic.serialportdemo.entity.DataEntity;
    import com.magic.serialportdemo.util.Const;
    import com.magic.serialportdemo.util.CustomSerialUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    
    /**
     * @Description: 数据处理类
     * @Author: Juncheng He
     * @CreateDate: 2021/10/15 18:18
     * @Version: 1.0
     */
    @Component
    public class CustomDataProcessor implements SerialDataProcessor<String> {
    
    
        private Logger logger = LoggerFactory.getLogger(CustomDataProcessor.class);
    
    
     
    
        @Override
        public void process(Strings) {
     
            logger.info("回复消息:" + s);
        }
    }
    
    

    6、application.properties

    serialport.baud-rate=115200
    serialport.port-name=COM1
    serialport.stop-bits=1
    serialport.parity=2
    serialport.data-bits=8
    

    相关文章

      网友评论

          本文标题:SpringBoot 串口通讯

          本文链接:https://www.haomeiwen.com/subject/peltfrtx.html