JAVA设备对接与串口通信详解

 更新时间:2025年07月15日 16:42:07   作者:秋分的秋刀鱼  
作者为项目对接串口设备,系统学习串口通信原理与编程,通过代码示例实践实现通信功能,总结其在数据传输稳定性、系统设计灵活性等方面的重要价值

一、背景

近期,我的项目需要对接各种设备,其中有几台设备采用了串口通信方式。由于之前对串口通信方式接触不多,深感自己在这方面知识的欠缺,于是我决定对串口通信进行深入的研究和开发。

我仔细学习了串口通信的原理、协议以及相关的编程技术,并通过实践不断加深对串口通信的理解。

在这个过程中,我遇到了不少挑战,但通过不断的学习和实践,最终成功实现了与设备的串口通信。现在,我将自己的学习和开发经验记录下来,形成这篇博文,希望能够为有需要的同学提供一些帮助和指导。

二、串口通信技术介绍

串口是串行接口(serial port)的简称,也称为串行通信接口或COM接口。

串口通信是指采用串行通信协议(serial communication)在一条信号线上将数据一个比特一个比特地逐位进行传输的通信模式。

  • 串口通信是一种利用串行通信协议,在计算机与外部设备之间进行异步通信的技术。串行通信按照时间顺序,按位依次发送通信字节,相较于并行通信,它仅需较少的数据线,通常两根线即可实现双向通信。在串行通信中,并行数据被转换为串行数据后,通过传输线路依次传输。
  • 异步通信则是发送端和接收端通过起始位、停止位来同步数据块的方式。发送端在发送数据字节前,会先发送一个起始位,然后依次发送数据字节的每个位,最后发送一个或多个停止位。接收端通过检测起始位的状态转变来同步接收数据,一旦检测到起始位,便会根据事先约定的规则接收之后的数据位和停止位。

三、代码示例

3.1 创建串口监听器

// 串口监听器
@Slf4j
public class MyLister implements SerialPortEventListener {

    //private static final Logger log = LoggerFactory.getLogger(MyLister.class);

