SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据

记录一下使用SpringBoot+RXTXcomm实现Java串口通信,使用Java语言开发串口,对串口进行读写操作。

案例源码:SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据

RXTXcomm.jar这个包支持的系统较多,但是更新太慢,在win系统下使用没有问题,但是在centos的工控机系统里使用读取和发送有问题,至今没能解决,报错的日志也记录一下


serial port com start success
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f5e636f75da, pid=18871, tid=0x00007f5e635ee700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [librxtxSerial.so+0x75da]  Java_gnu_io_RXTXPort_nativeDrain+0xea
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /home/hs_err_pid18871.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
已放弃
[root@localhost home]# 
Java HotSpot(TM) Server VM warning: You have loaded library /home/jdk18/jre/lib/i386/librxtxSerial.so which might have disabled stack guard. The VM will try to fix the stack guard now.
It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.
java.lang.UnsatisfiedLinkError: /home/jdk18/jre/lib/i386/librxtxSerial.so: /home/jdk18/jre/lib/i386/librxtxSerial.so: 错误 ELF 类: ELFCLASS64 (Possible cause: architecture word width mismatch) thrown while loading gnu.io.RXTXCommDriver
15:33:11.580 spring-boot-logging [main] INFO  o.s.b.a.l.ConditionEvaluationReportLoggingListener - 

19:26:03.323 spring-boot-logging [main] INFO  c.z.d.serialport.SerialPortManager - open serial port success:/dev/ttyS1
serial port com start success
19:26:15.326 spring-boot-logging [Thread-5] INFO  c.z.data.serialport.SerialPortThread - stepCount--200
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007ffa604a7733, pid=17020, tid=0x00007ffa6019e700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [librxtxSerial.so+0x7733]  Java_gnu_io_RXTXPort_nativeDrain+0xc3
#
# Core dump written. Default location: /usr/local/core or core.17020
#
# An error report file with more information is saved as:
# /usr/local/hs_err_pid17020.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
已放弃(吐核)


因此如果要使用RXTXcomm.jar这个串口包,建议在win系统下使用更好一些,其他系统使用可能出现莫名其妙的问题,如果非要在linux(centos)系统使用,推荐使用jSerialComm.jar

<dependency>
    <groupId>com.fazecast</groupId>
    <artifactId>jSerialComm</artifactId>
    <version>2.9.2</version>
</dependency>

这里记录使用SpringBoot加RXTXcomm在win10系统下的使用
mfz-rxtx-2.2-20081207-win-x64的下载地址

http://fizzed.com/oss/rxtx-for-java

SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第1张图片
根据自己的系统选择对应的下载包
myw
SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第2张图片
从readme.txt得知编译的环境特别旧了,将来的使用定然是越来越少

win-x86, win-x64, ia64
-----------------------------------------------
Built using Microsoft Visual C++ 2008 - not MinGW. The
x86 and x64 versions are native and do not rely on
any other non-standard windows libraries.  Just drop
in the compiled .dlls that are specific to the version
of Java you run. If you installed the 64-bit version
of the JDK, then install the x64 build.

I've tested the x86 and x64 version with Windows 2008,
2003, and Vista SP1.


linux-i386 & linux-x86_64
-----------------------------------------------
Built using CentOS 5.2 and gcc 4.1.2.

Just drop in the compiled .dlls that are specific to
the version of Java you run. If you installed the 64-bit
version of the JDK, then install the x64 build.

I've tested the x86 and x64 versions with x86 and x64
versions of CentOS 5.0 and 5.2.

SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第3张图片
根据文档,先将rxtxSerial.dll和rxtxParallel.dll放在指定的目录内
我自己电脑的JAVA_HOME
SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第4张图片
那么文件存放位置
SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第5张图片
至于 RXTXcomm.jar包不放在文档里的位置,放在具体的项目内引用(根据个人喜好来,我直接按照文档的方式去放,发现不起作用)

pom.xml

