SpringBoot 串口通讯

本文介绍了如何在Windows环境下,使用SpringBoot 2.5.5集成RXTX库进行串口通讯。通过配置IDEA、JDK和Gradle,设置虚拟串口并使用UartAssist工具进行测试。关键代码包括依赖注入、串口读取器、事件监听器、数据解析器和数据处理类,实现串口数据的接收、解析和处理。应用配置了串口参数如波特率,并展示了数据异常处理和CRC校验流程。
摘要由CSDN通过智能技术生成

一、开发环境介绍

  • 系统环境: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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值