    @Override
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
            // 串口存在有效数据
            case SerialPortEvent.DATA_AVAILABLE:
                byte[] bytes = SerialPortUtil.getSerialPortUtil().readFromPort(PortInit.serialPort);
                log.info("===========start===========");
                String str = new String(bytes, StandardCharsets.UTF_8);
                log.info("{}【读到的字符】:-----{}", LocalDateTime.now(), str);
                log.info("{}【字节数组转16进制字符串】:-----{}", LocalDateTime.now(), stringToHex(str));
                log.info("===========end===========");
                break;
            // 2.输出缓冲区已清空
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                log.error("输出缓冲区已清空");
                break;
            // 3.清除待发送数据
            case SerialPortEvent.CTS:
                log.error("清除待发送数据");
                break;
            // 4.待发送数据准备好了
            case SerialPortEvent.DSR:
                log.error("待发送数据准备好了");
                break;
            // 10.通讯中断
            case SerialPortEvent.BI:
                log.error("与串口设备通讯中断");
                break;
            default:
                break;
        }
    }

    public static String stringToHex(String input) {
        byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
        StringBuilder hexString = new StringBuilder();

        for (byte b : bytes) {
            String hex = Integer.toHexString(0xFF & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    /**
     * ASCII码转字节数组
     */
    public static byte[] asciiToIntArray(List<Integer> asciiValues) {
        byte[] byteArray = new byte[asciiValues.size()];
        for (int i = 0; i < asciiValues.size(); i++) {
            int asciiValue = asciiValues.get(i);
            byteArray[i] = (byte) asciiValue;
        }
        return byteArray;
    }

    /**
     * 字符串转ASCII码
     */
    public static List<Integer> stringToAscii(String input) {
        List<Integer> asciiValues = new ArrayList<>();
        for (char c : input.toCharArray()) {
            asciiValues.add((int) c);
        }
        return asciiValues;
    }


    /**
     * 16进制字符串转字节数组
     *
     * @param hex 16进制字符串
     * @return 字节数组
     */
    public static byte[] hex2byte(String hex) {
        if (!isHexString(hex)) {
            return null;
        }
        char[] arr = hex.toCharArray();
        byte[] b = new byte[hex.length() / 2];
        for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
            String swap = "" + arr[i++] + arr[i];
            int byteint = Integer.parseInt(swap, 16) & 0xFF;
            b[j] = new Integer(byteint).byteValue();
        }
        return b;
    }

    /**
     * 校验是否是16进制字符串
     *
     * @param hex
     * @return
     */
    public static boolean isHexString(String hex) {
        if (hex == null || hex.length() % 2 != 0) {
            return false;
        }
        for (int i = 0; i < hex.length(); i++) {
            char c = hex.charAt(i);
            if (!isHexChar(c)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 校验是否是16进制字符
     *
     * @param c
     * @return
     */
    private static boolean isHexChar(char c) {
        return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
    }

}

3.2 串口初始化

/**
 * 串口初始化
 */
@Component
@Slf4j
public class PortInit implements ApplicationRunner {
    public static SerialPort serialPort = null;

    @Override
    public void run(ApplicationArguments args) {
        String portName = "COM1";
        //查看所有串口
        SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
        ArrayList<String> port = serialPortUtil.findPort();
        log.info("发现全部串口:{}", port);
        log.info("打开指定portName:{}", portName);
        //打开该对应portName名字的串口
        PortInit.serialPort = serialPortUtil.openPort(
                portName,
                9600,
                SerialPort.DATABITS_8,
                SerialPort.PARITY_NONE,
                SerialPort.PARITY_ODD);
        //给对应的serialPort添加监听器
        serialPortUtil.addListener(PortInit.serialPort, new MyLister());
    }

}

3.3 启动类

@SpringBootApplication
public class SerialPortProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(SerialPortProjectApplication.class, args);
    }

    @PreDestroy
    public void destroy() {
        //关闭应用前 关闭端口
        SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
        serialPortUtil.removeListener(PortInit.serialPort);
        serialPortUtil.closePort(PortInit.serialPort);
    }
}

3.4 pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xsd</groupId>
    <artifactId>SerialPortProject</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SerialPortProject</name>
    <description>SerialPortProject</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>com.baomidou</groupId>-->
<!--            <artifactId>mybatis-plus-boot-starter</artifactId>-->
<!--            <version>3.5.2</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>mysql</groupId>-->
<!--            <artifactId>mysql-connector-java</artifactId>-->
<!--            <scope>runtime</scope>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.springframework.security</groupId>-->
<!--            <artifactId>spring-security-test</artifactId>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.1-jre</version>
        </dependency>
        <!--    PropertyUtils工具类    -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.9</version>
        </dependency>
        <!--   jwt依赖    -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        <!-- 串口内容读取 -->
        <dependency>
            <groupId>org.bidib.jbidib.org.qbang.rxtx</groupId>
            <artifactId>rxtxcomm</artifactId>
            <version>2.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.5 发送的Job

@Component
@EnableScheduling
public class SendJob {

    /**
     * 定时发送数据
     * initialDelay :系统启动后,3秒后开始运行此方法
     * fixedDelay:表示此方法运行结束后,过1秒再次运行此方法
     */
    @Scheduled(initialDelay = 1000 * 3, fixedDelay = 1000)
    public void plcAnalytic() {
        String s = "4040000900手动滑稽阿萨德和静安寺060004000000100172323";
        SerialPortUtil.getSerialPortUtil().sendToPort(PortInit.serialPort, MyLister.asciiToIntArray(MyLister.stringToAscii(s)));
    }
}

3.6 模拟窗口发布的软件

四、总结

串口通信因其简单性、灵活性和通用性,在众多应用场景中仍然保持着极高的实用价值。

深入理解串口的工作原理,掌握串口通信接口的选择与编程技巧,对于更好地应用串口技术、设计出更为可靠的通信系统具有重要意义。

这不仅能够帮助我们充分发挥串口通信的优势,还能在不同应用场景中灵活应对各种通信需求,确保数据传输的稳定性和准确性。因此,深入学习和掌握串口通信的相关知识,是提升通信系统设计能力、实现高效数据传输的关键所在。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • spring中aop的xml配置方法实例详解

    spring中aop的xml配置方法实例详解

    AOP的配置方式有2种方式:xml配置和AspectJ注解方式。下面这篇文章主要给大家介绍了关于spring中aop的xml配置方法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-07-07
  • SpringBoot 如何使用Dataway配置数据查询接口

    SpringBoot 如何使用Dataway配置数据查询接口

    这篇文章主要介绍了SpringBoot 如何使用Dataway配置数据查询接口,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • java lambda 表达式中的双冒号的用法说明 ::

    java lambda 表达式中的双冒号的用法说明 ::

    这篇文章主要介绍了java lambda 表达式中的双冒号的用法说明 ::具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Spring-IOC容器中的常用注解与使用方法详解

    Spring-IOC容器中的常用注解与使用方法详解

    Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题,这篇文章给大家详细介绍Spring-IOC容器中的常用注解与使用方法,感兴趣的朋友跟随小编一起看看吧
    2021-04-04
  • Hibernate三种状态和Session常用的方法

    Hibernate三种状态和Session常用的方法

    本文主要介绍了Hibernate三种状态和Session常用的方法,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-03-03
  • Netty网络编程零基础入门

    Netty网络编程零基础入门

    Netty是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端,如果你还不了解它的使用,就赶快继续往下看吧
    2022-08-08
  • 浅谈java安全编码指南之堆污染

    浅谈java安全编码指南之堆污染

    什么是堆污染呢?是指当参数化类型变量引用的对象不是该参数化类型的对象时而发生的。我们知道在JDK5中,引入了泛型的概念,在创建集合类的时候,指定该集合类中应该存储的对象类型。如果在指定类型的集合中,引用了不同的类型,那么这种情况就叫做堆污染。
    2021-06-06
  • SpringSecurity微服务实战之公共模块详解

    SpringSecurity微服务实战之公共模块详解

    这篇文章主要为大家介绍了SpringSecurity微服务实战之公共模块详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 已解决:No ''Access-Control-Allow-Origin''跨域问题

    已解决:No ''Access-Control-Allow-Origin''跨域问题

    这篇文章主要介绍了已解决:No 'Access-Control-Allow-Origin' 跨域,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Jmeter调用java脚本过程详解

    Jmeter调用java脚本过程详解

    这篇文章主要介绍了Jmeter调用java脚本过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12

最新评论