<?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 http://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.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>boot.example.mfz.rxtx</groupId>
    <artifactId>boot-example-serial-port-mfz-rxtx-2.0.5</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>boot-example-serial-port-mfz-rxtx-2.0.5</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>gnu.io</groupId>
            <artifactId>RXTXcomm</artifactId>
            <version>2.2</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/libs/jar/RXTXcomm.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.example.BootRXTXApplication</mainClass>
                    <includeSystemScope>true</includeSystemScope><!--外部进行打包-->
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

这里有个点儿

<systemPath>${project.basedir}/libs/jar/RXTXcomm.jar</systemPath>

SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第6张图片
BootRXTXApplication.java

package com.example;


import com.example.serialport.SerialPortManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.List;


@SpringBootApplication
@EnableScheduling
@EnableAsync
public class BootRXTXApplication implements CommandLineRunner {

    private final Logger log =  LoggerFactory.getLogger(this.getClass());

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

    @Override
    public void run(String... args) throws Exception {
        try{
            List<String> portList = SerialPortManager.getSerialPortList();
            if(!portList.isEmpty()){
                log.info(portList.toString());
                SerialPortManager.connectSerialPort();
            }
        } catch (Exception e){
            log.error("获取串口信息失败");
        }

    }

    @PreDestroy
    public void destroy() {
        SerialPortManager.closeSerialPort();
        System.exit(0);
    }



}

测试往串口发数据
SerialPortSendController.java

package com.example.controller;

import com.example.serialport.ConvertHexStrAndStrUtils;
import com.example.serialport.SerialPortManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class SerialPortSendController {

    //  http://localhost:8781/sendTest?message=mywmyyhtw

    @GetMapping(value = "/sendTest")
    @ResponseBody
    public String sendStringTopic(@RequestParam(name="message",required = true) String message) throws Exception {
        SerialPortManager.sendSerialPortData(ConvertHexStrAndStrUtils.strToHexStr(message));
        return "success";
    }

}

字符串以及16进制以及字节之间的转换工具类ConvertHexStrAndStrUtils.java

package com.example.serialport;

import java.nio.charset.StandardCharsets;

public class ConvertHexStrAndStrUtils {

    private static final char[] HEXES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public static String bytesToHexStr(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        StringBuilder hex = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            hex.append(HEXES[(b >> 4) & 0x0F]);
            hex.append(HEXES[b & 0x0F]);
        }
        return hex.toString().toUpperCase();
    }

    public static byte[] hexStrToBytes(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;
    }

    public static String strToHexStr(String str) {
        StringBuilder sb = new StringBuilder();
        byte[] bs = str.getBytes();
        int bit;
        for (int i = 0; i < bs.length; i++) {
            bit = (bs[i] & 0x0f0) >> 4;
            sb.append(HEXES[bit]);
            bit = bs[i] & 0x0f;
            sb.append(HEXES[bit]);
        }
        return sb.toString().trim();
    }

    public static String hexStrToStr(String hexStr) {
        //能被16整除,肯定可以被2整除
        byte[] array = new byte[hexStr.length() / 2];
        try {
            for (int i = 0; i < array.length; i++) {
                array[i] = (byte) (0xff & Integer.parseInt(hexStr.substring(i * 2, i * 2 + 2), 16));
            }
            hexStr = new String(array, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        return hexStr;
    }

}

串口工具类 我这里默认写死COM1 波特率9600
SerialPortManager.java

package com.example.serialport;

import gnu.io.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import java.util.concurrent.TimeUnit;

public class SerialPortManager {

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

    public static String SERIAL_PORT_NUMBER = "COM1";

    public static final int SERIAL_BAUD_RATE = 9600;

    public static volatile long SERIAL_CALLBACK_TIME = System.currentTimeMillis()/1000;

    public static volatile boolean SERIAL_PORT_STATE = false;

    public static volatile SerialPort SERIAL_PORT_OBJECT = null;

    //  获得系统可用的端口名称列表
    @SuppressWarnings("unchecked")
    public static List<String> getSerialPortList() {
        List<String> systemPorts = new ArrayList<>();
        //获得系统可用的端口
        Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
        while (portList.hasMoreElements()) {
            String portName = portList.nextElement().getName();//获得端口的名字
            systemPorts.add(portName);
        }
        return systemPorts;
    }

    public static void connectSerialPort(){
        try {
            closeSerialPort();
            TimeUnit.MILLISECONDS.sleep(4000);
            if(openSerialPort()){
                System.out.println("serial port com start success");
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    //  打开串口  设置中断和监听事件
    public static boolean openSerialPort() {
        try {
            CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(SERIAL_PORT_NUMBER);
            CommPort commPort = portIdentifier.open(SERIAL_PORT_NUMBER, 3000);
            if(commPort == null){return false;}
            //判断是不是串口
            if (commPort instanceof SerialPort) {
                SerialPort serialPort = (SerialPort) commPort;
                //设置串口参数(波特率,数据位8,停止位1,校验位无)
                serialPort.setSerialPortParams(SERIAL_BAUD_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
                // 当有数据到达时唤醒数据接收线程
                serialPort.notifyOnDataAvailable(true);
                // 当串口连接中断时唤醒中断线程
                serialPort.notifyOnBreakInterrupt(true);
                serialPort.notifyOnCarrierDetect(true);
                serialPort.notifyOnDSR(true);
                // 添加串口监听事件
                serialPort.addEventListener(new SerialPortListener(new SerialPortCallback()));
                SerialPortManager.SERIAL_PORT_OBJECT = serialPort;
                SerialPortManager.SERIAL_PORT_STATE = true;
                log.info("open serial port success:" + SERIAL_PORT_NUMBER);
                return true;
            } else {
                //是其他类型的端口
                throw new NoSuchPortException();
            }
        } catch (NoSuchPortException e) {
            log.error("not find serial port:" + e.getMessage());
        } catch (PortInUseException e) {
            log.error("the serial port used:" + e.getMessage());
        } catch (UnsupportedCommOperationException e) {
            log.error("open others serial port:" + e.getMessage());
        } catch (TooManyListenersException e) {
            log.error("the more listener this serial port:"+ e.getMessage());
        }

        return false;
    }

    //  关闭串口
    public static void closeSerialPort() {
        SERIAL_PORT_STATE = false;
        if (SERIAL_PORT_OBJECT != null) {
            SERIAL_PORT_OBJECT.close();
            SERIAL_PORT_OBJECT = null;
            log.info("serial port close");
        }
    }

    //  向串口发送数据
    public static void sendSerialPortData(String data) {
        OutputStream outputStream = null;
        try {
            outputStream = SERIAL_PORT_OBJECT.getOutputStream();
            outputStream.write(ConvertHexStrAndStrUtils.hexStrToBytes(data));
            outputStream.flush();
            log.info("send data success:"+data);
        } catch (IOException e) {
            log.error("read data exception:"+e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                log.error("read data inputStream close error:"+ e.getMessage());
            }
        }
    }

    //  从串口读取数据
    public static byte[] readSerialPortData() {
        InputStream in = null;
        byte[] bytes = {};
        try {
            TimeUnit.MILLISECONDS.sleep(200);
            in = SERIAL_PORT_OBJECT.getInputStream();
            byte[] readBuffer = new byte[1];
            int bytesNum = in.read(readBuffer);
            while (bytesNum > 0) {
                bytes = concat(bytes, readBuffer);
                bytesNum = in.read(readBuffer);
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }


    public static byte[] concat(byte[] firstArray, byte[] secondArray) {
        if (firstArray == null || secondArray == null) {
            return null;
        }
        byte[] bytes = new byte[firstArray.length + secondArray.length];
        System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
        System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
        return bytes;
    }

}

SerialPortListener.java

package com.example.serialport;

import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SerialPortListener implements SerialPortEventListener {

    private final Logger log =  LoggerFactory.getLogger(this.getClass());

    private final SerialPortCallback serialPortCallback;

    public SerialPortListener(SerialPortCallback serialPortCallback) {
        this.serialPortCallback = serialPortCallback;
    }

    public void serialEvent(SerialPortEvent serialPortEvent) {
        log.warn("SerialPortTestListener:"+serialPortEvent.getEventType());
        SerialPortManager.SERIAL_CALLBACK_TIME = System.currentTimeMillis()/1000;
        if (serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            if (serialPortCallback != null) {
                serialPortCallback.dataAvailable();
            }
        }

    }




}

从串口接收数据的SerialPortCallback.java

package com.example.serialport;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SerialPortCallback {

    private final Logger log =  LoggerFactory.getLogger(this.getClass());

    public void dataAvailable() {
        try {
            //throw new Exception();
            byte[] data = SerialPortManager.readSerialPortData();
            String s = ConvertHexStrAndStrUtils.bytesToHexStr(data);
            log.info("rev--data:"+s);
        } catch (Exception e) {
            log.error(e.toString());
        }
    }



}

定时器SerialPortTimer.java

package com.example.serialport;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;


@Service
public class SerialPortTimer {


    @Async
    @Scheduled(cron = "0 0/5 * * * ?")
    public void timeSerialPortScheduled() throws IOException, InterruptedException {
        long now = System.currentTimeMillis();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");
        System.out.println("timeSerialPortScheduled--"+format.format(now));
        long interval = 2 * 60 * 60;
        long rebootInterval = 24 * 60 * 60;
        long difference =  now/1000 - SerialPortManager.SERIAL_CALLBACK_TIME;

        // 当2个小时内收不到串口数据重启串口
        if(difference > interval){
            SerialPortManager.connectSerialPort();
        }

        //  当24小时内都还是收不到串口数据重启系统
        if(difference > rebootInterval){
            try {
                String osName = System.getProperty("os.name");
                if(osName.startsWith("Windows")) {
                    Runtime.getRuntime().exec("shutdown -r -t 0 -f");
                } else if(osName.startsWith("Linux")){
                    Runtime.getRuntime().exec("reboot");
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }

}

代码结构

│  pom.xml
│  
├─doc
│  │  mfz-rxtx-2.2-20081207-win-x64.zip
│  │  mfz-rxtx-2.2-20081207-win-x86.zip
│  │  
│  └─mfz-rxtx-2.2-20081207-win-x64
│          BuildProperties.txt
│          Install.txt
│          Readme.txt
│          ReleaseNotes.txt
│          RXTXcomm.jar
│          rxtxParallel.dll
│          rxtxSerial.dll
│          
├─libs
│  └─jar
│          jna.jar
│          RXTXcomm.jar
│          
├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─example
│  │  │          │  BootRXTXApplication.java
│  │  │          │  
│  │  │          ├─controller
│  │  │          │      SerialPortSendController.java
│  │  │          │      
│  │  │          └─serialport
│  │  │                  ConvertHexStrAndStrUtils.java
│  │  │                  SerialPortCallback.java
│  │  │                  SerialPortListener.java
│  │  │                  SerialPortManager.java
│  │  │                  SerialPortTimer.java
│  │  │                  
│  │  └─resources
│  │          application.properties
│  │          logback-spring.xml
│  │          
│  └─test
│      └─java
│          └─com
│              └─example
│                      BootRXTXApplicationTest.java
│            

接收数据测试 通过com2 向 com1发送数据 那么就算SpringBoot串口接收数据
SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第7张图片
可以看到控制台有打印数据
SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第8张图片
发送数据测试

http://localhost:8781/sendTest?message=mywmyyhtw

可以看到控制台
myw
对应的接收端
SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据_第9张图片

你可能感兴趣的:(SpringBoot+Demo,SpringBoot读取串口,SpringBoot串口通信,Java,串口通讯,Java串口通信,java实现串口通信)